Currently it not feasible in MDSD to generate 100% of an application. Usually, you generate some kind of implementation skeleton into which developers integrate their own manually written code. For example, the generator generates an abstract base class, from which developers extend their implementation classes – which contains the application logic.
The screenshot above shows an example – and also illustrates the use of the Recipe Plugin. Let’s look at the various items in the screenshot.
.recipes file. How this file is created will be
shown below. The recipe file contains the checks that need to be executed on the
code base. In the context menu of a .recipes file you’ll find an item called Open
Recipe File that opens the checks in the respective browser. Note that the file has
to have a .recipes extension – otherwise the plugin will not open the file!
CalculatorImplementation – this is why the check that verifies its presence is
successful. There is no C1Implementation why the check that checks its presence
fails.
src-gen). It contains the generated base classes.
There are a couple of options to work with those recipes, as shown in the next illustration.
If you right-click on a check in the treeview, you can reevaluate the check explicity. The buttons at the top right of the view has three buttons: the first on collapses the tree view. The “play button with the small red cross” re-evaluates all the failed checks – and only those! The third button is a filter button; if selected, the tree view hides the checks that are ok. In the view’s drop down menu (activated with the small downward-pointing triangle) there are two entries: the green run-button labelled “reevaluate all” reevaluates all checks, even those that have been successful before. And finally, the reload button reloads the recipe file, and reevaluates everything.
There are two important automation steps:
There are two steps. The first one installs the plugin itself, i.e. the Recipe Browser View,
etc. The respective plugin is org.openarchitectureware.recipe. As usual you install it by
copying it into the Eclipse plugin directory or just downloading it from the oAW update
site. If the plugin is installed in this way, it can only evaluate the (relatively trivial) checks
in the recipe.simpleChecks project. To check useful things, you’ll have to extend the plugin
– you have to contribute the checks that should be executed. For this purpose, the
org.openarchitectureware.recipe plugin provides the check extension point. A number of
useful checks that can be evaluated in Eclipse are contained in the
org.openarchitectureware.recipe.eclipseChecks plugin.
You should install that one, too. It
also comes automatically from the update site.
In general, this means: whenever you develop your own checks and want to evaluate them in Eclipse, you have to contain them in a plugin and extend the above-mentioned extension point. Otherwise it will not work.
In order for the workflow to find the recipe JARs, they need to be on the classpath. The easiest way to achieve that is to make your generator project a plugin project and reference the recipe plugins in the plugin dependencies (all the oAW plugins with recipe in their name). This will update your classpath and add the necessary JARs. If, for some reason, you don't want your projects to be plugins, you have to reference the JAR files of the above mentioned plugins manually.
You have to write a workflow component that creates
the recipes. Your custom workflow component has to extend the RecipeCreationComponent
base class and overwrite the createRecipes() operation. The operation has to return the
collection of recipes that should stored into the recipe file. In the workflow file, you then
have to configure this component with the name of the application project in Eclipse, the
souce path where the manually write code can be found and the name of the recipe file to
be written.
Please take a look a the emfHelloWorld example. It contains an extensive example of how to use the recipes in oAW 4.
You can also check the recipes using ant. Of course you cannot use those nice and cool interactive checks – and you also can’t use Eclipse-based checks. They can be in the recipes file, since the ant tasks skips them automatically. The following piece of ant code shows how to run the checks – should be self-explanatory. Note that you have to use all the jar files from the recipe.ant project.
<?xml version="1.0" encoding="ISO-8859-1" ?>
<project name="scm - hello world - generate" default="generate">
<property file="build.properties" />
<path id="ant.runtime.classpath">
<pathelement location="${GENROOT}" />
<fileset dir="${GENROOT}" includes="*.jar"/>
<fileset dir="${OAWROOT}/dist" includes="*.jar"/>
<fileset dir="${RECIPE.CORE.DIR}/dist" includes="*.jar"/>
<fileset dir="${RECIPE.SIMPLECHECKS.DIR}/dist" includes="*.jar"/>
<fileset dir="${RECIPE.ANT.DIR}/dist" includes="*.jar"/>
<fileset dir="${RECIPE.ANT.DIR}/lib" includes="*.jar"/>
<fileset dir="${RECIPE.ECLIPSECHECKS.DIR}" includes="*.jar"/>
</path>
<target name="check" depends="">
<taskdef name="check"
classname="org.openarchitectureware.recipe.ant.RecipeCheckTask">
<classpath refid="ant.runtime.classpath" />
</taskdef>
<check recipeFile="L:/workspace/xy/helloWorld.recipes"/>
</target>
</project>The checks use log4j logging to output the messages. So you can set the log level using the log4j.properties file. The following output shows all the checks being successful:
Buildfile: l:\exampleWorkspace-v4\scmHelloWorld\build.xml
check:
[check] 0 INFO - checking recipes from file:
L:/runtime-EclipseApplication/xy/helloWorld.recipes
BUILD SUCCESSFUL
Total time: 1 secondIf you set the log level to DEBUG, there’s more; you can see that all the Eclipse checks are skipped.
Buildfile: l:\exampleWorkspace-v4\scmHelloWorld\build.xml
check:
[check] 0 INFO - checking recipes from file: L:/runtime-
EclipseApplication/xy/helloWorld.recipes
[check] 60 DEBUG - [skipped] resource exists exists –
skipped - mode was batch only.
[check] 70 DEBUG - [skipped] resource exists exists –
skipped - mode was batch only.
[check] 70 DEBUG - [skipped] resource exists exists –
skipped - mode was batch only.
[check] 80 DEBUG - [skipped] resource exists exists –
skipped - mode was batch only.
[check] 80 DEBUG - [skipped] resource exists exists --
skipped - mode was batch only.
[check] 90 DEBUG - [skipped] resource exists exists –
skipped - mode was batch only.
[check] 90 DEBUG - [skipped] resource exists exists –
skipped - mode was batch only.
[check] 90 DEBUG - [skipped] resource exists exists –
skipped - mode was batch only.
BUILD SUCCESSFUL
Total time: 1 secondIf there are errors, they will be output as a ERROR level message.
The following piece of code is the simplest check you could possibly write:
package org.openarchitectureware.recipe.checks.test;
import org.openarchitectureware.recipe.core.AtomicCheck;
import org.openarchitectureware.recipe.eval.EvaluationContext;
public class HelloWorldCheck extends AtomicCheck {
private static final long serialVersionUID = 1L;
public HelloWorldCheck() {
super( "hello world", "this check always succeeds" );
}
public void evaluate(EvaluationContext c) {
ok();
}
}A couple of notes:
setLongDescription() if you want to set the explanatory text.
EvalTrigger. By default, the EvalTrigger.ON_REQUEST is used which
means that the check is only evaluated upon explicit request. If you pass
EvalTrigger.ON_CHANGE, the check will be automatically re-evaulated if the Eclipse
workspace changes.
evaluate() method you do the check itself. We will explain more on that later.
More sensible checks distinguish themselves in two respects:
evaluate() operation.
setParameter( name, value )
operation for that. More on that below.
An example:
public void evaluate(EvaluationContext c) {
if ( something is not right ) {
fail( "something has gone wrong");
}
if ( some condition is met ) {
ok();
}
By the way, you don’t need to care about the EvaluationContext. Its only needed by the framework.
Eclipse checks are a bit special. If the check were implemented in the way described above, you’d have a lot of dependencies to all the Eclipse plugins/jars. You’d have these dependencies as soon as you’d instantiate the check – i.e. also in the generator when you configure the check. In order to avoid this, we have to decouple the configuration of a check in the generator and its evaluation later in Eclipse:
An Eclipse check is thus implemented in the following way. First of all, our check has to
extend the EclipseCheck base class.
public class ResourceExistenceCheck extends EclipseCheck {Again, we add a serial version uid to make sure deserialization will work.
private static final long serialVersionUID = 2L;
In the constructor we decide whether we want to have this check evaluated whenever the
Eclipse workspace changes (EvalTrigger.ON_CHANGE) or not. We also store some of the
parameters in the parameter facility of the framework. Note that we do no implement the
evaluate() operation!
public ResourceExistenceCheck( String message,
String projectName, String resourceName ) {
super( "resource exists exists", message, EvalTrigger.ON_CHANGE );
setProjectName( projectName );
setResourceName( resourceName );
}
private void setProjectName(String projectName) {
setParameter( "projectName", projectName );
}
private void setResourceName(String resourceName) {
setParameter( "resourceName", resourceName );
}
In order to provide the evaluation functionality, you have to implement an
ICheckEvaluator. It has to have the same qualified name as the Check itself, postfixed with
Evaluator. During the evaluation of the check, the class is loaded dynamically based on its
name. A wrong name will result in a runtime error during evaluation the Eclipse Plugin.
public class ResourceExistenceCheckEvaluator
implements ICheckEvaluator {
public void evaluate( AtomicCheck check ) {
String projectName =
check.getParameter("projectName").getValue().toString();
String resourceName =
check.getParameter("resourceName").getValue().toString();
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IResource project =
workspace.getRoot().getProject(projectName);
if ( project == null )
check.fail("project not found: "+projectName);
IFile f = workspace.getRoot().getFile(
new Path(projectName+"/"+resourceName) );
String n = f.getLocation().toOSString();
if ( !f.exists() ) check.fail(
"resource not found: "+projectName+"/"+resourceName);
check.ok();}
When implementing the evaluator, you can basically do the same things as in the
evaluate() operation in normal checks. However, in order to set the
ok() or fail(“why”)
flags, you have to call the respective operations on the check passed as the parameter to the
operation.
In order to allow the Eclipse runtime to execute your checks, it has to find the respective classes when deserializing the recipe file. This is a bit tricky in Eclipse, since each plugin has its own classloader. So assume you want to define your own checks and want to use them in Eclipse; what you have to do is: implement your own plugin that extends a given extension point in the Recipe Browser plugin. The following XML is the plugin descriptor of the org.openarchitectureware.recipe.eclipseChecks.plugin.EclipseChecksPlugin, a sample plugin that comes with the recipe framework and provides a number of Eclipse-capable checks.
<?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.0"?> <plugin id="org.openarchitectureware.recipe.eclipseChecks" name="%plugin_name" version="4.0.0" provider-name="%provider_name" class="org.openarchitectureware.recipe.\ eclipseChecks.plugin.EclipseChecksPlugin">
Here we now define the jar file that contains our checks (will be important below!)
<runtime>
<library name="oaw-recipe-eclipsechecks.jar">
<export name="*"/>
</library>
</runtime>
The required plugins mainly depend on the implementations of the CheckEvaluators, of course, however, you have to make sure the dependencies contains the org.openarchitectureware.recipe plugin, since you’re going to extend an extension point defined therein.
<requires>
<import plugin="org.eclipse.ui"/>
<import plugin="org.eclipse.core.runtime"/>
<import plugin="org.eclipse.core.resources"/>
<import plugin="org.openarchitectureware.recipe"/>
<import plugin="org.eclipse.jdt.core"/>
</requires>
This is the important line: here you specify that you extend the check extension point of the Recipe Browser Plugin. If you don’t do this, deserialization of the recipe file will fail and you’ll get nasty errors. And yes, you need the dummy element; otherwise the class loading "magic" will not work.
<extension point="org.openarchitectureware.recipe.check”>
<dummy/>
</extension>
</plugin>
When you need to use the checks outside of Eclipse (e.g. in the generator for configuration/serialization purposes) you just add the plugin’s jar to your generator classpath. You don’t need all the Eclipse plugin’s jars referenced in the requires section, since these things will only be used during evaluation!
| Component | Plugin | Depends on | Description |
|---|---|---|---|
| recipe.core | Yes | - | Framework core. Needed whenever you do anything with recipes |
| recipe.ant | Yes | recipe.core | Contains the ant task to check recipes. Needed only for recipe evaluation in ant |
| recipe.simpleChecks | Yes | recipe.core | Contains a number of (more or less useful) sample checks |
| recipe.plugin | Yes | recipe.core | Contains the Eclipse Recipe Browser view |
| recipe.eclipsechecks.plugin | Yes | recipe.core | Contains the pre-packaged Eclipse checks. |
This table contains a list of all currently available checks. We are working on additional ones. Contributions are always welcome! This list might thus not always be up to date – just take a look at the code to find out more.
| Type | Classname | Purpose |
|---|---|---|
| Batch | org.openarchitectureware.recipe.checks. file.FileExistenceCheck | Checks whether a given file exists |
| Batch | org.openarchitectureware.recipe.checks. file.FileContentsCheck | Checks whether a given substring can be found in a certain file |
| Eclipse | org.openarchitectureware.recipe. eclipseChecks.checks. JavaClassExistenceCheck | Checks whether a given Java class exists |
| Eclipse | org.openarchitectureware.recipe. eclipseChecks.checks. JavaSupertypeCheck | Checks whether a given Java class extends a certain superclass |
| Eclipse | org.openarchitectureware.recipe. eclipseChecks.checks. ResourceExistenceCheck | Checks whether a given Eclipse Workspace Resource exists |