/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.modelgenerator.wsdl;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.datatools.connectivity.IConnectionProfile;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.xsd.XSDSchema;
import org.eclipse.xsd.XSDSimpleTypeDefinition;
import org.eclipse.xsd.impl.XSDSchemaImpl;
import org.eclipse.xsd.util.XSDConstants;
import org.teiid.core.designer.ModelerCoreException;
import org.teiid.core.designer.util.I18nUtil;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.types.DatatypeConstants;
import org.teiid.designer.core.types.DatatypeManager;
import org.teiid.designer.core.workspace.ModelResource;
import org.teiid.designer.core.workspace.ModelUtil;
import org.teiid.designer.core.workspace.ModelWorkspaceException;
import org.teiid.designer.metamodels.core.ModelAnnotation;
import org.teiid.designer.metamodels.core.ModelType;
import org.teiid.designer.metamodels.relational.DirectionKind;
import org.teiid.designer.metamodels.relational.NullableType;
import org.teiid.designer.metamodels.relational.Procedure;
import org.teiid.designer.metamodels.relational.ProcedureParameter;
import org.teiid.designer.metamodels.relational.RelationalFactory;
import org.teiid.designer.metamodels.relational.RelationalPackage;
import org.teiid.designer.metamodels.relational.Schema;
import org.teiid.designer.modelgenerator.wsdl.model.Message;
import org.teiid.designer.modelgenerator.wsdl.model.Model;
import org.teiid.designer.modelgenerator.wsdl.model.Operation;
import org.teiid.designer.modelgenerator.wsdl.model.Part;
import org.teiid.designer.modelgenerator.wsdl.model.Port;
import org.teiid.designer.modelgenerator.wsdl.model.Service;
import org.teiid.designer.modelgenerator.wsdl.schema.extensions.SOAPSchemaProcessor;
import org.teiid.designer.modelgenerator.xsd.procedures.ITraversalCtxFactory;
import org.teiid.designer.modelgenerator.xsd.procedures.ProcedureBuilder;
import org.teiid.designer.modelgenerator.xsd.procedures.RequestTraversalContextFactory;
import org.teiid.designer.modelgenerator.xsd.procedures.ResultTraversalContextFactory;
import org.teiid.designer.schema.tools.model.schema.SchemaModel;
import org.teiid.designer.schema.tools.model.schema.SchemaObject;
import org.teiid.designer.schema.tools.processing.SchemaProcessingException;
import org.teiid.designer.schema.tools.processing.SchemaProcessor;
import org.teiid.designer.ui.viewsupport.ModelObjectUtilities;
import org.teiid.designer.ui.viewsupport.ModelUtilities;
/**
* The WebService connection connects to one Endpoint URL, but a single WSDL can
* contain many services that point to different endpoints. In order to minimize
* the number of WS connections that we have to make in Teiid, this class
* combines the all of the operations for an endpoint into one physical model,
* and creates multiple physical models only in the case of multiple Endpoints
* (as defined my Ports).
*
* The virtual models to create and parse the XML are defined as Procedures and
* are not linked to any particular WS operation. The elements defined in a WSDL
* Schema are often used across operations, and binding them to an operation
* would limit reuse or require duplication.
*
* @since 8.0
*/
public class RelationalModelBuilder {
private static final String I18N_PREFIX = I18nUtil.getPropertyPrefix(RelationalModelBuilder.class);
/**
* Get the localized string text for the provided id
*/
private static String getString( final String id ) {
return ModelGeneratorWsdlPlugin.Util.getString(I18N_PREFIX + id);
}
public static final String XML = "XML"; //$NON-NLS-1$
public static final String DOT_XMI = ".xmi"; //$NON-NLS-1$
private static final String INVOKE = "invoke"; //$NON-NLS-1$
private List<ModelResource> models;
private Map<String, ModelResource> serviceNameToPhysicalMap;
private Map<String, ModelResource> serviceNameToVirtualMap;
private Model wsdlModel;
private SchemaModel schemaModel;
private IContainer folder;
private RelationalFactory factory;
private DatatypeManager datatypeManager = ModelerCore.getBuiltInTypesManager();
private List<ProcedureBuilder> builders= new ArrayList<ProcedureBuilder>();
private WSSoapConnectionInfoProvider connProvider;
private IConnectionProfile connectionProfile;
private XSDSchema[] schemas;
boolean isSoapImportOption = false;
IContainer soapSourceModelFolder;
IContainer soapViewModelFolder;
String soapSourceModelName;
String soapViewModelName;
public RelationalModelBuilder(Model model, IConnectionProfile profile) throws SchemaProcessingException {
wsdlModel = model;
SchemaProcessor processor = new SOAPSchemaProcessor(null);
processor.representTypes(true);
processor.setNamespaces(wsdlModel.getNamespaces());
schemas = wsdlModel.getSchemas();
processor.processSchemas(schemas);
schemaModel = processor.getSchemaModel();
factory = org.teiid.designer.metamodels.relational.RelationalPackage.eINSTANCE
.getRelationalFactory();
connectionProfile = profile;
connProvider = new WSSoapConnectionInfoProvider();
}
public void modelOperations(List<Operation> operations, IContainer container)
throws ModelBuildingException, ModelerCoreException {
folder = container;
models = new ArrayList<ModelResource>();
serviceNameToPhysicalMap = new HashMap<String, ModelResource>();
serviceNameToVirtualMap = new HashMap<String, ModelResource>();
Map<String, Port> ports = new HashMap<String, Port>();
for (Operation operation : operations) {
Port port = operation.getBinding().getPort();
ports.put(port.getName(), port);
// all ops on a service/port are created in a single physical model
ModelResource serviceVirtualModel;
try {
getPhysicalModelForService(operation);
serviceVirtualModel = getVirtualModelForService(operation);
} catch (CoreException e) {
throw new ModelBuildingException(e);
}
Schema schema = factory.createSchema();
serviceVirtualModel.getEmfResource().getContents().add(schema);
schema.setName(operation.getName());
schema.setNameInSource(operation.getName());
ProcedureBuilder resultBuilder = new ProcedureBuilder(schema, serviceVirtualModel);
builders.add(resultBuilder);
modelInputMessage(operation, resultBuilder);
ProcedureBuilder requestBuilder = new ProcedureBuilder(schema, serviceVirtualModel);
builders.add(requestBuilder);
modelOutputMessage(operation, requestBuilder);
//operation.getFaults();
}
WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {
@Override
public void execute(final IProgressMonitor monitor) {
for (ModelResource resource : models) {
try {
ModelUtilities.saveModelResource(resource, monitor, false, this);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (ProcedureBuilder builder : builders) {
builder.createTransformations();
}
try {
ModelUtilities.saveModelResource(resource, monitor, false, this);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
IProgressMonitor monitor = new NullProgressMonitor();
try {
operation.run(monitor);
} catch (Exception ex) {
if (ex instanceof InvocationTargetException) {
throw new ModelBuildingException(
((InvocationTargetException) ex).getTargetException());
}
throw new ModelBuildingException(ex);
}
}
public void modelOperations(List<Operation> operations, IContainer sourceContainer, String sourceModelName, IContainer viewContainer, String viewModelName)
throws ModelBuildingException, ModelerCoreException {
isSoapImportOption = true;
soapSourceModelFolder = sourceContainer;
soapViewModelFolder = viewContainer;
soapSourceModelName = sourceModelName;
soapViewModelName = viewModelName;
models = new ArrayList<ModelResource>();
serviceNameToPhysicalMap = new HashMap<String, ModelResource>();
serviceNameToVirtualMap = new HashMap<String, ModelResource>();
Map<String, Port> ports = new HashMap<String, Port>();
for (Operation operation : operations) {
Port port = operation.getBinding().getPort();
ports.put(port.getName(), port);
// all ops on a service/port are created in a single physical model
ModelResource serviceVirtualModel;
try {
getPhysicalModelForService(operation);
serviceVirtualModel = getVirtualModelForService(operation);
} catch (CoreException e) {
throw new ModelBuildingException(e);
}
Schema schema = factory.createSchema();
serviceVirtualModel.getEmfResource().getContents().add(schema);
schema.setName(operation.getName());
schema.setNameInSource(operation.getName());
ProcedureBuilder resultBuilder = new ProcedureBuilder(schema, serviceVirtualModel);
builders.add(resultBuilder);
modelInputMessage(operation, resultBuilder);
ProcedureBuilder requestBuilder = new ProcedureBuilder(schema, serviceVirtualModel);
builders.add(requestBuilder);
modelOutputMessage(operation, requestBuilder);
//operation.getFaults();
}
WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {
@Override
public void execute(final IProgressMonitor monitor) {
for (ModelResource resource : models) {
try {
ModelUtilities.saveModelResource(resource, monitor, false, this);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (ProcedureBuilder builder : builders) {
builder.createTransformations();
}
try {
ModelUtilities.saveModelResource(resource, monitor, false, this);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
IProgressMonitor monitor = new NullProgressMonitor();
try {
operation.run(monitor);
} catch (Exception ex) {
if (ex instanceof InvocationTargetException) {
throw new ModelBuildingException(
((InvocationTargetException) ex).getTargetException());
}
throw new ModelBuildingException(ex);
}
}
private void createPhysicalProcedure(Service service,
ModelResource serviceModel) throws ModelerCoreException {
Procedure procedure = factory.createProcedure();
serviceModel.getEmfResource().getContents().add(procedure);
procedure.setName(INVOKE);
procedure.setNameInSource(INVOKE);
ProcedureParameter param = factory.createProcedureParameter();
procedure.getParameters().add(param);
param.setDirection(DirectionKind.IN_LITERAL);
param.setName(getString("param.binding")); //$NON-NLS-1$
param.setNameInSource(getString("param.binding")); //$NON-NLS-1$
param.setNullable(NullableType.NULLABLE_LITERAL);
param.setType(datatypeManager.getBuiltInDatatype(DatatypeConstants.BuiltInNames.STRING));
param = factory.createProcedureParameter();
procedure.getParameters().add(param);
param.setDirection(DirectionKind.IN_LITERAL);
param.setName(getString("param.action")); //$NON-NLS-1$
param.setNameInSource(getString("param.action")); //$NON-NLS-1$
param.setNullable(NullableType.NULLABLE_LITERAL);
param.setType(datatypeManager.getBuiltInDatatype(DatatypeConstants.BuiltInNames.STRING));
param = factory.createProcedureParameter();
procedure.getParameters().add(param);
param.setDirection(DirectionKind.IN_LITERAL);
param.setName(getString("param.request")); //$NON-NLS-1$
param.setNameInSource(getString("param.request")); //$NON-NLS-1$
param.setNullable(NullableType.NULLABLE_LITERAL);
param.setType(datatypeManager.getBuiltInDatatype(DatatypeConstants.BuiltInNames.XML_LITERAL));
param = factory.createProcedureParameter();
procedure.getParameters().add(param);
param.setDirection(DirectionKind.IN_LITERAL);
param.setName(getString("param.endpoint")); //$NON-NLS-1$
param.setNameInSource(getString("param.endpoint")); //$NON-NLS-1$
param.setNullable(NullableType.NULLABLE_LITERAL);
param.setType(datatypeManager.getBuiltInDatatype(DatatypeConstants.BuiltInNames.STRING));
param = factory.createProcedureParameter();
procedure.getParameters().add(param);
param.setDirection(DirectionKind.OUT_LITERAL);
param.setName(getString("param.result")); //$NON-NLS-1$
param.setNameInSource(getString("param.result")); //$NON-NLS-1$
param.setNullable(NullableType.NULLABLE_LITERAL);
param.setType(datatypeManager.getBuiltInDatatype(DatatypeConstants.BuiltInNames.XML_LITERAL));
}
private void modelOutputMessage(Operation operation,
ProcedureBuilder requestBuilder)
throws ModelerCoreException {
Message message = operation.getOutputMessage();
if( message == null ) {
return;
}
Part[] parts = message.getParts();
for (int i = 0; i < parts.length; i++) {
Part part = parts[i];
String elementNamespace;
String elementName;
if (part.isElement()) {
elementName = part.getElementName();
elementNamespace = part.getElementNamespace();
} else {
elementName = part.getTypeName(); // get the name here also
elementNamespace = part.getTypeNamespace();
}
ITraversalCtxFactory factory = new ResultTraversalContextFactory();
QName qName = new QName(elementNamespace, elementName);
SchemaObject sObject = schemaModel.getElement(qName);
if(null != sObject) {
requestBuilder.build(sObject, factory);
} else {
XSDSimpleTypeDefinition simpleType = XSDSchemaImpl.getSchemaForSchema(schemas[0].getSchemaForSchemaNamespace()).
resolveSimpleTypeDefinition(XSDConstants.SCHEMA_FOR_SCHEMA_URI_2001, elementName);
requestBuilder.build(simpleType, part.getName(), factory);
}
}
}
private void modelInputMessage(Operation operation,
ProcedureBuilder resultBuilder)
throws ModelerCoreException {
Message message = operation.getInputMessage();
Part[] parts = message.getParts();
for (int i = 0; i < parts.length; i++) {
Part part = parts[i];
String elementNamespace;
String elementName;
if (part.isElement()) {
elementName = part.getElementName();
elementNamespace = part.getElementNamespace();
} else {
elementName = part.getTypeName(); // get the name here also
elementNamespace = part.getTypeNamespace();
}
ITraversalCtxFactory factory = new RequestTraversalContextFactory();
QName qName = new QName(elementNamespace, elementName);
SchemaObject sObject = schemaModel.getElement(qName);
if(null != sObject) {
resultBuilder.build(sObject, factory);
} else {
XSDSimpleTypeDefinition simpleType = XSDSchemaImpl.getSchemaForSchema(schemas[0].getSchemaForSchemaNamespace()).
resolveSimpleTypeDefinition(XSDConstants.SCHEMA_FOR_SCHEMA_URI_2001, elementName);
resultBuilder.build(simpleType, part.getName(), factory);
}
}
}
public ModelResource getPhysicalModelForService(Operation operation)
throws CoreException {
Port port = operation.getBinding().getPort();
ModelResource serviceModel = null;
// we have the model for this port/service in our map
if (serviceNameToPhysicalMap.containsKey(port.getService().getName())) {
serviceModel = serviceNameToPhysicalMap.get(port.getService()
.getName());
} else {
// we don't have it in out map, and it doesn't exist
if( isSoapImportOption ) {
String srcName = soapSourceModelName;
IFile iFile = createNewFile(soapSourceModelFolder, srcName);
serviceModel = createNewModelResource(iFile);
serviceNameToPhysicalMap.put(soapSourceModelName, serviceModel);
ModelAnnotation modelAnnotation = serviceModel.getModelAnnotation();
modelAnnotation.setModelType(ModelType.PHYSICAL_LITERAL);
modelAnnotation.setPrimaryMetamodelUri(RelationalPackage.eNS_URI);
modelAnnotation.setNameInSource(port.getLocationURI());
if( ! procedureExists(serviceModel, INVOKE)) {
createPhysicalProcedure(port.getService(), serviceModel);
}
// Inject the connection profile properties into the physical model
connProvider.setConnectionInfo(serviceModel, connectionProfile);
} else {
IFile iFile = createNewFile(folder, port.getService().getName());
serviceModel = createNewModelResource(iFile);
serviceNameToPhysicalMap.put(port.getService().getName(),
serviceModel);
ModelAnnotation modelAnnotation = serviceModel.getModelAnnotation();
modelAnnotation.setModelType(ModelType.PHYSICAL_LITERAL);
modelAnnotation.setPrimaryMetamodelUri(RelationalPackage.eNS_URI);
modelAnnotation.setNameInSource(port.getLocationURI());
if( ! procedureExists(serviceModel, INVOKE)) {
createPhysicalProcedure(port.getService(), serviceModel);
}
// Inject the connection profile properties into the physical model
connProvider.setConnectionInfo(serviceModel, connectionProfile);
}
}
return serviceModel;
}
public boolean procedureExists(ModelResource modelResource, String procedureName) throws ModelWorkspaceException {
if( modelResource != null ) {
for( Object obj : modelResource.getAllRootEObjects() ) {
EObject eObj = (EObject)obj;
if (eObj instanceof Procedure && procedureName.equalsIgnoreCase(ModelObjectUtilities.getName(eObj)) ) {
return true;
}
}
}
return false;
}
public ModelResource getVirtualModelForService(Operation operation)
throws CoreException {
Port port = operation.getBinding().getPort();
ModelResource serviceModel;
// we have the model for this port/service in our map
if (serviceNameToVirtualMap.containsKey(port.getService().getName() + XML)) {
serviceModel = serviceNameToVirtualMap.get(port.getService()
.getName() + XML);
} else {
if( isSoapImportOption ) {
String viewName = soapViewModelName;
// we don't have it in out map, and it doesn't exist
IFile iFile = createNewFile(soapViewModelFolder, viewName);
serviceModel = createNewModelResource(iFile);
serviceNameToVirtualMap.put(soapViewModelName, serviceModel);
ModelAnnotation modelAnnotation = serviceModel.getModelAnnotation();
modelAnnotation.setModelType(ModelType.VIRTUAL_LITERAL);
modelAnnotation.setPrimaryMetamodelUri(RelationalPackage.eNS_URI);
} else {
// we don't have it in out map, and it doesn't exist
IFile iFile = createNewFile(folder, port.getService().getName() + XML);
serviceModel = createNewModelResource(iFile);
serviceNameToVirtualMap.put(port.getService().getName(),
serviceModel);
ModelAnnotation modelAnnotation = serviceModel.getModelAnnotation();
modelAnnotation.setModelType(ModelType.VIRTUAL_LITERAL);
modelAnnotation.setPrimaryMetamodelUri(RelationalPackage.eNS_URI);
}
}
return serviceModel;
}
private IFile createNewFile(IContainer container, String name) {
if( name.endsWith(DOT_XMI) ) {
Path modelPath = new Path(name);
IFile iFile = container.getFile(modelPath);
return iFile;
} else {
Path modelPath = new Path(name + DOT_XMI);
IFile iFile = container.getFile(modelPath);
return iFile;
}
}
private ModelResource createNewModelResource(IFile iFile) {
ModelResource resource = ModelerCore.create(iFile);
models.add(resource);
return resource;
}
}