/****************************************************************************** | |
* Copyright (c) 2011-2012, EBM WebSourcing | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* EBM WebSourcing - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.bpel.ui.wizards; | |
import java.io.ByteArrayInputStream; | |
import java.io.File; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.net.URISyntaxException; | |
import java.util.Collection; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import javax.xml.namespace.QName; | |
import javax.xml.parsers.ParserConfigurationException; | |
import org.eclipse.bpel.common.wsdl.importhelpers.WsdlImportHelper; | |
import org.eclipse.bpel.common.wsdl.parsers.WsdlParser; | |
import org.eclipse.bpel.model.BPELFactory; | |
import org.eclipse.bpel.model.BPELPackage; | |
import org.eclipse.bpel.model.OnMessage; | |
import org.eclipse.bpel.model.PartnerLink; | |
import org.eclipse.bpel.model.Pick; | |
import org.eclipse.bpel.model.Process; | |
import org.eclipse.bpel.model.Reply; | |
import org.eclipse.bpel.model.Sequence; | |
import org.eclipse.bpel.model.Variable; | |
import org.eclipse.bpel.model.partnerlinktype.PartnerLinkType; | |
import org.eclipse.bpel.model.partnerlinktype.PartnerlinktypeFactory; | |
import org.eclipse.bpel.model.partnerlinktype.PartnerlinktypePackage; | |
import org.eclipse.bpel.model.partnerlinktype.Role; | |
import org.eclipse.bpel.model.resource.BPELResourceFactoryImpl; | |
import org.eclipse.bpel.model.resource.BPELWriter; | |
import org.eclipse.bpel.model.util.BPELConstants; | |
import org.eclipse.bpel.ui.BPELUIPlugin; | |
import org.eclipse.bpel.ui.Templates.TemplateResource; | |
import org.eclipse.bpel.ui.util.BPELUtil; | |
import org.eclipse.core.resources.IFile; | |
import org.eclipse.core.runtime.CoreException; | |
import org.eclipse.core.runtime.IPath; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.core.runtime.Path; | |
import org.eclipse.emf.common.util.URI; | |
import org.eclipse.emf.ecore.resource.Resource; | |
import org.eclipse.emf.ecore.resource.ResourceSet; | |
import org.eclipse.wst.wsdl.Definition; | |
import org.eclipse.wst.wsdl.Fault; | |
import org.eclipse.wst.wsdl.Import; | |
import org.eclipse.wst.wsdl.Message; | |
import org.eclipse.wst.wsdl.Operation; | |
import org.eclipse.wst.wsdl.Part; | |
import org.eclipse.wst.wsdl.PortType; | |
import org.eclipse.wst.wsdl.WSDLFactory; | |
import org.eclipse.wst.wsdl.util.WSDLConstants; | |
import org.xml.sax.SAXException; | |
/** | |
* Utilities used in the new BPEL wizard. | |
* <p> | |
* This class simplifies the reuse of the BPEL wizard pages. | |
* In any case, it assumes the 3 first pages of the wizard are reused. | |
* </p> | |
* | |
* @author Vincent Zurczak - EBM WebSourcing | |
*/ | |
public class NewBpelFileWizardUtils { | |
private final NewBpelFileFirstPage firstPage; | |
private final NewBpelFileTemplatePage wsdlPage; | |
private final NewBpelFilePortTypePage portTypePage; | |
private final IFile targetFile; | |
/** | |
* Constructor. | |
* @param firstPage the first page | |
* @param wsdlPage the WSDL page | |
* @param portTypePage the port type page | |
* @param targetFile the BPEL file to create | |
*/ | |
public NewBpelFileWizardUtils( | |
NewBpelFileFirstPage firstPage, | |
NewBpelFileTemplatePage wsdlPage, | |
NewBpelFilePortTypePage portTypePage, | |
IFile targetFile ) { | |
this.firstPage = firstPage; | |
this.wsdlPage = wsdlPage; | |
this.portTypePage = portTypePage; | |
this.targetFile = targetFile; | |
} | |
/** | |
* Creates a BPEL process and the associated resources from the selected template. | |
* @param monitor a progress monitor | |
* @throws CoreException if a resource fails to be created | |
*/ | |
public void createResourcesFromTemplate( IProgressMonitor monitor ) throws CoreException { | |
Map<String,Object> tplProperties = this.firstPage.getProcessTemplateProperties(); | |
tplProperties.putAll( this.wsdlPage.getProcessTemplateProperties()); | |
List<TemplateResource> tplResources = this.wsdlPage.getSelectedTemplate().getResources(); | |
monitor.subTask( Messages.BPELCreateOperation_0 ); | |
for( TemplateResource tplResource : tplResources ) { | |
String result = tplResource.process( tplProperties ); | |
IPath path = new Path( tplResource.getName( tplProperties )); | |
IFile targetFile = this.targetFile.getParent().getFile( path ); | |
if( targetFile.exists()) | |
targetFile.setContents( new ByteArrayInputStream( result.getBytes()), true, true, monitor ); | |
else | |
targetFile.create( new ByteArrayInputStream( result.getBytes()), true, monitor ); | |
monitor.worked( 1 ); | |
} | |
} | |
/** | |
* Creates a BPEL process from a WSDL definition. | |
* @param monitor a progress monitor | |
* @throws CoreException if a resource fails to be created | |
* @throws IOException | |
* @throws CoreException | |
*/ | |
public void createResourcesFromWsdl( IProgressMonitor monitor ) throws IOException, CoreException { | |
// Import the original WSDL? | |
monitor.subTask( "Processing the original WSDL definition..." ); | |
String newWsdlUrl = this.portTypePage.getWsdlUrl(); | |
if( this.portTypePage.isImportWsdl()) { | |
File targetDirectory = this.targetFile.getParent().getLocation().toFile(); | |
try { | |
Map<String,File> uriToImportedFile = new WsdlImportHelper().importWsdlOrXsdAndDependencies( targetDirectory, newWsdlUrl ); | |
File importedWsdlFile = uriToImportedFile.get( newWsdlUrl ); | |
if( importedWsdlFile == null ) | |
throw new IOException( "The WSDL file could not be found after import." ); | |
// The URL to put in the imports is the relative location of the WSDL | |
// with respect to the process (and they are both in the same directory) | |
newWsdlUrl = importedWsdlFile.getName(); | |
} catch( IllegalArgumentException e ) { | |
throw new IOException( "The WSDL could not be imported in the project.", e ); | |
} catch( URISyntaxException e ) { | |
throw new IOException( "The WSDL could not be imported in the project.", e ); | |
} catch( SAXException e ) { | |
throw new IOException( "The WSDL could not be imported in the project.", e ); | |
} catch( ParserConfigurationException e ) { | |
throw new IOException( "The WSDL could not be imported in the project.", e ); | |
} | |
} | |
monitor.worked( 2 ); | |
// Prepare the save operations | |
Map<Object,Object> saveOptions = new HashMap<Object,Object> (); | |
saveOptions.put( BPELWriter.SKIP_AUTO_IMPORT, Boolean.TRUE ); | |
ResourceSet resourceSet = WsdlParser.createBasicResourceSetForWsdl(); | |
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put( "bpel", new BPELResourceFactoryImpl()); | |
resourceSet.getPackageRegistry().put( PartnerlinktypePackage.eNS_URI, PartnerlinktypePackage.eINSTANCE ); | |
resourceSet.getPackageRegistry().put( BPELPackage.eNS_URI, BPELPackage.eINSTANCE ); | |
// Create the WSDL for the artifacts | |
monitor.subTask( "Generating the WSDL for the artifacts..." ); | |
Definition artifactsDefinition = generateWsdlArtifacts( monitor, newWsdlUrl ); | |
IFile wsdlFile = this.targetFile.getParent().getFile( new Path( computeWsdlArtifactsName())); | |
URI emfUri = URI.createPlatformResourceURI( wsdlFile.getFullPath().toString(), true ); | |
Resource resource = resourceSet.createResource( emfUri ); | |
resource.getContents().add( artifactsDefinition ); | |
resource.save( saveOptions ); | |
monitor.worked( 2 ); | |
// Create the process | |
monitor.subTask( "Generating the BPEL file..." ); | |
Process bpelProcess = generateBpelProcess( monitor, artifactsDefinition, newWsdlUrl ); | |
emfUri = URI.createPlatformResourceURI( this.targetFile.getFullPath().toString(), true ); | |
resource = resourceSet.createResource( emfUri ); | |
resource.getContents().add( bpelProcess ); | |
resource.save( saveOptions ); | |
monitor.worked( 2 ); | |
// Hack: if the process is abstract, replace the name space, | |
// since it is not supported by the meta-model | |
if( this.firstPage.isAbstractProcess()) { | |
java.net.URI fileUri = this.targetFile.getLocation().toFile().toURI(); | |
String newContent = WsdlImportHelper.readResourceContent( fileUri ); | |
newContent = newContent.replace( BPELConstants.NAMESPACE, BPELConstants.NAMESPACE_ABSTRACT_2007 ); | |
InputStream is = new ByteArrayInputStream( newContent.getBytes()); | |
this.targetFile.setContents( is, true, false, monitor ); | |
monitor.worked( 2 ); | |
} | |
} | |
/** | |
* Generates a WSDL for the artifacts. | |
* @param monitor a progress monitor | |
* @param newWsdlUrl the URL of the business WSDL (as to put in the import declaration) | |
* @return the created definition (to serialize) | |
*/ | |
@SuppressWarnings( "unchecked" ) | |
private Definition generateWsdlArtifacts( IProgressMonitor monitor, String newWsdlUrl ) { | |
PortType portType = this.portTypePage.getPortType(); | |
Definition businessDefinition = (Definition) portType.eContainer(); | |
Definition artifactsDefinition = WSDLFactory.eINSTANCE.createDefinition(); | |
artifactsDefinition.setTargetNamespace( businessDefinition.getTargetNamespace() + "Artifacts" ); | |
// Hack for the role: we need to define manually the name space prefix for the TNS of the business WSDL | |
artifactsDefinition.getNamespaces().put( "tns", businessDefinition.getTargetNamespace()); | |
// WSDL import | |
Import wsdlImport = WSDLFactory.eINSTANCE.createImport(); | |
wsdlImport.setLocationURI( newWsdlUrl ); | |
wsdlImport.setNamespaceURI( businessDefinition.getTargetNamespace()); | |
artifactsDefinition.addImport( wsdlImport ); | |
// Partner Link Type | |
PartnerLinkType plType = PartnerlinktypeFactory.eINSTANCE.createPartnerLinkType(); | |
plType.setName( portType.getQName().getLocalPart() + "PLT" ); | |
Role plRole = PartnerlinktypeFactory.eINSTANCE.createRole(); | |
plRole.setName( portType.getQName().getLocalPart() + "Role" ); | |
plRole.setPortType( portType ); | |
plType.getRole().add( plRole ); | |
artifactsDefinition.getEExtensibilityElements().add( plType ); | |
plType.setEnclosingDefinition( artifactsDefinition ); | |
return artifactsDefinition; | |
} | |
/** | |
* Generates a BPEL process. | |
* @param monitor a progress monitor | |
* @param artifactsDefintion the artifacts definition | |
* @param newWsdlUrl the URL of the business WSDL (as to put in the import declaration) | |
* @return the created process (to serialize) | |
*/ | |
private Process generateBpelProcess( IProgressMonitor monitor, Definition artifactsDefinition, String newWsdlUrl ) { | |
PortType portType = this.portTypePage.getPortType(); | |
Definition businessDefinition = (Definition) portType.eContainer(); | |
Process bpelProcess = BPELFactory.eINSTANCE.createProcess(); | |
bpelProcess.setName( this.targetFile.getName()); | |
bpelProcess.setTargetNamespace( businessDefinition.getTargetNamespace()); | |
bpelProcess.setPartnerLinks( BPELFactory.eINSTANCE.createPartnerLinks()); | |
bpelProcess.setVariables( BPELFactory.eINSTANCE.createVariables()); | |
// Import the business definition | |
org.eclipse.bpel.model.Import bpelImport = BPELFactory.eINSTANCE.createImport(); | |
bpelImport.setLocation( newWsdlUrl ); | |
bpelImport.setNamespace( businessDefinition.getTargetNamespace()); | |
bpelImport.setImportType( WSDLConstants.WSDL_NAMESPACE_URI ); | |
bpelProcess.getImports().add( bpelImport ); | |
// Import the artifacts definition | |
bpelImport = BPELFactory.eINSTANCE.createImport(); | |
bpelImport.setLocation( computeWsdlArtifactsName()); | |
bpelImport.setNamespace( businessDefinition.getTargetNamespace() + "Artifacts" ); | |
bpelImport.setImportType( WSDLConstants.WSDL_NAMESPACE_URI ); | |
bpelProcess.getImports().add( bpelImport ); | |
// Create the main partner link | |
PartnerLink pl = BPELFactory.eINSTANCE.createPartnerLink(); | |
for( Object elt : artifactsDefinition.getEExtensibilityElements()) { | |
if( ! ( elt instanceof PartnerLinkType )) | |
continue; | |
pl.setPartnerLinkType((PartnerLinkType) elt); | |
pl.setName( "bpelProcessPartner" ); | |
pl.setMyRole(((PartnerLinkType) elt).getRole().get( 0 )); | |
bpelProcess.getPartnerLinks().getChildren().add( pl ); | |
break; | |
} | |
// Prepare the flow itself | |
Sequence mainSequence = BPELFactory.eINSTANCE.createSequence(); | |
mainSequence.setName( "MainSequence" ); | |
bpelProcess.setActivity( mainSequence ); | |
Pick mainPick = BPELFactory.eINSTANCE.createPick(); | |
mainPick.setName( "SwitchInvokedOperation" ); | |
mainPick.setCreateInstance( true ); | |
mainSequence.getActivities().add( mainPick ); | |
// Create the variables: they are deduced from the port type to "implement" | |
Collection<Definition> definitions = WsdlParser.findAllWsdlDefinitions( businessDefinition ); | |
for( Object o : portType.getOperations()) | |
addOperationDerivedElements((Operation) o, bpelProcess, mainPick, pl, definitions ); | |
return bpelProcess; | |
} | |
/** | |
* Creates the required variables and activities to handle an invocation to this operation. | |
* @param operation the operation | |
* @param bpelProcess the BPEL process | |
* @param mainPick the main pick | |
* @param partnerLink the partner link associated with the various activities | |
* @param definitions all the definitions related to the business interface (in case information should be searched in them) | |
*/ | |
private void addOperationDerivedElements( Operation operation, Process bpelProcess, Pick mainPick, PartnerLink partnerLink, Collection<Definition> definitions ) { | |
String opName = BPELUtil.lowerCaseFirstLetter( operation.getName()); | |
// Input: create the variable... | |
Variable var = BPELFactory.eINSTANCE.createVariable(); | |
var.setName( opName + "Request" ); | |
findAndSetVariableXmlType( var, operation.getEInput().getEMessage().getQName(), definitions ); | |
bpelProcess.getVariables().getChildren().add( var ); | |
// ... and add an OnMessage activity | |
OnMessage onMessage = BPELFactory.eINSTANCE.createOnMessage(); | |
onMessage.setPartnerLink( partnerLink ); | |
onMessage.setVariable( var ); | |
onMessage.setOperation( operation ); | |
mainPick.getMessages().add( onMessage ); | |
// Output: if it exists... | |
if( operation.getOutput() != null ) { | |
// Create the variable... | |
var = BPELFactory.eINSTANCE.createVariable(); | |
var.setName( opName + "Response" ); | |
findAndSetVariableXmlType( var, operation.getEOutput().getEMessage().getQName(), definitions ); | |
bpelProcess.getVariables().getChildren().add( var ); | |
// And add a Reply activity | |
Reply reply = BPELFactory.eINSTANCE.createReply(); | |
reply.setName( "ReplyTo" + BPELUtil.upperCaseFirstLetter( opName )); | |
reply.setVariable( var ); | |
reply.setOperation( operation ); | |
reply.setPartnerLink( partnerLink ); | |
onMessage.setActivity( reply ); | |
} else { | |
onMessage.setActivity( BPELFactory.eINSTANCE.createEmpty()); | |
} | |
// Faults | |
if( operation.getFaults() != null ) { | |
for( Object oo : operation.getFaults().values()) { | |
Fault fault = (Fault) oo; | |
var = BPELFactory.eINSTANCE.createVariable(); | |
var.setName( opName + fault.getName()); | |
findAndSetVariableXmlType( var, fault.getEMessage().getQName(), definitions ); | |
bpelProcess.getVariables().getChildren().add( var ); | |
} | |
} | |
} | |
/** | |
* Finds and sets the XML type for a variable associated with a given WSDL message. | |
* @param variable the variable whose XML type must be set | |
* @param messageName the name of the WSDL message | |
* @param definitions the list of definitions the message part | |
*/ | |
private void findAndSetVariableXmlType( Variable variable, QName messageName, Collection<Definition> definitions ) { | |
// Find and set the variable's type | |
boolean found = false; | |
boolean processed = false; | |
for( Definition def : definitions ) { | |
for( Object o : def.getEMessages()) { | |
Message msg = (Message) o; | |
if( ! ( messageName.equals( msg.getQName()))) | |
continue; | |
found = true; | |
if( msg.getEParts().size() == 1 ) { | |
processed = true; | |
Part part = (Part) msg.getEParts().get( 0 ); | |
if( part.getTypeDefinition() != null ) | |
variable.setType( part.getTypeDefinition()); | |
else if( part.getElementDeclaration() != null ) | |
variable.setXSDElement( part.getElementDeclaration()); | |
} | |
} | |
} | |
// Log possible errors | |
if( ! found ) | |
BPELUIPlugin.log( new Exception( "The message " + messageName + " could not be found." ), IStatus.ERROR ); | |
else if( ! processed ) | |
BPELUIPlugin.log( new Exception( "The message " + messageName + " contains more than 1 part. This case is not supported." ), IStatus.ERROR ); | |
else if( variable.getXSDElement() == null && variable.getType() == null ) | |
BPELUIPlugin.log( new Exception( "The XML type could not be set for the variable " + variable.getName() + ". Please, report a bug." ), IStatus.ERROR ); | |
} | |
/** | |
* @return the name of the WSDL file for the artifacts | |
*/ | |
private String computeWsdlArtifactsName() { | |
IPath path = new Path( this.targetFile.getName()); | |
return path.removeFileExtension().toString() + "Artifacts.wsdl"; | |
} | |
} |