7.3 The model

 < Day Day Up > 



7.3 The model

In this section, we describe the model used by the sample application.

7.3.1 Modifying the WorkflowModel

In this section we describe the modifications made to the WorkflowModel in order to use it in the workflow editor sample application.

Choosing the naming convention for references

A reference between two classes has usually two names associated with it. One for each of the navigation paths between them.

The one-to-one association names are singular and are always easily chosen. For the one-to-many association names, we have an extra level of freedom, because we can choose the Modeling or the Java naming convention to give it a name. The main difference between the two is that Modeling uses singular while Java uses plural.

Java coding conventions are strong. By respecting them, code is generally more readable and understandable. It is not that those conventions are the only way or the best way to go, but when you follow them, code become more easily familiar to developers. Modeling uses different conventions, because the interests are not the same.

Knowing that the convention choice has some effect on the generated code, the result is that you rapidly end up with some sort of decision like, do we privilegize the modeling or Java standpoint? When implementing the sample application, with Java code as the only mapping, we decide to use the Java standpoint.

To help you visualize the potential implications of the choice of one view, we use the Workflow to WorkflowNode association, called node(s) from Workflow to WorkflowNode.

In Java, the association is implemented by a collection called nodes. See Example 7-7:

Example 7-7: Java reference implementation

start example
 package com.ibm.itso.sal330r.workflow.impl; public class WorkflowImpl extends WorkflowElementImpl implements Workflow {     /**      * The cached value of the '{@link #getNodes() <em>Nodes</em>}' containment reference list.      * <!-- begin-user-doc -->      * <!-- end-user-doc -->      * @see #getNodes()      * @generated      * @ordered      */     protected EList nodes = null;     /**      * <!-- begin-user-doc -->      * <!-- end-user-doc -->      * @generated      */     public EList getNodes() {    if (nodes == null) {        nodes = new EObjectContainmentWithInverseEList(              WorkflowNode.class,              this,              WorkflowPackage.WORKFLOW__NODES,              WorkflowPackage.WORKFLOW_NODE__WORKFLOW);    }    return nodes;     } } 
end example

In XML, if you look at a file containing the result of a workflow serialization, you will see an extra 's' at the end of each node entity, which is unusual for an XML entity. You can look at any Ecore file for more examples of eClassifiers or eReferences XML entities. See Example 7-8.

Example 7-8: Workflow XMI file serialization

start example
 <?xml version="1.0" encoding="ASCII"?> <workflow:Workflow xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:workflow="http://www.redbooks.ibm.com/sal330r/workflow" >   <nodes xsi:type="workflow:Task" x="99" y="80" >     <outputs xsi:type="workflow:FaultPort" name="fault" />     <outputs name="output" />     <inputs name="input" />   </nodes>   <nodes xsi:type="workflow:Choice" x="83" y="213" >     <outputs xsi:type="workflow:FaultPort" name="fault" />     <outputs xsi:type="workflow:ConditionalOutputPort" name="ConditionalPort0"  condition="false"/>     <inputs name="input" />   </nodes>   <nodes xsi:type="workflow:LoopTask" x="293" y="184" >     <outputs xsi:type="workflow:FaultPort" name="fault" />     <outputs name="output" />     <inputs name="input" />     <subworkflow />   </nodes> </workflow:Workflow> 
end example

7.3.2 Modifying the code generated from the model

This section describes additions and customizations made to the interfaces and implementations generated from the WorkflowModel, in order to use this generated code as the model for the workflow editor sample application.

7.3.3 Respecting model constraints in the editor

In this section we use the connectTo method in the Workflow class to show model object relationships, and explain the execute and undo method of the ConnectionCommand class. We describe the containment relationship between Workflow and Edge and the relationship between InputPort and OutputPort and Edge.

Enforcing model constraint in the model implementation

WorkflowNodes have Ports. The application requires Task, CompoundTask, and LoopTask tasks to have only one Input and one Output. Transformations can have multiple Inputs, and Conditionals can have multiple Outputs. All nodes have a default FaultPort.

The model, as designed, tell us that the association between tasks and ports are inherited from the WorkflowNode, where two one-to-many associations are defined between WorkflowNode and InputPort on one side and WorkflowNode and OutputPort on the other side. The reference named outputs contains all the OutputPort, all the ConditionalOutputPort, and the default FaultPort ports.

With those elements in mind, we can see that we have a problem to reduce the visibility of the inherited methods for outputs and inputs relationships. The model provides methods dealing with a collection, where methods dealing only with one object should be defined.

Nothing prevents the following code to be written in the case of a CoumpoundTask:

    this.getInputs().addAll(collection); 

Several solutions have been investigated and evaluated. Here is a short description of the most important ones:

  1. A model redesign to move inputs and outputs references to the subclasses, Task, Transformation, and Choice, would have solved the problem elegantly, but at a price of three times the number of references. Task would have three one-to-one references for the InputPort, the OutputPort, and the FaultPort port. Transformation would have a one-to-many reference for the InputPort, a one-to-one reference for the OutputPort, and a one-to-one for the FaultPort port. Choice would have a one-to-one reference for the InputPort, a one-to-many reference for the OutputPort ports, and a one-to-one for the FaultPort port. The main problem with this approach is that the WorkflowNode loses its knowledge of Ports, so there is no easy way to loop on all the ports, or to connect an OutputPort, or a FaultPort port to an InputPort with an Edge.

  2. The Java way of manually implementing the model would have required the inputs and outputs associations to be left at the same place and to be private. The corresponding accessor methods handling the many cardinalities of the reference would be private or protected. All subclasses would have to redefine the methods accessing the collection in order to enforce the constraints. Unfortunately, this solution cannot be implemented easily in EMF, because the serialization process requires the reference to be publicly accessible. There is no way to have a private reference in EMF.

  3. The existence of a constraint language, integrated with the code generation tools taking could have been a good solution. We could have kept the associations at the WorkflowNode level and be able to express the constraints.

  4. The solution we implemented has the following goals:

    1. To keep the model as designed in order to minimize the number of association and to benefit of the polymorphism for the ports

    2. To not to use the default methods generated, including the one giving direct access to the underlying collection

    3. To use the method we provided to support and enforce the application constraints

    Figure 7-2 shows the resulting WorkFlowNode hierarchy.

    click to expand
    Figure 7-2: WorkflowNode hierarchy

Note 

At the moment, the design can be split into three simple implementation lanes. The first is one input and one output; the second is with many inputs and one output; and the third is the one input and many outputs. Once we have more than one class in a lane, basically two classes with no direct inheritance in between, it would be nice to create an abstract intermediate class in order to provide one-one, many-one, or one-many behavior.

The connectTo method

The algorithm to connect an OutputPort port to an InputPort port with an Edge consists of these steps:

  1. Checking if the link does not already exist.

  2. Adding the Edge to the Workflow in order to have the object created in the Workflow entity context, because of the containment reference between them.

  3. Linking the OutputPort to the Edge.

  4. Linking the InputPort to the Edge.

The Java code for the connectTo method is found in the WorkflowImpl class as shown in Example 7-9:

Example 7-9: TaskImpl connectTo method

start example
 /** * Connects the output port to the given input port. * From an edge standpoint, the source is an output port * and the target an input port. * * @param outputPort * @param inputPort * @param res */ public Edge connectTo(OutputPort outputPort, InputPort inputPort) {    // Check to see if input and output are not already    // connected by an edge.    Edge edge = outputPort.findEdgeTo(inputPort);    if (edge == null) {       // No connection found       WorkflowFactory workflowFactory = WorkflowModelManager.getFactory();       // Create an edge       edge = workflowFactory.createEdge();       // Add the edge to the workflow, to benefit       // of the containment link between workflow and edge       this.getEdges().add(edge);       // Link input and output to the edge       inputPort.getEdges().add(edge);       outputPort.getEdges().add(edge);    }    return edge; } 
end example

The EMF eOpposite attribute of the eReferences entity is very helpful when making a connection between two ports with an edge, because for all the references with an eOpposite attribute, EMF keeps track of the changes on the other side of the reference. This means, for example, that if you add an Edge to an OutputPort:

    outputPort.getEdges().add(edge); 

Then EMF will do the opposite setup automatically and transparently for you:

    edge.setSource(outputPort); 

When creating an association in the EMF Class Diagram in the UML plug-in.

The Navigable checkbox (see Figure 1-12 on page 20) drives the access to the association features. Once an association is navigable on both ends, a change on one side is reflected on the other side, because the eReferences' eOpposite attributes are used.



 < Day Day Up > 



Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Eclipse Development Using the Graphical Editing Framework And the Eclipse Modeling Framework
ISBN: 0738453161
EAN: 2147483647
Year: 2004
Pages: 70
Authors: IBM Redbooks

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net