Skip to main content
Jump to: navigation, search

Difference between revisions of "MWE The Big Picture"

(Local Variables)
m (Local Variables: Formatting)
 
(18 intermediate revisions by 3 users not shown)
Line 13: Line 13:
  
 
== Configuration language (codename DI-Language -> DI=Dependency Injection)==
 
== Configuration language (codename DI-Language -> DI=Dependency Injection)==
 +
'''The language described here is already implemented and checked into EMF/MWE CVS. Note that it is based on [Xtext] 2, which is currently (July 08) under development.'''
 +
 +
 
The description of a generator workflow is made of a sequence of declared concrete components (e.g. EMFResourceLoaderComponent, EValidationComponent, JetComponent, XpandComponent, etc.) where each is configured.
 
The description of a generator workflow is made of a sequence of declared concrete components (e.g. EMFResourceLoaderComponent, EValidationComponent, JetComponent, XpandComponent, etc.) where each is configured.
  
For instance an EMFResourceLoaderComponent would need the URIs to the resources to be loaded and optionally respective ResourceFactory configuration, because workflows are run in standalone mode most of the time. XpandComponent needs to know what kind of TypeAdapter to use, where to generate the different files, how to postprocess them, etc.
+
For instance an EMFResourceLoaderComponent would need the URIs to the resources to be loaded and optionally respective ResourceFactory configuration, because workflows are run in standalone mode most of the time, i.e. without access to an Eclipse environment. XpandComponent needs to know what kind of TypeAdapter to use, where to generate the different files, how to postprocess them, etc.
  
 
Essentially such a description is sequence of components where each component, can be configured with arbitrary complex components itself.
 
Essentially such a description is sequence of components where each component, can be configured with arbitrary complex components itself.
Line 49: Line 52:
 
==== Type adapter ====
 
==== Type adapter ====
 
It is not only possible to instantiate Java classes via default constructor, but provide arbritrary implementations of TypeSystem.
 
It is not only possible to instantiate Java classes via default constructor, but provide arbritrary implementations of TypeSystem.
The di-language is shipped with implemenations for EMF (using EMF's refection layer) and for Java (using default constructor and adder and setter methods).
+
The di-language is shipped with implemenations for EMF (using EMF's reflection layer) and for Java (using default constructor and adder and setter methods).
  
 
This hook will also be used to improve convenience for repeated tasks. For instance, it is possible to use it like a factory and have preconfigured complex object trees being injected. Another useful case is to provide a built-in TypeSystem for a specific runtime model (see next section) in order to let users write more intentional looking configurations like e.g.:
 
This hook will also be used to improve convenience for repeated tasks. For instance, it is possible to use it like a factory and have preconfigured complex object trees being injected. Another useful case is to provide a built-in TypeSystem for a specific runtime model (see next section) in order to let users write more intentional looking configurations like e.g.:
Line 69: Line 72:
 
==== Local Variables ====
 
==== Local Variables ====
 
Local Variable can be accessed in two ways
 
Local Variable can be accessed in two ways
* via name on the right hand side of an assignment
+
* via name on the right hand side of an assignment
 
   foo = localVarName
 
   foo = localVarName
* via &{name} within a string literal.
+
* via ${name} within a string literal.
 
   foo = "foo${bar}"
 
   foo = "foo${bar}"
  
Example:
+
Another example :
 
   my.Object {
 
   my.Object {
     name= "${theName}"
+
     name = "${theName}"
 
     otherProp = aLocalVar
 
     otherProp = aLocalVar
 
   }
 
   }
Line 91: Line 94:
  
 
===== Complex Values can be stored as variable =====
 
===== Complex Values can be stored as variable =====
 +
 +
When declaring a complex value one can assign it to a localVar by adding ''':varname''' in front of the opening curly bracket.
  
 
The following code would inject a reference to the created object in it's property 'self'.
 
The following code would inject a reference to the created object in it's property 'self'.
Line 116: Line 121:
  
 
==== Type inference ====
 
==== Type inference ====
If the dependency's type is a conrete type and you want to inject an instance of that type, you don't have to epxlicitely state the type's name, because it can be inferred from the dependency.
+
If the dependency's type is a concrete type and you want to inject an instance of that type, you don't have to explicitely state the type's name, because it can be inferred from the dependency.
 
Example:
 
Example:
  
 
If we have the following two classes
 
If we have the following two classes
  
class Foo {
+
class Foo {
 
   setBar(Bar) ...
 
   setBar(Bar) ...
}
+
}
 
+
class Bar {
class Bar {
+
 
   setName(String) ...
 
   setName(String) ...
}
+
}
  
 
The configuration
 
The configuration
 
   
 
   
Foo {
+
Foo {
 
   bar = Bar {name = "foo"}
 
   bar = Bar {name = "foo"}
}
+
}
  
can be abbreviated like so:
+
can be abbreviated like this:
  
 
+
Foo {
Foo {
+
 
   bar = {name = "foo"}
 
   bar = {name = "foo"}
}
+
}
 
+
 
+
  
 
== Runtime Workflow Model ==
 
== Runtime Workflow Model ==
For a wokflow in order to being executable, we need some kind of interface to the different components.
+
As you can see one can instantiate any kind of Object graph using the di-langauge.
Such an interface defines a main 'execute' method and optionally additional life cycle callback methods (like preconfigure, postconfigure, preExecute,etc.)
+
For a workflow in order to being executable, we need some kind of runtime model invoking the different components and manging the overall execution. Therefore each component needs to conform to an interface so the runtime workflow model can work on.
In the past we used an interface called WorkflowComponent, having two methods : checkConfiguration(), invoke(). Also there is a workflowcontext (essentially a hashmap) being passed into the invoke method, so the different components can communicate some how. One component puts the model into a slot another reads it from there, transforms it and writes the oiutcome to another slot. And so on...
+
Such an interface usually defines some kind of 'execute' method and optionally additional life cycle callback methods (like preconfigure, postconfigure, preExecute,etc.)
 
+
In the past we used an interface called WorkflowComponent, having two methods : checkConfiguration(), invoke(). Also there is a workflowcontext (essentially a hashmap) being passed into the invoke method, so the different components can communicate some how. One component puts the model into a slot another reads it from there, transforms it and writes the outcome to another slot. Pretty simple but worked ...
 
We also introduced a so called CompositeComponent, which is just a composite node being able to hold a sequence of WorkflowComponent.
 
We also introduced a so called CompositeComponent, which is just a composite node being able to hold a sequence of WorkflowComponent.
 +
In addition there is a basic If-then-else construct, used to conditionally execute components.
 +
 +
In Bug 225892 Brian Hunt posted an EMF based runtime workflow model.
 +
He also listed some interesting requirements:
  
The runtime workflow model could provide additional things like
+
* Lightweight
* A way to execute multiple components in parallel
+
* Easily persisted
* A way to conditionally execute components
+
* Transportable to a distributed computing environment for execution
 +
* Capable of execution of where it left off in the event of a component failure
 +
** Successful components must not be re-executed
 +
* Capable of extending the functionality of parameters
 +
** Simple key = value pairs are not sufficient
 +
* Capable of executing components in parallel
 +
* Capable of executing components conditionally
 +
* Capable of extending the possible execution result states
 +
* Capable of providing a rich editor for both the workflow model and parameter
 +
values
 +
** I think GMF might fit nicely for a workflow editor
 +
* Capable of specifying parameter values separate from the workflow model
 +
* Capable of providing different algorithms for determining the final execution
 +
state of a composite

Latest revision as of 16:36, 15 July 2008

The purpose of MWE is to help orchestrate and configure code generation 'workflows'. Just like with compilers a code generator can and should be splitted into several single parts, each part doing a specific job. Such parts could be:

  • parsing / loading model
  • validating model
  • linking model
  • transforming model
  • modifying model
  • generating code out of a model

Typically there are a couple of component fulfilling such jobs included in a generator (compiler-chain), where each needs additional component-specific configuration. Also there are different solutions for each job. For instance modeltransformation can be done using QVT, ATL or Xtend, each need different configuration.

Configuration language (codename DI-Language -> DI=Dependency Injection)

The language described here is already implemented and checked into EMF/MWE CVS. Note that it is based on [Xtext] 2, which is currently (July 08) under development.


The description of a generator workflow is made of a sequence of declared concrete components (e.g. EMFResourceLoaderComponent, EValidationComponent, JetComponent, XpandComponent, etc.) where each is configured.

For instance an EMFResourceLoaderComponent would need the URIs to the resources to be loaded and optionally respective ResourceFactory configuration, because workflows are run in standalone mode most of the time, i.e. without access to an Eclipse environment. XpandComponent needs to know what kind of TypeAdapter to use, where to generate the different files, how to postprocess them, etc.

Essentially such a description is sequence of components where each component, can be configured with arbitrary complex components itself. Example:

EmfResourceLoaderComponent {
  platformRoot = ".."
  uri = "platform:/resource/foo/bar/model.dsl"
  extensionToFactoryMap += { 
     ext="dsl" 
     factory= MySpecialDSLResourceFactory {
        someAdditionalConfig = "foobar"
     }
  } 
}

Previously this has been configured via XML :

<component class="EmfResourceLoaderComponent"
   platformRoot = ".."
   uri = "platform:/resource/foo/bar/model.dsl">
  <extensionToFactoryMap ext="dsl">
      <factory class="MySpecialDSLResourceFactory" 
               someAdditionalConfig="foo"/>
  </extensionToFactoryMap>
</component>

No matter what syntax being used, the core concepts remain. This part (the language) don't need to know anything about what to instantiate. The only interface to the configuration is Java reflection.

Features of the DI language

Type adapter

It is not only possible to instantiate Java classes via default constructor, but provide arbritrary implementations of TypeSystem. The di-language is shipped with implemenations for EMF (using EMF's reflection layer) and for Java (using default constructor and adder and setter methods).

This hook will also be used to improve convenience for repeated tasks. For instance, it is possible to use it like a factory and have preconfigured complex object trees being injected. Another useful case is to provide a built-in TypeSystem for a specific runtime model (see next section) in order to let users write more intentional looking configurations like e.g.:

workflow {
   parallel {
       generator {
          invoke = "foo::bar()"
       }
       
       generator {
          invoke = "foo::bar2()"
       }
   }
} 

Where workflow, parallel and generator are built-in names known by a configured type adapter.

Local Variables

Local Variable can be accessed in two ways

  • via name on the right hand side of an assignment
 foo = localVarName
  • via ${name} within a string literal.
 foo = "foo${bar}"

Another example :

 my.Object {
    name = "${theName}"
    otherProp = aLocalVar
 }
Variables can be passed in as parameters

When instantiating a workflow description, a map of parameters can be passed in. The values can be any kind of Java object.

Variables can be declared

Variable declarations (and usage) look like:

 var x = "foo"
 var y = my.Object { name = "${x}bar" }
Complex Values can be stored as variable

When declaring a complex value one can assign it to a localVar by adding :varname in front of the opening curly bracket.

The following code would inject a reference to the created object in it's property 'self'.

 my.Object :myVar {
   self = myVar
 }

Invocation of other workflows

The root element of another workflow can be injected via:

 my.Object {
    coolStuff = file "scheme:/foo/workflow.mwe" {}
 }

Parameters can be passed like this:

 my.Object {
    coolStuff = file "scheme:/foo/workflow.mwe" {
        generateTo = "${project}/src-gen"
        packageRegistry = myPackageRegistry
    }
 }


Type inference

If the dependency's type is a concrete type and you want to inject an instance of that type, you don't have to explicitely state the type's name, because it can be inferred from the dependency. Example:

If we have the following two classes

class Foo {
 setBar(Bar) ...
}
class Bar {
 setName(String) ...
}

The configuration

Foo {
 bar = Bar {name = "foo"}
}

can be abbreviated like this:

Foo {
 bar = {name = "foo"}
}

Runtime Workflow Model

As you can see one can instantiate any kind of Object graph using the di-langauge. For a workflow in order to being executable, we need some kind of runtime model invoking the different components and manging the overall execution. Therefore each component needs to conform to an interface so the runtime workflow model can work on. Such an interface usually defines some kind of 'execute' method and optionally additional life cycle callback methods (like preconfigure, postconfigure, preExecute,etc.) In the past we used an interface called WorkflowComponent, having two methods : checkConfiguration(), invoke(). Also there is a workflowcontext (essentially a hashmap) being passed into the invoke method, so the different components can communicate some how. One component puts the model into a slot another reads it from there, transforms it and writes the outcome to another slot. Pretty simple but worked ... We also introduced a so called CompositeComponent, which is just a composite node being able to hold a sequence of WorkflowComponent. In addition there is a basic If-then-else construct, used to conditionally execute components.

In Bug 225892 Brian Hunt posted an EMF based runtime workflow model. He also listed some interesting requirements:

  • Lightweight
  • Easily persisted
  • Transportable to a distributed computing environment for execution
  • Capable of execution of where it left off in the event of a component failure
    • Successful components must not be re-executed
  • Capable of extending the functionality of parameters
    • Simple key = value pairs are not sufficient
  • Capable of executing components in parallel
  • Capable of executing components conditionally
  • Capable of extending the possible execution result states
  • Capable of providing a rich editor for both the workflow model and parameter

values

    • I think GMF might fit nicely for a workflow editor
  • Capable of specifying parameter values separate from the workflow model
  • Capable of providing different algorithms for determining the final execution

state of a composite

Back to the top