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

VIATRA/Integration/MWE2 Integration

< VIATRA‎ | Integration
Revision as of 05:14, 16 June 2015 by Lunk.peter.incquerylabs.com (Talk | contribs) (Finished up modifications)

Transformation Chains Using VIATRA MWE 2 Integration

Motivation

Define heterogeneous model transformation chains using a specialized description language.

  • Sequence of model transformation steps can be easily specified and maintained.
  • Nontrivial side-effect relations between transformation steps can be handled.
  • The execution of event-driven transformation can be controlled.
  • Because of the specialized language, the description code base is short and easily expandable.

MWE 2 Basics

Generic modeling workflows can be easily described via using the Xtext Modeling Workflow Engine. It enables the creation of components with various attributes, and it executes them in order. More, exact information about the Xtext MWE 2 can be found at https://www.eclipse.org/Xtext/documentation/306_mwe2.html

VIATRA MWE2 Integration Library

The VIATRA MWE2 Integration library provides support for defining complex transformation chains using Xtext MWE 2 workflows. As it can be seen on the figure below, due to the fact that the transformation chain itself is an MWE component, it can be easily integrated into higher level modeling workflows.

VIATRAMWE2.png

A Transformation chain consists of numerous transformation steps and channels. This is required as certain transformations (for example event-driven transformations) do not follow the batch execution semantics of the MWE language. This way fine-grained incremental model transformations can be wrapped into these batch transformation chains. Transformation steps represent the individual transformations. Similar to the transformations themselves, these transformation steps define dependability relations towards each other. Channels can be used to determine these aforementioned dependability relations, as well as provide a direct communication method between transformation steps. If a step listens to a channel, the addition of a certain event to the channel will enable the execution of the step. After the execution has finished, new events are pushed to the site's target channels. The creation and processing of these events is the responsibility of the corresponding IEventFactory and IEventProcessor classes. The communicating transformation steps contain IListeningChannel and ITargetChannel objects. These wrapper classes bind together the channels with the factory (in case of target channels) or processor object (in case of listening channels) specified by the user. Note, that the transformation chain itself has input and output channels as well. These can be used to indicate the first and last transformation steps in the chain.

Prerequisites and Installation

In order to use this library, the following Eclipse plug-ins are required:

After the prerequisites are met clone the following repository and import the contained projects. Note, that it contains the source of the VIATRA framework as well. https://github.com/lunkpeter/org.eclipse.viatra

Library Classes

The library defines a set of base classes which represent the main elements the previously described transformation workflow semantics. User defined behavior can be added to these elements via inheritance.

Interfaces

  • ITransformationChain: Represents a basic transformation chain. It provides an interface for defining transformation steps, as well as owns references to its inwards and outwards facing channels.
  • ITransformationStep: Interface that defines the main entry points of transformation steps. The user defined functionality can be implemented in the execute, initialize and dispose methods.
  • IChannel: Defines methods for registering and receiving events.
  • IEvent: Interface for events, defines methods for getting and setting the event parameter.
  • IEventFactory: Provides an interface for creating certain typed events. As the events can contain parameters, event- and parameter types are specified as generic type parameters.
  • IEventProcessor: Provides an interface for creating certain typed events. As the events can contain parameters, event- and parameter types are specified as generic type parameters.
  • IListeningChannel: Provides an interface for channels, that are specifically designed as listening channels. It defines easy to use methods for event processing, that the owner transformation step can use.
  • ITargetChannel:Provides an interface for channels, that are specifically designed as target channels. It defines easy to use methods for event creation, that the owner transformation step can use.

Classes

  • MWE2TransformationChain: The MWE2 based implementation of the ITransformationChain interface it represents the entire transformation workflow as an MWE IWorkflowComponent.

It also defines a start and an end channel which can be used to identify the first and last transformation steps in the workflow. In its execute method, the registered transformation steps are initialized and a control event instance is sent to the start channel. After the initialization, the transformation chain object waits for the transformation workflow to finish, and disposes every transformation step.

  • MWE2TransformationStep:Abstract ITransformationStep implementation, that acts as a base class for user defined transformation steps. It incorporates the following features:
    • In its run() method, it periodically checks if one of its listening channels contains an event.
    • If events are contained it calls the execute() method, which has to be overridden by the user.
    • It also provides helper methods for processing events, and sending them.

If any of the specified listening channels contain an event, this transformation step will trigger, consuming the given event.

  • MWE2SyncTranformationStep: Abstract child class of the MWE2TransformationStep. The main difference is, that this step will be activated if ALL of its listening channels contain processable events. If the step is activated, it removes an event from every listening channel, and executes the user defined functionality.
  • MWE2Channel: MWE 2 based IChannel implementation. It stores its contained events in a BlockingQueue object.
  • MWE2ControlEvent: MWE2 Control event implementation. It has no parameters, its only purpose is to enable the transformation step it is being sent to. This is the default event type, every newly created IListeningChannel and ITargetChannel object will use this event by default. Naturally, this can be overridden by the addition of custom factories and processors.
  • MWE2ControlEventFactory: Event factory that is responsible for the instantiation of MWE2 Control events. This factory is the default factory of every newly instantiated ITargetChannel object.
  • MWE2ControlEventProcessor: Event processor that processes MWE2 Control events. This processor is the default processor of every newly instantiated ITargetChannel object.
  • MWE2ListeningChannel: MWE2 based listening channel implementation. It contains an IChannel and an IEventProcessor implementation. The event processor is used for processing the events contained by the channel. This IListeningChannel implementation also features name and priority properties, which are used for directly referencing the channels and help in defining an order in which conflicting channels can be processed.
  • MWE2TargetChannel: MWE 2 based ITargetChannel implementation. As specified in the ITargetChannel interface, instances of this class contain an IChannel and an IEventFactory object. The event factory is responsible for adding events to the channel. The TargetChannel class provides an easy to use interface for adding events to given channels, while also enabling the user to specify the soft type of the given channel, via adding a certain event factory.

Classes and interfaces used with event-driven transformations

  • IController: An interface that defines methods for explicitly controlling the execution of a fine-grained event-driven model transformation step.
  • IEnabler: Interface that defines methods that can enable and disable a fine-grained, event-driven event driven transformation step.
  • ISchedulerController: Interface that defines functions which are used when an IController object also wraps an IncQuery EVM Scheduler.
  • MWE2ControllableExecutor: An IncQuery EVM Executor, that enables the MWE 2 workflow to control any IncQuery EVM based fine-grained event-driven transformation, provided that the transformation's ExecutionSchema is created with this executor. It also implements the IController interface which enables the workflow to explicitly start the execution of the transformation. The MWE2ControllableExecutor also provides information about the state of the transformation (i.e.: if the transformation has reached a steady state or not).
  • MWE2EnablerExecutor: An IncQuery EVM Executor, that enables the MWE 2 workflow to control any EVM based fine-grained event-driven transformation, provided that the transformation's ExecutionSchema is created with this executor. It also implements the IEnabler interface which means that based on the state of other transformation steps, the event-driven transformation can be enabled or disabled.
  • MWE2BaseControllableScheduler: An EVM scheduler that enables the workflow to explicitly control the execution of a fine-grained event-driven transformation. As it implements the IController interface, its usage is similar to the MWE2ControllableExecutor. Internally, however, this solution uses a custom Scheduler object instead of an Executor. Using a custom Scheduler means, that the original IncQuery EVM Scheduler will be overridden.

Detailed information about the usage of these classes can be found in the CPS example.

Hello World Example

The usage of these classes can be most effectively described via a simple 'Hello World' example:

  • Create a new Eclipse plug-in project via File Menu --> New --> Project
  • Create a new MWE2 file (in a not default package) using the similar method. If prompted, add the Xtext nature to the project.

Define Transformation Steps

In the next step define the concrete transformation steps. Add these classes to the package created before.

BatchTransformationStep.java

import org.eclipse.emf.mwe2.runtime.workflow.IWorkflowContext;
import org.eclipse.viatra.emf.mwe2integration.mwe2impl.MWE2TransformationStep;
 
public class BatchTransformationStep extends MWE2TransformationStep {
    @Override
    public void initialize(IWorkflowContext ctx) {
        //The reference to the context is set
        this.context = ctx;
        //Normally the transformation is initialized here.
        System.out.println("Init batch transformation");   
    }
 
    public void execute() {
        //Firstly the triggering event is processed
        processNextEvent();
        //The transformation is executed 
        System.out.println("Batch transformation executed");
        //Events are sent to all target channels
        sendEventToAllTargets();
    }
 
    @Override
    public void dispose() {
        //Upon ending the runnable monitoring the incoming events needs to be stopped
        isRunning = false;
        //Dispose functions
        System.out.println("Dispose batch transformation");
    }
}

EventDrivenTransformationStep.java

import org.eclipse.emf.mwe2.runtime.workflow.IWorkflowContext;
import org.eclipse.viatra.emf.mwe2integration.mwe2impl.MWE2TransformationStep;
 
public class EventDrivenTransformationStep extends MWE2TransformationStep {
    @Override
    public void initialize(IWorkflowContext ctx) {
        System.out.println("Init event-driven transformation");
        this.context = ctx;
    }
 
    @Override
    public void execute() {
        processNextEvent();
        System.out.println("Send tick to event-driven transformation");
        sendEventToAllTargets();
    }
 
    @Override
    public void dispose() {
        isRunning = false;
        System.out.println("Dispose event-driven transformation");
 
    }
}

Define Transformation Structure

Define the transformation chain structure using the MWE2 language. Make sure to include all the referenced packages (if the classes are in the same package as the MWE2 file, include that package as well), and that the module name matches the containing package.

TransformationDemo.mwe2

module org.eclipse.viatra.emf.mwe2orchestrator.transdemo
 
import org.eclipse.viatra.emf.mwe2integration.*
import org.eclipse.viatra.emf.mwe2integration.mwe2impl.*
import org.eclipse.viatra.emf.mwe2integration.demo.*
 
//The channels between the transformation steps are defined here
var chainStartChannel = MWE2Channel {}
var chainEndChannel = MWE2Channel {}
 
var BatchChannel = MWE2Channel {}
var EventDrivenChannel = MWE2Channel {}
 
//The workflow and components are defined in the usual MWE2 fashion
Workflow {
	//Add an MWE2TransformationChain component
	component = MWE2TransformationChain {
		//define the start and end channels of the chain
		startChannel = MWE2TargetChannel{channel = chainStartChannel}
		endChannel = MWE2ListeningChannel{channel = chainEndChannel}
 
		//define individual transformation steps
		transformationStep = EventDrivenTransformationStep {
			//register listening channels to which this step listens
			//Add the chainStartChannel as listening channel, this way the transformation 
			//chain can start the step sequence
			listeningChannel = MWE2ListeningChannel{ channel = chainStartChannel}
			listeningChannel = MWE2ListeningChannel{ channel = EventDrivenChannel}
			//register target channels to which this step sends events
			targetChannel = MWE2TargetChannel{channel = BatchChannel}
		}
 
		transformationStep = BatchTransformationStep {
			listeningChannel = MWE2ListeningChannel{ channel = BatchChannel}
			//Add the chainEndChannel as target channel, this way the transformation 
			//chain when to finish its execution.
			targetChannel = MWE2TargetChannel{ channel = chainEndChannel}
		}
	}
	//You can add additional regular MWE2 components here
}

This example is included in the test project of the library.

Complex Example (CPS)

A more complex example can be found in the IncQuery CPS example project: https://github.com/IncQueryLabs/incquery-examples-cps/wiki. The project itself aims at providing an example, how EMF IncQuery related technologies can be used in a model driven development workflow.

Naturally, this example contains numerous different transformation methods and other auxiliary steps. The VIATRA MWE2 integration library can be used to define the structure of these transformation steps. The example project at https://github.com/lunkpeter/incquery-examples-cps/tree/MWE/addons/org.eclipse.incquery.examples.cps.mwe2integration.example contains both batch- and fine-grained, event-driven M2M transformations. In this section, a VIATRA based event driven M2M transformation will be used.

MWE Workflow Components

  • Initializer component: This standard MWE 2 workflow component is responsible for the initialization of the transformation environment. It creates the model resources, generates the source model, instantiates the main IncQuery engine used by the transformation steps and creates the output project structure. It also adds the created resources to the workflow context.

Transformation Steps

The transformation step contains the following attributes:

    protected MWE2ControllableExecutor executor; //Executor that implements the IControllable interface. Responsible for controlling the ED transformation.
    protected AdvancedIncQueryEngine engine;     //Main IncQuery engine used by the transformation steps.
    protected CPS2DeploymentTransformationViatra transformation;   //The previously mentioned VIATRA based event-driven transformation.

It overrides the following methods:

    @Override
    public void initialize(IWorkflowContext ctx) {
        //store the context
        this.context = ctx;
        //get the input model from the Workflow context (it was added by the initializer)
        CPSToDeployment cps2dep = (CPSToDeployment) ctx.get("model");
        //get the main IncQuery engine from the Workflow context (it was added by the initializer)
        engine = (AdvancedIncQueryEngine) ctx.get("engine");
        //create the EVM Executor that controls the transformation
        executor = new MWE2ControllableExecutor(IncQueryEventRealm.create(engine));
        transformation = new CPS2DeploymentTransformationViatra();
        //Initialize the transformation using the controller Executor
        transformation.setExecutor(executor);
        transformation.initialize(cps2dep, engine);
        System.out.println("Initialized model-to-model transformation");
    }
 
    @Override
    public void execute() {
        //Process one incoming event
        processNextEvent();
        //Command the event-driven transformation to run
        executor.run();
        //Wait for the event-driven transformation
        while (!executor.isFinished()) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
 
        System.out.println("Model-to-model transformation executed");
        //Send events to all target channels
        sendEventToAllTargets();
    }
 
    @Override
    public void dispose() {
        //At the end of the workflow, stop the the Thread the transformation step is checking for new events and dispose the transformation itself
        isRunning = false;
        transformation.dispose();
        System.out.println("Disposed model-to-model transformation");
    }
  • Change Monitor Transformation Step: This step detects the changes in the intermediary deployment model, and sends the changes themselves to the M2T transformation step.

The transformation step contains the following attributes:

    protected AdvancedIncQueryEngine engine;     //Main IncQuery engine used by the transformation steps.
    protected DeploymentChangeMonitor monitor;   //A Change monitor class that collects all the changes in a specified deployment model, for a period of time.

It overrides the following methods:

    @Override
    public void initialize(IWorkflowContext ctx) {
        //store the context
        this.context = ctx;
        //get the main IncQuery engine from the Workflow context (it was added by the initializer)
        engine = (AdvancedIncQueryEngine) ctx.get("engine");
        //Get the deployment model root from the workflow context
        Deployment deployment = ((CPSToDeployment) ctx.get("model")).getDeployment();
        //Initialize monitor and start monitoring.
        monitor = new DeploymentChangeMonitor(deployment, engine);
        try {
            monitor.startMonitoring();
        } catch (IncQueryException e) {
            e.printStackTrace();
        }
    }
 
    @Override
    public void execute() {
        //Process one incoming event
        processNextEvent();
        //Create a new checkpoint that contains all the model changes since the previous checkpoint up to the current time.
        DeploymentChangeDelta delta = monitor.createCheckpoint();
        System.out.println("Checkpoint created");
        //Send the model changes to all target channels
        sendEventToAllTargets(delta);
    }
 
    @Override
    public void dispose() {
        //At the end of the workflow, stop the the Thread the transformation step is checking for new events
        isRunning = false;
        System.out.println("Disposed change monitor");
    }
  • M2T Transformation Step: The M2T transformation step uses a distributed code generator component that returns the output files as character sequences.

The transformation step contains the following attributes:

    protected AdvancedIncQueryEngine engine; 
    public ICPSGenerator generator;    //Code Generator component
    public String projectName;         //Name of the output project
    public String sourceFolder;        //Location of the output project
    public List<M2TOutputRecord> output;  //Output of the transformation
    public DeploymentChangeDelta delta;   //Incoming delta

It overrides the following methods:

    @Override
    public void initialize(IWorkflowContext ctx) {
        //store the context
        this.context = ctx;
        //get the main IncQuery engine from the Workflow context (it was added by the initializer)
        engine = (AdvancedIncQueryEngine) ctx.get("engine");
        //get the output project name from the Workflow context (it was added by the initializer)
        projectName = (String) ctx.get("projectname");
        //get the output project folder from the Workflow context (it was added by the initializer)
        sourceFolder = (String) ctx.get("folder");
        //Create new code generator
        generator = new CodeGenerator(projectName,engine,true);
    }
 
    @Override
    public void execute() {
        processNextEvent();
 
        ChangeM2TOutputProvider provider = new ChangeM2TOutputProvider(delta, generator, sourceFolder);
        output = provider.generateChanges();
 
        System.out.println("Model-to-text transformation executed");
        //Send output to serializer
        sendEventToAllTargets(output);
    }
 
    @Override
    public void dispose() {
        //At the end of the workflow, stop the the Thread in which the transformation step is checking for new events
        isRunning = false;
        System.out.println("Disposed model-to-text transformation");
    }
  • Serializer Transformation Step: Creates new source files based on the output provided by the M2T transformation.

The transformation step contains the following attributes:

    private boolean firstRun = true; //If the serializer is run for the first time, it sends events to the Model modification step, otherwise quits the workflow.
    public DefaultSerializer serializer; //CPS serializer
    public String sourceFolder;          //Location of the source folder
    public List<M2TOutputRecord> m2tOutput; //Output created by the M2T transformation

It overrides the following methods:

    @Override
    public void initialize(IWorkflowContext ctx) {
        this.context = ctx;
        //Init serializer
        System.out.println("Initialized serializer");
        serializer = new DefaultSerializer();
        sourceFolder = (String) ctx.get("folder");
 
    }
 
    @Override
    public void execute() {
        processNextEvent();
        //Serialize output
        ListBasedOutputProvider provider = new ListBasedOutputProvider(m2tOutput);
        serializer.serialize(sourceFolder, provider, new JavaIOBasedFileAccessor());
 
        System.out.println("Serialization completed");
        //If the serializer is run for the first time, it sends events to the Model modification step, otherwise quits the workflow.
        if(firstRun){
            getModifierChannel().createEvent();
            firstRun = false;
        } else {          
            getChainEndChannel().createEvent();
        }  
    }
 
    @Override
    public void dispose() {
        isRunning = false;
        System.out.println("Disposed serializer");
    }


  • Model Modifier Transformation Step: Modifies the source CPS model and sends events to the M2M transformation, meaning that the workflow has restarted.

The transformation step contains the following attributes:

    protected CPSToDeployment model; //Model root
    protected CPSModelBuilderUtil modelBuilder = new CPSModelBuilderUtil(); //CPS model modification utility class

It overrides the following methods:

    @Override
    public void initialize(IWorkflowContext ctx) {
        //receive the model root from the context
        model = (CPSToDeployment) ctx.get("model");  
    }
 
    @Override
    public void execute() {
        processNextEvent();
        //modify model
        modifyModel();
        System.out.println("Model modification executed");
        sendEventToAllTargets();
 
    }
 
    @Override
    public void dispose() {
        isRunning = false;
        System.out.println("Disposed model modifier");
    }

Events, Event Factories, Processors

  • ChangeDeltaEvent: Event type that contains the model modifications detected by a DeploymentChangeMonitor.
  • M2TOutputEvent: Event Type that contains character sequences created by the M2T transformation.
  • ChangeDeltaEventFactory: Event factory that creates a ChangeDeltaEvent.
  • M2TOutputEventFactory: Event factory that creates a M2TOutputEvent.
  • ChangeDeltaEventProcessor: Event processor that adds the ChangeDelta contained by a ChangeDeltaEvent to its parent Transformation Step.
  • M2TOutputEventProcessor: Event processor that adds the M2T output character sequences contained by a M2TOutputEvent to its parent Transformation Step.


Workflow Structure

The structure of the workflow is specified in the ControllableEventDrivenTransformation.mwe2 file. After the previously defined transformation steps and workflow components have been imported to the module, they can be easily instantiated using the MWE2 description language. Note, that the package containing the .mwe2 file should be imported as well.

module org.eclipse.viatra.emf.mwe2integration.transdemo
 
import org.eclipse.viatra.emf.mwe2integration.*
import org.eclipse.viatra.emf.mwe2integration.mwe2impl.*
import org.eclipse.incquery.examples.cps.integration.*
import org.eclipse.incquery.examples.cps.integration.events.*
import org.eclipse.incquery.examples.cps.integration.eventdriven.controllable.*
 
//Define Channels here
var chainStartChannel = MWE2Channel {}
var chainEndChannel = MWE2Channel {}
 
var M2MChannel = MWE2Channel {}
var ChangeMonitorChannel = MWE2Channel {}
var M2TChannel = MWE2Channel {}
var SerializerChannel = MWE2Channel {}
var ModifierChannel = MWE2Channel {}
 
Workflow {
        //Add initializer workflow component
	component = InitializerComponent{
                //Set the seed and model size for model generation
		seed = "11111"
		modelSize = "4"
                //Define location for model resource
		modelDir = "D:\\MWE_TEST\\model"
		modelName = "MWE_TEST"
                //Define output project location and name
		outputProjectLocation = "D:\\MWE_TEST"
		outputProjectName = "VIATRA_ED_Controllable"
	}
	//Use the MWE2TransformationChain class for defining transformation chains
	component = MWE2TransformationChain {
                //Define the start and end channels of the transformation chain
		startChannel = MWE2TargetChannel{channel = chainStartChannel}
		endChannel = MWE2ListeningChannel{channel = chainEndChannel}
 
                //Add an event driven viatra transformation step that listens to the chainStartChannel and the m2mChannel
                //It targets the change monitor
		transformationStep = M2MControllableEventDrivenViatraTransformationStep {
			chainStartChannel = MWE2ListeningChannel{channel = chainStartChannel}
			m2MChannel = MWE2ListeningChannel{channel = M2MChannel}
			changeMonitorChannel = MWE2TargetChannel{channel = ChangeMonitorChannel}
		}
 
                //Create a change monitor transformation step
                //Add a ChangeDeltaEventFactory to its target channel
		transformationStep = ChangeMonitorTransformationStep{
			changeMonitorChannel = MWE2ListeningChannel{channel = ChangeMonitorChannel}
			m2TChannel = MWE2TargetChannel{channel = M2TChannel factory = ChangeDeltaEventFactory{}}
		}
 
                //Create a M2t transformation step
                //Add a ChangeDeltaEventProcessor to its listening channel
                //Add a M2TOutputEventFactoryto its target channel
		transformationStep = M2TDistributedTransformationStep {
			m2TChannel = MWE2ListeningChannel{channel = M2TChannel processor = ChangeDeltaEventProcessor{}}
			serializerChannel = MWE2TargetChannel{channel = SerializerChannel factory = M2TOutputEventFactory{}}			
		}
 
                //Create a serializer transformation step
                //Add a M2TOutputEventProcessor to its listening channel
                //Define both chainEndChannel and ModifierChannel as target channels (the workflow will quit after two cycles)
		transformationStep = SerializerTransformationStep{
			serializerChannel = MWE2ListeningChannel{channel = SerializerChannel processor = M2TOutputEventProcessor{}}
			chainEndChannel = MWE2TargetChannel{channel = chainEndChannel}
			modifierChannel = MWE2TargetChannel{channel = ModifierChannel}
		}
 
                //Create a serializer transformation step
                //Define both m2MChannel as target channel
		transformationStep = ModelModifierStep{
			modifierChannel = MWE2ListeningChannel{channel = ModifierChannel}
			m2MChannel = MWE2TargetChannel{channel = M2MChannel}
		}
	}
 
}

Execution

To run the specified workflow, simply run the mwe2 file as an 'MWE2 Workflow'.

Project Locations

The library itself can be found at https://github.com/lunkpeter/org.eclipse.viatra/tree/MWE/plugins/org.eclipse.viatra.emf.mwe2integration. The test project at https://github.com/lunkpeter/org.eclipse.viatra/tree/MWE/tests/org.eclipse.viatra.emf.mwe2integration.test contains a simple example as well. Note that at its current state, the library itself is not an integrated part of the VIATRA framework, however as this inclusion is intended in the future, the library is in the org.eclipse.viatra namespace.

Back to the top