Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
JWT Transformations
For JWT 1.2 and earlier, see JWT Transformations (up to JWT 1.2.0 included).
Contents
Introducing JWT Transformations
Terminology
- Transformation : an import or an export.
- Import: A process producing a JWT workflow out of an input.
- Export: A process producing an output out of a JWT workflow.
Aim
The aim of transformations is to make possible to use workflows designed with JWT in another contexts, and vice versa, all the more since the JWT workflow (file) format is on its own not a standard used by other workflow editors or engines. In fact, transformations are the basis of compatibility, and what makes of JWT a bridge between various tools and platforms, not only in the workflow domain but also elsewhere like e.g. SOA, or conceptual diagrams, allowing to use them all together.
Use Cases
Here are a few use cases of transformations :
- description : import workflow description from another format (ex. BPMN to JWT transformation)
- execution : export JWT workflows to an executable format, so they can be executed on a runtime that supports the format (ex. JWT to XPDL or jPDL transformations)
- integration : use JWT tools along with other, complementary tools and platforms (ex. JWT to STP-IM)
- migration : export JWT workflows to use them elsewhere
- migration : import other workflow description file types not to discourage users that would migrate from external workflow editor to JWT
- simulation : Transformations will also be used when we will implement a function that will deploy the JWT workflow on an embedded workflow engine only with a few clicks (upcoming in JWT Desktop, using Bonita engine)
How to use it
You need a JWT WE with the jwt-transformations-base plugin installed as well as any transformation plugin you want to use, for example jwt-bpmn or jwt-xpdl.
This makes available "Export" and "Import" buttons in the JWT WE toolbar, and under those any transformations provided by the installed plugins. To use for example the XPDL transformation, choose Export, and there choose JWT2XPDL in the dialog box ; fill in the output file name, and start the transformation by clicking on OK.
Alternatively, do File->Export from the main toolbar after opening the workflow model in eclipse. Type "Export process" in the filter text area, and then choose Java Workflow Tooling->Export process into another metamodel. Select one of the transformations: XPDL, STP-IM, BPMN according to the format you want to export in.
Transformations Gallery
Here are the file types that are already compatible, or that will soon be, with JWT
XPDL
XPDL is an XML-based, executable workflow language standardized by the WfMC.
Only export JWT to XPDL is available. But there are some limitations. Scarbo provides an enriched version of it.
This transformation uses XSLT.
Produces executable XPDL that has been validated on the Bonita 4 workflow engine.
See the JWT2XPDL documentation for extensive specifications.
jPDL
jPDL is jBoss jBPM's XML-based executable workflow language.
This transformation uses XSLT.
Thoughts
Since jPDL is not an EMF-based metamodel, there are 3 main ways to process this transformation:
- Use XSLT
- pros: XSLT is standard, not IP issue, easy to integrate
- cons: XSLT not really extensible, customizable, maintainable...
- Use JWT model API and a DOM/SAX API for output
- pros: DOM or SAX API are embedded in Java, no IP issue
- cons: DOM/SAX not so fun to use
- Use JWT model API and jpdl Java API (package org.jbpm.jpdl)
- pros: full Java, maybe easier to use
- cons: code from jBpm API => IP issues for integration into JWT, a lot of deps...
Here are some mappings:
- jwt:Activity(Name) -> process-definition(name)
- InitialNode -> start-state
- (automatic) Action (without role) -> node
- Application -> action (same a Bonita hook)
- (human) Action (with role) -> task
- role -> swimlane
BPMN
BPMN is a business process representation format standardized by the OMG. Here we use the (EMF-based) XML format defined by STP's BPMN editor.
We have two different transformations:
- one from JWT to BPMN, allowing to export JWT workflow models to
- and one the other way round (from BPMN to JWT).
These transformations use ATL.
STP-IM
STP-IM is an SOA intermediate model by the SOA Tools Project dedicated to help bridging SOA-minded problematics and concerns.
We have a transformation from JWT to the STP Intermediate Model (STP-IM). This intermediate model allows the further development of JWT in SCA, BPMN, BPEL, etc.
This transformation uses ATL.
ECM Sync Demo
Export transformations to Document Management Systems, namely to Nuxeo or Alfresco. These transformations use the ECM synchronization JWT plugin in order to demonstrate how it can be used.
For developers
Core concepts
To facilitate the add of a transformation, we chose to use the extensions-based Eclipse plug-in architecture.
As of the 1.3.0 release, two extension points are available :
- org.eclipse.jwt.transformations.transformations, which allows to define a new implementation processing input in order to produce output.
- org.eclipse.jwt.transformations.iodefinitions, which allows to define types of input/output, and widgets to allow the user to choose these input/output. For instance, org.eclipse.jwt.transformations.iodefinitions.default.export.output.local is an iodefinition bundled with the transformation-base plugin, that allows the user to pick a local file
The latest plugin.xml is available online here : [1]. If you installed JWT in your Eclipse instance, formated documentation is available in the "Plug-ins" view, when selecting the "org.eclipse.jwt.transformations.baseTransformation" plugin, then "Extension points", then "Show extension point description".
When the plugin is started
Activator is called on the generic transformation plug-in. This activation calls the ProcessServiceMember.process method, which will get all informations to get informations from the extension point and add it to a registry that will be use later. (This mechanism is done as explained at http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html) This part of the code is used only once, at activation.
When a transformation is called
The method that handle the call just has to call TransformationsRegister.getInstance.getTransformation(transformationName_defined_in_extension).transform It is aimed to have one or several generic handlers that would be defined in generic plug-in, to unify transformations that have the same goal (export to model, export to execution platform compliant type...)
Extensibility
If necessary, it is easy to add some metadata to a transformation. To do it, just add an attribute to the extension point, add it as an attribute to the transformationService class, and set it when you load the extension is ProcessServiceRegistry.
How do I add a transformation to JWT?
Of course, it requires org.eclipse.jwt.transformation.base plug-in.
First, choose the input and output that your transformation will handle. Existing IODefinitions can be listed in Eclipse: just open the Plug-ins view, double-click org.eclipse.jwt.transformation.base, then click "Extension points", then select the "org.eclipse.jwt.transformations.iodefinitions" extension point and click "Find references". This will provide you with pointers to existing IODefinitions and their implementation. If no existing IODefinition suits your needs, you can always declare yours (see next section). The Java types corresponding to these IODefinitions, along with their (optional) parameters should be documented in the implementing class. TODO : list all implementations available by default here.
Second, implement a class that extends AbstractTransformation. The core method to implement is "void transform(Object input, Object output)" : it will allow you to perform the actual transformation, with the input and output chosen by the user as parameters (whose type depends on the IODefinition).
Finally, create an extension for the extension point "org.eclipse.jwt.transformations.transformations" that defines a name for your transformation and the class that implements the transformation (extending TransformationService), along with the iodefinitions you chose and more other optional fields. See #Core concepts for more information about extension points.
How do I add an IODefinition to JWT?
It also requires org.eclipse.jwt.transformation.base plug-in.
First, implement a class that extends AbstractIODefinition. The core method to implement is "IOHandle createIOControl(Composite parent, ContentChangeListener pageListener, IStructuredSelection currentSelection);". If you want to access a parameter loaded from an IODefinition reference, use "getParameter(String name)". The code of AbstractIODefinition and its interface, IODefinition is fully documented.
Then, create an extension for the extension point "org.eclipse.jwt.transformations.iodefinitions" that defines a label, the class that implements the transformation and the supported parameters.
XSLT Based Transformation
XSL transformations allow to transform a JWT workflow model (and its model extensions) from its own (EMF based) XML representation to any kind of XML or text, by writing an XSL template. It is a good all-around choice if you want to get XML or simple files (csv...) out of your workflow model.
- For XSL transformations, there is a tool plugin called jwt-transformations-xslt-tools which contains some stuff to call XSL transformations and some extension classes that can be used to "introspect" the JWT XML model file.
- These transformations use Xalan 2.7.1 (for XSLT) and (Xerces 2.9) for XPath processors (dependency taken from Orbit)
- Important: Any plugin that wishes to add an extension class have to add in its MANIFEST.MF this line Eclipse-RegisterBundle: org.apache.xalan
ATL Based Transformation
ATL transformations allow to transform the JWT workflow model (and its model extensions) from its EMF model to another EMF model, by writing a model-to model transformation in the ATL language. It is less common and more complex, but a powerful choice, especially if you already have an EMF version (.ecore file) of your target model. Note that if you don't have it, you have to find or write an ATL compatible version of your model first, for instance with the XML2Ecore tool if your target model is in XML.
- there is no JWT plugin that gathers useful ATL stuff for JWT yet, but you can have a look in existing ATL-based transformation plugins (jwt-bpmn, jwt-stpim...), or even copy-paste one of them as a basis.
- an ATL based transformation plugin requires the org.eclipse.m2m.atl.engine plugin, or the more recent org.eclipse.m2m.atl.engine.emfvm plugin.
- How to write an ATL transformation (.atl file) ? Have a look at the ATL language reference at ATL/User_Guide as well as at existing ATL-based transformation plugins (jwt-bpmn, jwt-stpim...)
- to compile the .atl file to its .asm executable couterpart, give to your project the ATL nature (right-click on project > Add ATL Nature). This gives it the ATL Builder, which will automatically compile all .atl files of your project.
- However you may not want to keep the ATL nature all the time (and remove it, by right-click on project > Remove ATL Nature), because it inclines to regenerate all .asm files a bit too often, which is meddlesome notably when working with source versioning.
How to call an ATL transformation in java (e.g. in a transformation plugin)
Here is a piece of code (from the jwt-bpmn plugin) that can be used to run ATL transformations in Java. Linked in ATL Howtos
/** * This sample is shared under WTFPL * http://sam.zoy.org/wtfpl/ */ package org.eclipse.jwt.transformations.bpmn; import java.net.URL; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.eclipse.emf.common.util.URI; import org.eclipse.m2m.atl.drivers.emf4atl.ASMEMFModel; import org.eclipse.m2m.atl.engine.AtlEMFModelHandler; import org.eclipse.m2m.atl.engine.AtlLauncher; import org.eclipse.m2m.atl.engine.AtlModelHandler; /** * Should implement a template method pattern... */ class ATLTransformation { private static ATLTransformation instance = null; private final static String resourcePath = "/org.eclipse.jwt.transformations.bpmn/src/org/eclipse/jwt/transformations/bpmn/resources/"; private AtlEMFModelHandler modelHandler; private URI XML_ModelResource; private URI JWT_ModelResource; private URI BPMN_ModelResource; private URL JWT2BPMN_TransfoResource; private URL BPMN2JWT_TransfoResource; private ASMEMFModel xmlMetamodel; private ASMEMFModel jwtMetamodel; private ASMEMFModel bpmnMetamodel; private URL JWT2BPMN_Pool_TransfoResource; private URL JWT2BPMN_Sub_TransfoResource; static public ATLTransformation getInstance() { if (instance == null) instance = new ATLTransformation(); return instance; } private void createResources() { modelHandler = (AtlEMFModelHandler) AtlModelHandler .getDefault(AtlModelHandler.AMH_EMF); XML_ModelResource = URI.createPlatformPluginURI(resourcePath + "XML.ecore", false); JWT_ModelResource = URI.createPlatformPluginURI(resourcePath + "jwt.ecore", false); BPMN_ModelResource = URI.createPlatformPluginURI(resourcePath + "bpmn.ecore", false); JWT2BPMN_TransfoResource = ATLTransformation.class.getResource("resources/JWT2BPMN.asm"); JWT2BPMN_Pool_TransfoResource = ATLTransformation.class.getResource("resources/Jwt2BpmnAllInPool.asm"); JWT2BPMN_Sub_TransfoResource = ATLTransformation.class.getResource("resources/Jwt2BpmnAllInSub.asm"); BPMN2JWT_TransfoResource = ATLTransformation.class.getResource("resources/BPMN2JWT.asm"); } private void initMetamodels(Map<String, Object> models) { xmlMetamodel = (ASMEMFModel) modelHandler.loadModel( "XML", modelHandler.getMof(), XML_ModelResource); jwtMetamodel = (ASMEMFModel) modelHandler.loadModel( "jwt", modelHandler.getMof(), JWT_ModelResource); bpmnMetamodel = (ASMEMFModel) modelHandler.loadModel( "bpmn", modelHandler.getMof(), BPMN_ModelResource); models.put("XML", xmlMetamodel); models.put("jwt", jwtMetamodel); models.put("bpmn", bpmnMetamodel); } public void jwt2bpmn(String inFilePath, String outFilePath) { try { Map<String, Object> models = new HashMap<String, Object>(); createResources(); initMetamodels(models); // get/create models ASMEMFModel jwtInputModel = (ASMEMFModel) modelHandler.loadModel("IN", jwtMetamodel, URI.createFileURI(inFilePath)); ASMEMFModel bpmnOutputModel = (ASMEMFModel) modelHandler.newModel("OUT", URI.createFileURI(outFilePath).toFileString(), bpmnMetamodel); // load models models.put("IN", jwtInputModel); models.put("OUT", bpmnOutputModel); // launch AtlLauncher.getDefault().launch(this.JWT2BPMN_Pool_TransfoResource, Collections.EMPTY_MAP, models, Collections.EMPTY_MAP, Collections.EMPTY_LIST, Collections.EMPTY_MAP); modelHandler.saveModel(bpmnOutputModel, outFilePath, false); dispose(models); } catch (Exception e) { e.printStackTrace(); } } public void bpmn2jwt(String inFilePath, String outFilePath) { try { Map<String, Object> models = new HashMap<String, Object>(); createResources(); initMetamodels(models); // get/create models ASMEMFModel bpmnInputModel = (ASMEMFModel) modelHandler.loadModel("IN", bpmnMetamodel, URI.createFileURI(inFilePath)); ASMEMFModel jwtOutputModel = (ASMEMFModel) modelHandler.newModel("OUT", URI.createFileURI(outFilePath).toString(), jwtMetamodel); // load models models.put("IN", bpmnInputModel); models.put("OUT", jwtOutputModel); // launch AtlLauncher.getDefault().launch(this.BPMN2JWT_TransfoResource, Collections.EMPTY_MAP, models, Collections.EMPTY_MAP, Collections.EMPTY_LIST, Collections.EMPTY_MAP); modelHandler.saveModel(jwtOutputModel, outFilePath, false); dispose(models); } catch (Exception e) { e.printStackTrace(); } } private void dispose(Map<String, Object> models) { for (Object model : models.values()) ((ASMEMFModel)model).dispose(); } }
As you can see, ATL transformations can be called in 4 steps:
- Add metamodel description to ATL
- Add models to ATL (specifying the metamodel that is used to describe them)
- Call the transformation (specifying the resource that describes the transformation and input and output files)
Remark: You can see that this piece of code loads directly ATL compiled files (*.asm) instead of ATL files. We could easily generate asm files from ATL at runtime using ATLCompiler class; however, it is better to assume that we have the compiled version of transformations. This way, we avoid compiling each time we call a transformation, and make the transformation faster.