## Model Workflow

### Introduction

The APP4MC AMALTHEA platform provides the option to define a workflow on a model with different steps. AMALTHEA provides a simple API for implementing such a workflow. The definition of the workflow can be done either in plain Java or any other language, which is able to access Java classes.

The EASE framework provides a scripting environment inside of Eclipse with different script engines like Rhino (JavaScript), Jython or Groovy. AMALTHEA provides a sample and some convenient helpers based on EASE to define and execute such a workflow.

As an alternative APP4MC provides also an implementation for the Modeling Workflow Engine 2 (MWE2), coming from the Xtext framework. The definition of the workflow can be done in a textual syntax from MWE2, containing different components which are working on the given model.

AMALTHEA provides several plugins for this purpose:

org.eclipse.app4mc.amalthea.workflow.core
  Includes basic API and some predefined workflow components, which can be used independent from any framework.

org.eclipse.app4mc.amalthea.workflow.ease
  Provides some helper modules for EASE

org.eclipse.app4mc.amalthea.workflow.mwe2
  Provides basic classes for usage with MWE2

### General Structure

The plugin org.eclipse.app4mc.amalthea.workflow.core provides a general abstract class `org.eclipse.app4mc.amalthea.workflow.core.WorkflowComponent` which can be used to extend in the case to provide or implement an own workflow step.

It provides the following features:

- Defines the constant `AMALTHEA_SLOT`, which can be used to store and retrieve the AMALTHEA model from the `org.eclipse.app4mc.amalthea.workflow.core.Context`.
- Provides some convenient methods to retrieve the model from the context, get a copy of the model or store the model in the context.

The interface `org.eclipse.app4mc.amalthea.workflow.core.Context` provides convenient methods to store and retrieve data in a `org.eclipse.app4mc.amalthea.workflow.core.WorkflowComponent`.

The class `org.eclipse.app4mc.amalthea.workflow.core.DefaultContext` is a default implementation using an internal `java.util.HashMap` to store the data.

A sample workflow implementation with two components `WorkfklowComponent1` and `WorkflowComponent2` can look like the following structure.

Both classes are extending `org.eclipse.app4mc.amalthea.workflow.core.WorkflowComponent`.

The next step is to create a WorkflowDefinition, which needs to do the following steps:

1. Create an instance of a `org.eclipse.app4mc.amalthea.workflow.core.Context` using the `org.eclipse.app4mc.amalthea.workflow.core.DefaultContext`
2. Create an instance of `WorkflowComponent1` and configure it if needed with the proper setter methods
3. Call the run method of `WorkflowComponent1` and pass the context
4. Create an instance of `WorkflowComponent2` and configure it if needed with the proper setter methods
5. Call the run method of `WorkflowComponent2` and pass the context

Therefore using the context, data can be shared between the different workflow component implementations.
The following diagram is showing this flow in more detail:

### Available Basic Components

The APP4MC AMALTHEA platform ships with some available workflow steps, which can be used out of the box.

#### Model Reader

The component `org.eclipse.app4mc.amalthea.workflow.component.ModelReader` reads a given list of files containing AMALTHEA models. The result model is stored in the `AMALTHEA_SLOT` as default. Please refer the JavaDoc for more details.

Simple configuration inside of a workflow can look like the following:

    ModelReader reader = new ModelReader();
    reader.addFileName("path to file");
    reader.run(ctx);

#### Model Writer

The component `org.eclipse.app4mc.amalthea.workflow.component.ModelWriter` writes a given AMALTHEA model to either one file or several files. As default the current available model in the `AMALTHEA_SLOT` is taken.
The following parameters are available to set:

- `boolean singleFile` default `true`
- `String` `outputDir`
- `String` `fileName`, if output is set to split files the different models are separated by an additional suffix in the name indicating the contained model.

Sample configuration inside of a workflow:

    ModelWriter writer = new ModelWriter();
    writer.setOutputDir("path to dir");
    writer.setFileName("output.amxmi");
    writer.run(ctx);

#### Add Schedule Points

The component `org.eclipse.app4mc.amalthea.workflow.component.AddSchedulePoints` modifies a given model (default in the `AMALTHEA_SLOT`) in that way, that in the `org.eclipse.app4mc.amalthea.model.sw.SWModel` the contained `org.eclipse.app4mc.amalthea.model.sw.Task` elements are checked if the preemption is set to cooperative. If this is the case, it will add between the elements of the `org.eclipse.app4mc.amalthea.model.sw.CallGraph` new elements of type `org.eclipse.app4mc.amalthea.model.sw.SchedulePoint`.

Sample configuration inside of a workflow:

    AddSchedulePoints addSchedulePoints = new AddSchedulePoints();
    addSchedulePoints.run(ctx);

### EASE modules

The purpose of using EASE is to provide one way to define and run a workflow for a model.
Therefore APP4MC provides some helper methods to be used in the EASE scripting environment.
The modules are provided by the plugin org.eclipse.app4mc.amalthea.workflow. ease .

#### Workflow Module

The workflow module provides some helpers regarding running a APP4MC workflow definition based on EASE.
The general module can be loaded with the following line:

    loadModule('/APP4MC/Workflow')

EASE opens an own Eclipse console by default to show the output of the executed script when using right click and Run as -> EASE Script. Therefore if you are using Log4J for your logging, you can use the following provided methods to configure dedicated Log4J Loggers to use also the EASE console for output.

The following overview gives an overview about the available helper methods:

Method | Params | Description
--- | --- | ---
addLoggerToConsole | String loggerName | Adds a Log4J logger to the output of the current used output of the EASE script engine.
addLoggerToConsole | String loggerName, String pattern | Adds a Log4J logger to the output of the current used output of the EASE script engine with a given pattern, see `org.apache.log4j.PatternLayout` for more details
endWorkflow | - | Basic finish actions to be performed, should be called at the end

### MWE2 Workflow

The plugin org.eclipse.app4mc.amalthea.workflow. mwe2 provides a general class `org.eclipse.app4mc.amalthea.workflow.base.AmaltheaWorkflow` which can be used to extend in the case to provide or implement an own workflow step based on Modeling Workflow Engine 2 (MWE2).
It provides the following features:

- Extends `org.eclipse.emf.mwe.core.lib.WorkflowComponentWithModelSlot`
- Defines the constant AMALTHEA_SLOT, which can be used to store and retrieve the AMALTHEA model from the `org.eclipse.emf.mwe.core.WorkflowContext`.
- Provides some convenient methods to retrieve the model from the context, get a copy of the model or store the model in the context.

To use the AMALTHEA model workflow component, currently the following dependencies are needed in addition to the AMALTHEA model plugins:

- org.eclipse.app4mc.amalthea.workflow.mwe2
- org.eclipse.emf.mwe2.lib
- org.eclipse.emf.mwe2.launch
- org.apache.log4j

#### MWE2 Components

The APP4MC AMALTHEA platform ships with some available workflow steps for usage together with MWE2.
Note: To use the components below as shown in the corresponding configurations, the classes must be imported!

##### Reader

The component `org.eclipse.app4mc.amalthea.workflow.mwe2.util.AmaltheaReader` reads a given list of files containing AMALTHEA models. The result model is stored in the AMALTHEA_SLOT as default.

Sample configuration inside of a workflow:

    component = AmaltheaReader {
    	fileName = "${base}/model/AMALTHEA_Democar_MappingExample.amxmi"
    	fileName = "${base}/model/AMALTHEA_Democar_MappingExample-hw.amxmi"
    }

##### Writer

The component `org.eclipse.app4mc.amalthea.workflow.mwe2.util.AmaltheaWriter` writes a given AMALTHEA model to either one file or several files. As default the current available model in the AMALTHEA_SLOT is taken.
The following parameters are available to set:

- boolean singleFile default true
- String outputDir
- String fileName, if output is set to split files the different models are separated by an additional suffix in the name indicating the contained model.

Sample configuration inside of a workflow:

    component = AmaltheaWriter {
    	fileName = "createtasks"
    	singleFile = true
    	outputDir = "${base}/workflow-output"
    }

##### Add Schedule Points

The component `org.eclipse.app4mc.amalthea.workflow.mwe2.util.AddSchedulePoints` modifies a given model (default in the AMALTHEA_SLOT) in that way, that in the `org.eclipse.app4mc.amalthea.model.SWModel` the contained `org.eclipse.app4mc.amalthea.model.Task` elements are checked if the preemption is set to cooperative. If this is the case, it will add between the elements of the `org.eclipse.app4mc.amalthea.model.CallGraph` new elements of type `org.eclipse.app4mc.amalthea.model.SchedulePoint`.

Sample configuration inside of a workflow:

    component = AddSchedulePoints {
    }

### Current Limitations / Open Points

As there are two options available (Basic Java/EASE and MWE2 based) for running a workflow, there are currently also some limitations existent. The following table should help you to chose the right base:

Use Case | Supported in MWE2 | Supported in EASE | Reason
--- | --- | --- | ---

Loading of AMALTHEA model files (with cross document references) using workflow/script file

no

yes

- MWE2: Separate Java runtime is started by MWE2, Sphinx relies in running Eclipse instance
- EASE: Makes use of the runtime from runtime workbench

Using workflow components which are available in the runtime (i.e. workflow component classes which are already available in plugins as a part of the runtime product)

yes

yes

- MWE2: Separate runtime is started by MWE2 and if plugin (containing workflow component) is added as dependency then its classes are loaded
- EASE: Makes use of the runtime from runtime workbench and has access to all classes

Defining custom workflow components (Java classes) and using them in the workflow/script file located in the same workspace

yes

yes

- MWE2: Custom workflow component objects can be directly created in MWE2 script file(as in Java) and there is no restriction [irrespective of whether there is constructor]
- EASE: As the runtime from the launched workbench (runtime workbench) is used, custom Java classes available in the workspace are not part of the classpath. For creation of objects of custom workflow components (Java classes): EASE JVM module `createInstance` API should be used by specifying the absolute path of the Java class

Using Java classes (available in the runtime workspace from other plugins --> these classes are not part of runtime workbench) inside custom workflow component Java classes

yes

yes

- MWE2: works perfectly (just like native)
- EASE: works, for creation of object from custom Java classes, EASE JVM module is used, which compiles the used Java classes and creates objects of them

Using classes (API) from 3rd party libraries (added in the classpath of the plugin)inside custom workflow component java classes

yes

yes(*)

- MWE2: As new Java runtime is created, all the required plugins & jars are added in the classpath
- EASE: The runtime from workbench and the jars from the classpath of a plugin (created newly in the runtime workbench) are loaded by EASE.

*: This feature is only available for EASE versions newer than 0.3.0. Older versions will fail with an exception java.lang.NoClassDefFoundError: <your used class from 3rd party lib>

Sample for using EASE JVM module to load a class from the same workspace:

    var labelsCreationComponent = createInstance("workspace://com.ease.example/src/org/eclipse/app4mc/amalthea/example/workflow/components/CreateLabelsComponent.java")

### Adding a new workflow component

Below you will find a sample how to add and implement a new workflow component.

### Create project

1. Add a new plugin project with the name my.sample.workflow
2. Open the MANIFEST.MF in the META-INF folder.
3. Switch to the tab Dependencies to add the following plugin dependencies: org.eclipse.app4mc.amalthea.workflow.core
4. Add a new class `my.sample.workflow.HelloWorld`, which is extending `org.eclipse.app4mc.amalthea.workflow.core.WorkflowComponent`.
5. Implement something in the `runInternal(Context ctx) throws WorkflowException` method (see sample below).

    @Override
    protected void runInternal(Context ctx) throws WorkflowException {
    	// some checking if sw model is available
    	if (null == getAmaltheaModel(ctx).getSwModel()) {
    		throw new WorkflowException("No proper SWModel available!");
    	}
    	this.log.info("Number of tasks in model: " + getAmaltheaModel(ctx).getSwModel().getTasks().size());
    }

#### Execute the new component in the available sample

The previous created class `my.sample.workflow.HelloWorld` should be added to a workflow.
Therefore we are using the provided sample project from APP4MC org.eclipse.app4mc.amalthea.example.democar.mapping.
Before starting with the next steps, we need to start a new runtime from the existing workspace, so that the plugin my.sample.workflow is already loaded.

Note: If you want to use the classes from the plugin my.sample.workflow in a EASE script located in the same workspace, you can create an instance of it at runtime using the JVM module of EASE. Please consider the EASE documentation for more details.

1. Add the AMALTHEA democar samples to your workspace (File – New – Example – Democar Examples)
2. Go to the project org.eclipse.app4mc.amalthea.example.democar.mapping.
3. Open the sample_workflow.js located in the workflow folder.
4. Add to the imports: `importPackage(my.sample.workflow)`
5. Add to the logger configs: `addLoggerToConsole("my.sample.workflow")`
6. Add the First snippet below after the first AmaltheaReader component and before the CreateTasks component.
7. Run the script by doing a right click -> Run As -> EASE Script

    var ctx = new DefaultContext()

    //Reader
    var reader = new ModelReader()
    reader.addFileName(MODEL_LOCATION1)
    reader.addFileName(MODEL_LOCATION2)
    reader.run(ctx)

    var hw = new HelloWorld()
    hw.run(ctx)

    //create tasks based on initial model
    //result is saved in modelslot createtasks
    var createTasks = new CreateTasks()
    createTasks.run(ctx)