This guide describes the procedure for adding and implementing a new Activity in bexee. This will be necessary as new activities will be added to the BPEL standard or if an existing activity that has not been implemented in bexee should be implemented.
To describe this procedure, the "Switch" activity will be implemented.
In order to implement the "Switch" activity it will necessary to do the following steps:
ObjectCreationFactory
for the "Switch" activitiesBPELElementFactory
classBefore the definition of the activity interface it is necessary to understand the type of the activity as specified in the bpel4ws xml schema. The type for "Switch" is defined as follows:
<complexType name="tSwitch"> <complexContent> <extension base="bpws:tActivity"> <sequence> <element maxOccurs="unbounded" name="case"> <complexType> <complexContent> <extension base="bpws:tActivityContainer"> <attribute name="condition" type="bpws:tBoolean-expr" use="required"/> </extension> </complexContent> </complexType> </element> <element minOccurs="0" name="otherwise" type="bpws:tActivityContainer"/> </sequence> </extension> </complexContent> </complexType>
This excerpt from the BPEL xml schema means that "Switch" is an
activity type, i.e. it inherits from the tActivity
type.
It contains an unspecified number of Boolean-expr
attribute and a nested activity. After all
The interface for the "Switch" activity will be placed in the
bexee.model.activity
package and it will extend the
Activity
superclass. We want to be able to add
unlimited number of case elements and retrieve an ordered list of
those elements. Furthermore, it must be possible to set the otherwise activity.
public interface Switch extends Activity { public void addCase(BpelCase bpelCase); public List getCases(); public void setOtherwise(Activity activity); public Activity getOtherwise(); }
It becomes obvious that it's further necessary to define a
BpelCase
interface. This interface will be placed in the
bexee.model.elements
package.
public interface BpelCase { public void setBooleanExpression(BooleanExpression booleanExpression); public BooleanExpression getBooleanExpression(); public void setCaseActivity(Activity activity); public Activity getCaseActivity(); }
The used interface BooleanExpression
has already been
defined and a default implementation has been provided.
The implementation of the "Switch" activity will be put in the
bexee.model.activity
package and might be implemented
in the following way:
public class SwitchImpl extends AbstractActivity implements Switch { private List bpelCases; private Activity otherwiseActivity; public SwitchImpl(StandardAttributes standardAttributes) { super(standardAttributes); bpelCases = new ArrayList(); } public void addCase(BpelCase bpelCase) { bpelCases.add(bpelCase); } public List getCases() { return bpelCases; } public void activity(Activity activity) { setOtherwise(activity); } public void setOtherwise(Activity activity) { otherwiseActivity = activity; } public Activity getOtherwise() { return otherwiseActivity; } public void accept(ProcessController controller, ProcessInstance instance) throws Exception { controller.process(this, instance); } public void accept(BPELElementVisitor elementVisitor) { elementVisitor.visit(this); } }
A speciality is the method activity(Activity)
. It is
used by the Digester in order to add activities enclosed directly in this "Switch",
in this case an otherwise activity. We need to add this method because we don't know
the position of an activity in a BPEL document. After the creation of
an Activity, we need to add it to the activitiy one level further up.
Therefore we need to equip all elements and activities which might contain a
nested activity with a uniformly named method, therefore we need this
activity(Activity)
method. You also will find this method
in the implementation of the BpelCase
interface.
It is necessary to implement the
accept(ProcessController, ProcessInstance)
and the
accept(BPELElementVisitor)
methods exactly in this way in
order to enable processing the activity inside bexee (see document:
Enhancing the Controller).
Now we need to implement the BpelCase
interface:
public class BpelCaseImpl implements BpelCase { private BooleanExpression booleanExpression; private Activity activity; public BpelCaseImpl() { super(); } public void setBooleanExpression(BooleanExpression booleanExpression) { this.booleanExpression = booleanExpression; } public BooleanExpression getBooleanExpression() { return booleanExpression; } public void activity(Activity activity) { setCaseActivity(activity); } public void setCaseActivity(Activity activity) { this.activity = activity; } public Activity getCaseActivity() { return activity; } }
It will now be necessary to complete the xml-oo mapping configuration file "bpel-rules.xml". This configuration file is needed by the Digester xml-oo mapping tool from the Apache Jakarta Project [Digester] .
The configuration file has to be completed directly inside the root of the "bpel-rules.xml" document:
<digester-rules> ... ... <pattern value="*/switch"> <factory-create-rule classname="bexee.model.xmltobpel.SwitchImplFactory"/> <pattern value="case"> <factory-create-rule classname="bexee.model.xmltobpel.BpelCaseImplFactory"/> <set-next-rule methodname="addCase"/> </pattern> <set-next-rule methodname="activity"/> </pattern> ... ... </digester-rules>
In normal case, we don't let Digester create the activities but
delegate the creation to a specialized factory. This is done with the
<factory-create-rule/>. We specify the fully qualified name of the
factory to be used, i.e.
bexee.model.xmltobpel.SwitchImplFactory
. The
implementation of this factory will be shown later. This factory will
be responsible of creating SwitchImpl
activities.
When the nested pattern "*/case" will be found, Digester will
delegeate the creation of a BpelCaseImpl
object to the
correspondent factory. The newly created BpelCaseImpl
object will be added to the list off cases of the formerly created
SwitchImpl
object.
After the completion of the "switch" element the newly created
SwitchImpl
object will be added to the enclosing activity
with the already mentioned "activity(Activity)" method.
The task of the factory will be to look up the right instance of the
BPELElementFactory
, extract standard attributes from the
attributes passed to it and then delegate the creation of a
SwitchImpl
to the BPELElementFactory
.
public class SwitchImplFactory extends AbstractObjectCreationFactory { public Object createObject(Attributes attributes) throws Exception { return getElementFactory().createSwitch( getStandardAttributes(attributes)); } }
Similarily, it is neseccary to implement a factory for the creation
of BpelCaseImpl
objects:
public class BpelCaseImplFactory extends AbstractObjectCreationFactory { public Object createObject(Attributes attributes) throws Exception { String condition = attributes.getValue(CONDITION); return getElementFactory().createBpelCase(condition); } }
All standard elements inside the newly created
SwitchImpl
object will be set implicitly by Digester.
Digester will find the patterns corresponding to the standard
elements, e.g. "*/target", create the elements and then set them in
the enclosing activity:
<pattern value="*/target"> <object-create-rule classname="bexee.model.elements.impl.TargetImpl"/> <set-next-rule methodname="addTarget"/> </pattern>
The methods to set
the standard elements within activities are provided by
the AbstractActivity
Finally, we have to complete the BPELElementFactory class with the
method used by the SwitchImplFactory
to create the
SwitchImpl objects. The implementation of this method is
fairly straightforward:
public Object createSwitch(StandardAttributes standardAttributes) { Switch bpelSwitch = new SwitchImpl(standardAttributes); return bpelSwitch; }
The creation of a BpelCaseImpl
is also delegated to this
factory, therefore the second addition:
public BpelCase createBpelCase(String condition) { BpelCase bpelCase = new BpelCaseImpl(); bpelCase.setBooleanExpression(new BooleanExpressionImpl(condition)); return bpelCase; }