Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Modeling Workflow Engine (MWE)"

(New page: == Introduction == The modeling workflow engine is a declarative configurable generator engine. It provides a simple, XML based configuration language with which all kinds of generator wo...)
 
Line 35: Line 35:
  
 
Here's an example:
 
Here's an example:
<code>
+
<pre>
 
<workflow>
 
<workflow>
 
   <component class="my.first.WorkflowComponent">
 
   <component class="my.first.WorkflowComponent">
Line 47: Line 47:
 
   </component>
 
   </component>
 
</workflow>
 
</workflow>
</code>
+
</pre>
 
The workflow shown above consists of three different workflow components. The order of the declaration is important! The workflow engine will execute the components in the specified order. To allow the workflow engine to instantiate the workflow component classes, WorkflowComponent implementations must have a default constructor.  
 
The workflow shown above consists of three different workflow components. The order of the declaration is important! The workflow engine will execute the components in the specified order. To allow the workflow engine to instantiate the workflow component classes, WorkflowComponent implementations must have a default constructor.  
  
Line 55: Line 55:
 
The Workflow class declares an adder() method
 
The Workflow class declares an adder() method
  
public void addComponent(WorkflowComponent comp)</para>
+
<pre>public void addComponent(WorkflowComponent comp)</pre>
 
which is used by the workflow factory in order to wire up a workflow (see next section 'Workflow Configuration').  
 
which is used by the workflow factory in order to wire up a workflow (see next section 'Workflow Configuration').  
  
Line 68: Line 68:
  
 
A workflow is wired up using an XML configuration language based on the dependency injection pattern (DI). Here is an example (not working, just an example!):  
 
A workflow is wired up using an XML configuration language based on the dependency injection pattern (DI). Here is an example (not working, just an example!):  
 
+
<pre>
 
<workflow>
 
<workflow>
 
<property name='genPath' value='/home/user/target'/>
 
<property name='genPath' value='/home/user/target'/>
Line 81: Line 81:
 
</component>
 
</component>
 
</workflow>
 
</workflow>
 +
</pre>
 
The root element is named workflow, then there are some property declarations followed by the declaration of two components.  
 
The root element is named workflow, then there are some property declarations followed by the declaration of two components.  
  
Line 102: Line 103:
  
 
Here is an example:
 
Here is an example:
 
+
<pre>
 
<workflow>
 
<workflow>
 
<property name='baseDir' value='./'/>
 
<property name='baseDir' value='./'/>
Line 112: Line 113:
 
pathToModel='${pathToModel}'/>
 
pathToModel='${pathToModel}'/>
 
</workflow>
 
</workflow>
 +
</pre>
 
First there is a simple property baseDir with the value "." defined. This property can be used in any attributes in the workflow file. The second property statement imports a property file. Property files use the well-known Java properties file syntax. There is one feature we added: You can use previously declared properties inside the properties file.  
 
First there is a simple property baseDir with the value "." defined. This property can be used in any attributes in the workflow file. The second property statement imports a property file. Property files use the well-known Java properties file syntax. There is one feature we added: You can use previously declared properties inside the properties file.  
  
 
Example:
 
Example:
 
+
<pre>
 
model = myModel
 
model = myModel
 
pathToModel = ${baseDir}/${model}.xmi
 
pathToModel = ${baseDir}/${model}.xmi
 +
</pre>
 
Components
 
Components
 
The wired up object graph consists of so called components (A workflow component is a special kind of a component). A component is declared by an XML element. The name represents the property of the parent component holding this component.  
 
The wired up object graph consists of so called components (A workflow component is a special kind of a component). A component is declared by an XML element. The name represents the property of the parent component holding this component.  
  
 
Example:
 
Example:
 
+
<pre>
 
<component class='MyBean'>
 
<component class='MyBean'>
 
<bean class='MyBean'/>
 
<bean class='MyBean'/>
 
</component>
 
</component>
 +
</pre>
 
The Java class MyBean needs to have a corresponding property accessor. E.g.:  
 
The Java class MyBean needs to have a corresponding property accessor. E.g.:  
 
+
<pre>
 
public class MyBean {
 
public class MyBean {
 
...
 
...
Line 135: Line 139:
 
...
 
...
 
}
 
}
 +
</pre>
 
There are currently the following possibilities for declaring the property accessors:  
 
There are currently the following possibilities for declaring the property accessors:  
  
 
Accessor methods
 
Accessor methods
 
As we have seen, one possibility for declaring a dependency is to declare a corresponding setter Method.  
 
As we have seen, one possibility for declaring a dependency is to declare a corresponding setter Method.  
 
+
<pre>
public void set<propertyname>(<PropertyType>; e)
+
public void set<propertyname>(<PropertyType> e)
 +
</pre>
 
If you want to set multiple multiple values for the same property, you should define an adder method.  
 
If you want to set multiple multiple values for the same property, you should define an adder method.  
 
+
<pre>
 
public void add<propertyname>(<PropertyType> e)
 
public void add<propertyname>(<PropertyType> e)
 +
</pre>
 
In some cases you may want to have key value pairs specified. This is done by providing the following method:  
 
In some cases you may want to have key value pairs specified. This is done by providing the following method:  
 
+
<pre>
 
public void put(Object k,Object v)
 
public void put(Object k,Object v)
 +
</pre>
 
Component creation
 
Component creation
 
The corresponding Java class (specified using the class attribute) needs to have a default constructor declared. If the class attribute is omitted, the Java class determined from the accessor method will be used. For the preceding example we could write...  
 
The corresponding Java class (specified using the class attribute) needs to have a default constructor declared. If the class attribute is omitted, the Java class determined from the accessor method will be used. For the preceding example we could write...  
 
+
<pre>
 
<component class='MyBean'>
 
<component class='MyBean'>
 
<bean/>
 
<bean/>
 
</component>
 
</component>
 +
</pre>
 
...because the setter method uses the MyBean type as its parameter type. This is especially useful for more complex configurations of workflow components.  
 
...because the setter method uses the MyBean type as its parameter type. This is especially useful for more complex configurations of workflow components.  
  
Line 161: Line 170:
  
 
Example:
 
Example:
 
+
<pre>
 
<workflow>
 
<workflow>
 
<component class='my.Checker'>
 
<component class='my.Checker'>
Line 172: Line 181:
 
...
 
...
 
</workflow>
 
</workflow>
 +
</pre>
 
In this example an object with the id mm (an instance of my.MetaModel), is first declared and then referenced using the attribute idRef. Note that this object will only be instantiated once and then reused. It is not allowed to specify any other attributes besides idRef for object references.  
 
In this example an object with the id mm (an instance of my.MetaModel), is first declared and then referenced using the attribute idRef. Note that this object will only be instantiated once and then reused. It is not allowed to specify any other attributes besides idRef for object references.  
  
Line 178: Line 188:
  
 
Example:
 
Example:
 
+
<pre>
 
<workflow>
 
<workflow>
 
<component class='my.Checker' myParam='foo'>
 
<component class='my.Checker' myParam='foo'>
 
<anotherParam value='bar'/>
 
<anotherParam value='bar'/>
 
</component>
 
</component>
 +
</workflow>
 +
</pre>
 
As you can see, there are two ways to specify a simple paramter.  
 
As you can see, there are two ways to specify a simple paramter.  
  
Line 195: Line 207:
 
Converters
 
Converters
 
There are currently converter implementations registered for the following Java types:  
 
There are currently converter implementations registered for the following Java types:  
 
+
* Object
Object
+
* String
 
+
* String[] (uses s.split(','))
String
+
* Boolean (both primitive and wrapper)
 
+
* Integer (both primitive and wrapper)
String[] (uses s.split(','))
+
 
+
Boolean (both primitive and wrapper)
+
 
+
Integer (both primitive and wrapper)
+
  
 
Including other workflow files (a.k.a cartridges)  
 
Including other workflow files (a.k.a cartridges)  
Line 210: Line 217:
  
 
file 1: mybean.mwe
 
file 1: mybean.mwe
 
+
<pre>
 
<anyname class='MyClass'/>
 
<anyname class='MyClass'/>
 +
</pre>
 
file 2: workflow.mwe
 
file 2: workflow.mwe
 
+
<pre>
 
<comp class='MyBean'>
 
<comp class='MyBean'>
 
<bean file='mybean.mwe'/>
 
<bean file='mybean.mwe'/>
 
</comp>
 
</comp>
 +
</pre>
 
One can pass properties and components into the included file in the usual way.  
 
One can pass properties and components into the included file in the usual way.  
  
 
file 1: mybean.mwe
 
file 1: mybean.mwe
 
+
<pre>
 
<anyname class='MyClass' aProp='${myParam}'>
 
<anyname class='MyClass' aProp='${myParam}'>
 
<bean idRef='myComponent'/>
 
<bean idRef='myComponent'/>
 
</anyname>
 
</anyname>
 +
<pre>
 
file 2: workflow.mwe
 
file 2: workflow.mwe
 
+
<pre>
 
<comp class='MyBean'>
 
<comp class='MyBean'>
 
<bean file='mybean.mwe'>
 
<bean file='mybean.mwe'>
Line 232: Line 242:
 
</bean>
 
</bean>
 
</comp>
 
</comp>
 +
</pre>
 
As you can see simple parameters are mapped to properties in the included workflow file and components can be accessed using the idRef attribute.  
 
As you can see simple parameters are mapped to properties in the included workflow file and components can be accessed using the idRef attribute.  
  
Line 240: Line 251:
 
InheritAll Feature
 
InheritAll Feature
 
If you don't want to explicitely pass the parameters to an included workflow description, you can use the inheritAll attribute. This will make all the properties and beans that are visible to the actual workflow file also visible to the included workflow file.  
 
If you don't want to explicitely pass the parameters to an included workflow description, you can use the inheritAll attribute. This will make all the properties and beans that are visible to the actual workflow file also visible to the included workflow file.  
 
+
<pre>
 
<component file="my/included/workflow.mwe" inheritAll="true"/>
 
<component file="my/included/workflow.mwe" inheritAll="true"/>
 +
</pre>
 
Component Implementation and Workflow Execution
 
Component Implementation and Workflow Execution
 
This section describes how to implement workflow components, how they can communicate with each other and how the workflow execution can be controlled.  
 
This section describes how to implement workflow components, how they can communicate with each other and how the workflow execution can be controlled.  
Line 247: Line 259:
 
The Workflow Context
 
The Workflow Context
 
Workflow components have to communicate among each other. For example, if an XMIReader component reads a model that a constraint checker component wants to check, the model must be passed from the reader to the checker. The way this happens is as follows. In the invoke operation, a workflow component has access to the so-called workflow context. This context contains any number of named slots. In order to communicate, two components agree on a slot name, the first component puts an object into that slot and the second component takes it from there. Basically, slots are named variables global to the workflow. The slot names are configured from the workflow file. Here is an example:  
 
Workflow components have to communicate among each other. For example, if an XMIReader component reads a model that a constraint checker component wants to check, the model must be passed from the reader to the checker. The way this happens is as follows. In the invoke operation, a workflow component has access to the so-called workflow context. This context contains any number of named slots. In order to communicate, two components agree on a slot name, the first component puts an object into that slot and the second component takes it from there. Basically, slots are named variables global to the workflow. The slot names are configured from the workflow file. Here is an example:  
 
+
<pre>
 
<?xml version="1.0" encoding="windows-1252"?>
 
<?xml version="1.0" encoding="windows-1252"?>
 
<workflow>
 
<workflow>
Line 261: Line 273:
 
</component>
 
</component>
 
</workflow>
 
</workflow>
 +
</pre>
 
As you can see, both these workflow components use the slot named model. Below is the (abbreviated) implementation of the XmiReader. It stores the model data structure into the workflow context in the slot whose name was configured in the workflow file.  
 
As you can see, both these workflow components use the slot named model. Below is the (abbreviated) implementation of the XmiReader. It stores the model data structure into the workflow context in the slot whose name was configured in the workflow file.  
 
+
<pre>
 
public class XmiReader implements WorkflowComponent {
 
public class XmiReader implements WorkflowComponent {
  
Line 278: Line 291:
  
 
}
 
}
 +
</pre>
 
The checker component reads the model from that slot:
 
The checker component reads the model from that slot:
 
+
<pre>
 
public class Checker implements WorkflowComponent {
 
public class Checker implements WorkflowComponent {
  
Line 295: Line 309:
 
}
 
}
 
}
 
}
 +
</pre>
 
Issues
 
Issues
 
Issues provide a way to report errors and warnings. There are two places where issues are used in component implementations:  
 
Issues provide a way to report errors and warnings. There are two places where issues are used in component implementations:  
Line 311: Line 326:
 
Workflow AO
 
Workflow AO
 
It is sometimes necessary to enhance existing workflow component declarations with additional properties. This is exemplified in the Template AOP example. To implement such an Advice Component, you have to extend the AbstractWorkflowAdvice class. You have to implement all the necessary getters and setters for the properties you want to be able to specify for that advice; also you have to implement the weave() Operation. In this operation, which takes the advised component as a parameter, you have to set the advised parameters:  
 
It is sometimes necessary to enhance existing workflow component declarations with additional properties. This is exemplified in the Template AOP example. To implement such an Advice Component, you have to extend the AbstractWorkflowAdvice class. You have to implement all the necessary getters and setters for the properties you want to be able to specify for that advice; also you have to implement the weave() Operation. In this operation, which takes the advised component as a parameter, you have to set the advised parameters:  
 
+
<pre>
 
public class GeneratorAdvice extends AbstractWorkflowAdvice {
 
public class GeneratorAdvice extends AbstractWorkflowAdvice {
  
Line 331: Line 346:
  
 
}
 
}
 +
</pre>
 
In the workflow file things are straight forward: You have to specify the advice's component class to be the advice component, and use the special property adviceTarget to identify the target component:  
 
In the workflow file things are straight forward: You have to specify the advice's component class to be the advice component, and use the special property adviceTarget to identify the target component:  
 
+
<pre>
 
<workflow>
 
<workflow>
 
 
<cartridge file="workflow.mwe"/>
 
<cartridge file="workflow.mwe"/>
 
<component adviceTarget="generator"  
 
<component adviceTarget="generator"  
 
class="oaw.xpand2.GeneratorAdvice">
 
class="oaw.xpand2.GeneratorAdvice">
 
<advices value="templates::Advices"/>
 
<advices value="templates::Advices"/>
</component>
+
</component>
</workflow>
+
</workflow>
 +
</pre>
 
Invoking a workflow
 
Invoking a workflow
 
If you have described your generator process in a workflow file, you might want to run it :-) There are different possibilities for doing so.  
 
If you have described your generator process in a workflow file, you might want to run it :-) There are different possibilities for doing so.  
Line 346: Line 362:
 
Starting the WorkflowRunner
 
Starting the WorkflowRunner
 
The class org.openarchitectureware.workflow.WorkflowRunner is the main entry point if you want to run the workflow from the command line. Take a look at the following example:  
 
The class org.openarchitectureware.workflow.WorkflowRunner is the main entry point if you want to run the workflow from the command line. Take a look at the following example:  
 
+
<pre>
java org.openarch..WorkflowRunner path/workflow.mwe
+
java org.eclipse.mwe.core.WorkflowRunner path/workflow.mwe
 +
</pre>
 
You can override properties using the -p option:
 
You can override properties using the -p option:
 
+
<pre>
java org.openarch..WorkflowRunner -pbasedir=/base/ path/workflow.mwe
+
java org.eclipse.mwe.core.WorkflowRunner -pbasedir=/base/ path/workflow.mwe
 +
</pre>
 
Starting with Ant
 
Starting with Ant
 
We also have an Ant task. Here's an example:
 
We also have an Ant task. Here's an example:
 
+
<pre>
<target name='generate'>
+
<target name='generate'>
<taskdef name="workflow" classname="org.eclipse.emf.mwe.core.ant.WorkflowAntTask"/>
+
<taskdef name="workflow" classname="org.eclipse.emf.mwe.core.ant.WorkflowAntTask"/>
<workflow file='path/workflow.mwe'>
+
<workflow file='path/workflow.mwe'>
<param name='baseDir' value='/base/'/>
+
<param name='baseDir' value='/base/'/>
</workflow>
+
</workflow>
...
+
...
</target>
+
</target>
 +
</pre>
 
The Workflow ant task extends the Java ant task. Therefore, you have all the properties known from that task (classpath, etc.).  
 
The Workflow ant task extends the Java ant task. Therefore, you have all the properties known from that task (classpath, etc.).  
  
Line 369: Line 388:
  
 
The slotContents map allows you to fill stuff into the workflow from your application. This is a typical use case: you run MWE from within your app because you already have a model in memory.  
 
The slotContents map allows you to fill stuff into the workflow from your application. This is a typical use case: you run MWE from within your app because you already have a model in memory.  
 
+
<pre>
 
String wfFile = "somePath\\workflow.mwe";
 
String wfFile = "somePath\\workflow.mwe";
 
Map properties = new HashMap();
 
Map properties = new HashMap();
 
Map slotContents = new HashMap();
 
Map slotContents = new HashMap();
 
new WorkflowRunner().run(wfFile ,  
 
new WorkflowRunner().run(wfFile ,  
new NullProgressMonitor(), properties, slotContents)
+
new NullProgressMonitor(), properties, slotContents);
 +
</pre>
 
Starting from Eclipse
 
Starting from Eclipse
 
You can also run a workflow file from within Eclipse, if you have installed the MWE plugins. Just right-click on the workflow file (whatever.mwe) and select Run As → MWE Workflow.
 
You can also run a workflow file from within Eclipse, if you have installed the MWE plugins. Just right-click on the workflow file (whatever.mwe) and select Run As → MWE Workflow.

Revision as of 18:51, 13 October 2007

Introduction

The modeling workflow engine is a declarative configurable generator engine. It provides a simple, XML based configuration language with which all kinds of generator workflows can be described. A generator workflow consists of a number of so-called workflow components that are executed sequentially in a single JVM.

Workflow components

At the heart of the workflow engine lies the WorkflowComponent. A WorkflowComponent represents a part of a generator process. Such parts are typically model parsers, model validators, model transformers and code generators. MWE ships with different WorkflowComponents which should be used where suitable, but you can also implement your own. The only thing you have to do is to implement the org.eclipse.emf.mwe.core.WorkflowComponent interface:

public interface WorkflowComponent { 

	/** 
	 * @param ctx 
	 * 		current workflow context 
	 * @param monitor 
	 * 		implementors should provide some feedback about the progress 
	 * 		using this monitor 
	 * @param issues 
	 */ 
	public void invoke(WorkflowContext ctx, ProgressMonitor monitor, Issues issues); 
	
	/** 
	 * Is called by the container after configuration so the 
	 * component can validate the configuration before invocation. 
	 * 
	 * @param issues - 
	 * implementors should report configuration issues to this. 
	 */ 
	 public void checkConfiguration(Issues issues);
	 
}

The invoke() operation performs the component's actual work. checkConfiguration is used to check whether the component is configured correctly before the workflow starts. More on these two operations later.

A workflow description consists of a list of configured WorkflowComponents.

Here's an example:

<workflow>
   <component class="my.first.WorkflowComponent">
      <aProp value="test"/>
   </component>
   <component class="my.second.WorkflowComponent">
      <anotherProp value="test2"/>
   </component>
   <component class="my.third.WorkflowComponent">
      <prop value="test"/>
   </component>
</workflow>

The workflow shown above consists of three different workflow components. The order of the declaration is important! The workflow engine will execute the components in the specified order. To allow the workflow engine to instantiate the workflow component classes, WorkflowComponent implementations must have a default constructor.

Workflow A workflow is just a composite implementation of the WorkflowComponent interface. The invoke and checkConfiguration methods delegate to the contained workflow components.

The Workflow class declares an adder() method

public void addComponent(WorkflowComponent comp)

which is used by the workflow factory in order to wire up a workflow (see next section 'Workflow Configuration').

Workflow Components with IDs If you want your workflow components to have an ID (so that you can recognize its output in the log) you have to implement the interface WorkflowComponentWithID and the setID() and getID() operations. Alternatively, you can also extend the base class AbstractWorkflowComponent, which handles the ID setter/getter for you.

More convenience AbstractWorkflowComponent has a property called skipOnErrors. If set to true, it will not execute if the workflow's issues collection contains errors. This is convenient if you want to be able to skip code generation when the preceding model verification finds errors. Note that instead of implementing invoke(...), subclasses of AbstractWorkflowComponent have to implement invokeInternal(...). This is necessary to allow the framework to intercept the invocation and stop it when there are errors in the workflow.


Workflow Configuration

A workflow is wired up using an XML configuration language based on the dependency injection pattern (DI). Here is an example (not working, just an example!):

<workflow>
	<property name='genPath' value='/home/user/target'/>
	<property name='model' value='/home/user/model.xmi'/>
	<component class='an.handwriten.XmiReader'>
		<model value='${model}'/>
	</component>
	<component class='oaw.xpand2.Generator'>
		<outlet>
			<path value='${genPath}'/>
		</outlet>
	</component>
</workflow>

The root element is named workflow, then there are some property declarations followed by the declaration of two components.

Here is a tree representation of the resulting Java object graph:


Figure 42. Java Object Graph


The configuration language expresses four different concepts:

Properties Borrowing from Apache Ant, we use the concept of properties. Properties can be declared anywhere in a workflow file. They will be available after declaration.

We have two different kinds of properties

simple properties

property files

Here is an example:

<workflow>
	<property name='baseDir' value='./'/>
	<property file='${baseDir}/my.properties'/>
	<component 
		class='my.Comp' 
		srcDir='${baseDir}' 
		modelName='${model}'
		pathToModel='${pathToModel}'/>
</workflow>

First there is a simple property baseDir with the value "." defined. This property can be used in any attributes in the workflow file. The second property statement imports a property file. Property files use the well-known Java properties file syntax. There is one feature we added: You can use previously declared properties inside the properties file.

Example:

model = myModel
pathToModel = ${baseDir}/${model}.xmi

Components The wired up object graph consists of so called components (A workflow component is a special kind of a component). A component is declared by an XML element. The name represents the property of the parent component holding this component.

Example:

<component class='MyBean'>
	<bean class='MyBean'/>
</component>

The Java class MyBean needs to have a corresponding property accessor. E.g.:

public class MyBean {
	...
	public void setBean(MyBean b) {
		bean = b;
	}
	...
}

There are currently the following possibilities for declaring the property accessors:

Accessor methods As we have seen, one possibility for declaring a dependency is to declare a corresponding setter Method.

public void set<propertyname>(<PropertyType> e)

If you want to set multiple multiple values for the same property, you should define an adder method.

public void add<propertyname>(<PropertyType> e)

In some cases you may want to have key value pairs specified. This is done by providing the following method:

public void put(Object k,Object v)

Component creation The corresponding Java class (specified using the class attribute) needs to have a default constructor declared. If the class attribute is omitted, the Java class determined from the accessor method will be used. For the preceding example we could write...

<component class='MyBean'>
	<bean/>
</component>

...because the setter method uses the MyBean type as its parameter type. This is especially useful for more complex configurations of workflow components.

Note that we will probably add factory support in the future.

References A component can have an attribute id. If this is the case we can refer to this component throughout the following workflow configuration.

Example:

<workflow>
	<component class='my.Checker'>
		<metaModel id='mm' class='my.MetaModel' 
			metaModelPackage='org.eclipse.metamodel'/>
	</component>
	<component class='my.Generator'>
		<metaModel idRef='mm'/>
	</component>
	...
</workflow>

In this example an object with the id mm (an instance of my.MetaModel), is first declared and then referenced using the attribute idRef. Note that this object will only be instantiated once and then reused. It is not allowed to specify any other attributes besides idRef for object references.

Simple Parameters Elements with only one attribute value are simple parameters. Simple parameters may not have any child elements.

Example:

<workflow>
	<component class='my.Checker' myParam='foo'>
		<anotherParam value='bar'/>
	</component>
</workflow>

As you can see, there are two ways to specify a simple paramter.

using an XML attribute

using a nested XML element with an attribute value

Both methods are equivalent, although declaring an attribute way saves a few keystrokes. However, the attributes class, id and file are reserved so they cannot be used.

Parameters are injected unsing the same accessor methods as described for components. The only difference is that they are not instantiated using a default constructor but instead using a so called converter.

Converters There are currently converter implementations registered for the following Java types:

  • Object
  • String
  • String[] (uses s.split(','))
  • Boolean (both primitive and wrapper)
  • Integer (both primitive and wrapper)

Including other workflow files (a.k.a cartridges) If an element has a property file it is handled as an inclusion. Using an inclusion one can inject a graph described in another workflow file. Here is an example:

file 1: mybean.mwe

<anyname class='MyClass'/>

file 2: workflow.mwe

<comp class='MyBean'>
	<bean file='mybean.mwe'/>
</comp>

One can pass properties and components into the included file in the usual way.

file 1: mybean.mwe

<anyname class='MyClass' aProp='${myParam}'>
	<bean idRef='myComponent'/>
</anyname>
<pre>
file 2: workflow.mwe
<pre>
<comp class='MyBean'>
	<bean file='mybean.mwe'>
		<myParam value='foo'/>
		<myComponent class='MyBean'/>
	</bean>
</comp>

As you can see simple parameters are mapped to properties in the included workflow file and components can be accessed using the idRef attribute.

Properties defined in the included workflow description will be overwritten by the passed properties.

The root element of a workflow description can have any name, because there is no parent defining an accessor method. Additionally you have to specify the attribute class for a root element. There is only one exception: If the root element is named workflow the engine knows that it has to instantiate the type org.eclipse.emf.mwe.internal.core.Workflow. Of course you can specify your own subtype of org.eclipse.emf.mwe.internal.core.Workflow using the class attribute (if you need to for any reason).

InheritAll Feature If you don't want to explicitely pass the parameters to an included workflow description, you can use the inheritAll attribute. This will make all the properties and beans that are visible to the actual workflow file also visible to the included workflow file.

<component file="my/included/workflow.mwe" inheritAll="true"/>

Component Implementation and Workflow Execution This section describes how to implement workflow components, how they can communicate with each other and how the workflow execution can be controlled.

The Workflow Context Workflow components have to communicate among each other. For example, if an XMIReader component reads a model that a constraint checker component wants to check, the model must be passed from the reader to the checker. The way this happens is as follows. In the invoke operation, a workflow component has access to the so-called workflow context. This context contains any number of named slots. In order to communicate, two components agree on a slot name, the first component puts an object into that slot and the second component takes it from there. Basically, slots are named variables global to the workflow. The slot names are configured from the workflow file. Here is an example:

<?xml version="1.0" encoding="windows-1252"?>
<workflow>
	<property file="workflow.properties"/>

	<component id="xmiParser" 
		class="my.XmiReader">
		<outputSlot value="model"/>
	</component>

	<component id="checker" class="datamodel.generator.Checker">
		<modelSlot value="model"/>
	</component>
</workflow>

As you can see, both these workflow components use the slot named model. Below is the (abbreviated) implementation of the XmiReader. It stores the model data structure into the workflow context in the slot whose name was configured in the workflow file.

public class XmiReader implements WorkflowComponent {

	private String outputSlot = null;

	public void setOutputSlot(String outputSlot) {
		this.outputSlot = outputSlot;
	}

	public void invoke(WorkflowContext ctx, ProgressMonitor monitor,
		Issues issues) {
		Object theModel = readModel();
		ctx.put( outputSlot, theModel );
	}

}

The checker component reads the model from that slot:

public class Checker implements WorkflowComponent {

	private String modelSlot;

	public final void setModelSlot( String ms ) {
		this.modelSlot = ms;
	}

	public final void invoke(WorkflowContext ctx, 
		ProgressMonitor monitor, Issues issues) {

		Object model = ctx.get(modelSlot);
		check(model);
	}
}

Issues Issues provide a way to report errors and warnings. There are two places where issues are used in component implementations:

Inside the checkConfiguration operation, you can report errors or warnings. This operation is called before the workflow starts running.

Inside the invoke operation, you can report errors or warnings that occur during the execution of the workflow. Typical examples are constraint violations.

The Issues API is pretty straightforward: you can call addError and addWarning. The operations have three parameters: the reporting component, a message as well as the model element that caused the problem, if there is one. The operations are also available in a two-parameter version, omitting the first (reporting component) parameter.

Controlling the Workflow There is an implicit way of controlling the workflow: if there are errors reported from any of the checkConfiguration operations of any workflow component, the workflow will not start running!

There is also an explicit way of terminating the execution of the workflow: if any invoke operation throws a WorkflowInterruptedException (a runtime exception) the workflow will terminate immediately.

Workflow AO It is sometimes necessary to enhance existing workflow component declarations with additional properties. This is exemplified in the Template AOP example. To implement such an Advice Component, you have to extend the AbstractWorkflowAdvice class. You have to implement all the necessary getters and setters for the properties you want to be able to specify for that advice; also you have to implement the weave() Operation. In this operation, which takes the advised component as a parameter, you have to set the advised parameters:

public class GeneratorAdvice extends AbstractWorkflowAdvice {

	private String advices;

	public String getAdvices() {
		return advices;
	}

	public void setAdvices(String advices) {
		this.advices = advices;
	}

	@Override
	public void weave(WorkflowComponent c) {
		Generator gen = (Generator)c;
		gen.setAdvices(advices);
	}

}

In the workflow file things are straight forward: You have to specify the advice's component class to be the advice component, and use the special property adviceTarget to identify the target component:

<workflow>
	<cartridge file="workflow.mwe"/>
		<component adviceTarget="generator" 
			class="oaw.xpand2.GeneratorAdvice">
			<advices value="templates::Advices"/>
	</component>
</workflow>

Invoking a workflow If you have described your generator process in a workflow file, you might want to run it :-) There are different possibilities for doing so.

Starting the WorkflowRunner The class org.openarchitectureware.workflow.WorkflowRunner is the main entry point if you want to run the workflow from the command line. Take a look at the following example:

java org.eclipse.mwe.core.WorkflowRunner path/workflow.mwe

You can override properties using the -p option:

java org.eclipse.mwe.core.WorkflowRunner -pbasedir=/base/ path/workflow.mwe

Starting with Ant We also have an Ant task. Here's an example:

<target name='generate'>
	<taskdef name="workflow" classname="org.eclipse.emf.mwe.core.ant.WorkflowAntTask"/>
	<workflow file='path/workflow.mwe'>
		<param name='baseDir' value='/base/'/>
	</workflow>
	...
</target>

The Workflow ant task extends the Java ant task. Therefore, you have all the properties known from that task (classpath, etc.).

Starting from you own code You can also run the generator from your own application code. Two things to note:

the contents of the properties map override the properties defined in the workflow.

The slotContents map allows you to fill stuff into the workflow from your application. This is a typical use case: you run MWE from within your app because you already have a model in memory.

	String wfFile = "somePath\\workflow.mwe";
	Map properties = new HashMap();
	Map slotContents = new HashMap();
	new WorkflowRunner().run(wfFile , 
		new NullProgressMonitor(), properties, slotContents);

Starting from Eclipse You can also run a workflow file from within Eclipse, if you have installed the MWE plugins. Just right-click on the workflow file (whatever.mwe) and select Run As → MWE Workflow.

Back to the top