We'll start with custom actions to give you an understanding of the structure of an action class. There are three different approaches you can take to have your code function as a custom action:
The first approach is to create lifecycle actions.
These are used by many of the OOTB JBoss ESB actions. These actions implement the org.jboss.soa.esb.actions.ActionLifecycle
interface, or its sub-interface org.jboss.soa.esb.actions.ActionPipelineProcessor
.
You can see the list of these actions here:
http://docs.jboss.org/jbossesb/docs/4.10/javadoc/esb/org/jboss/soa/esb/actions/ActionLifecycle.html
The actions are listed in the javadoc as the classes implementing the interface. These actions implement the life-cycle model for an action through these methods:
initialize
destroy
process
(ActionPipelineProcessor
only)processSuccess
(ActionPipelineProcessor
only)processException
(ActionPipelineProcessor
only)In this context, "lifecycle" refers to the lifecycle of a stateless action pipeline.
The initialize
and
destroy
methods can be overridden to enable you to create resources that will be used throughout the execution of the action pipeline.
Any methods that you implement in your custom actions beyond these methods are located and executed by Java reflection.
To make things easier, abstract base classes (org.jboss.soa.esb.actions.AbstractActionPipelineProcessor
and org.jboss.soa.esb.actions.AbstractActionLifecycle
) that implement these interfaces are included with JBoss ESB. You can simply extend either of these abstract classes in your custom actions. These classes include sub methods for everything except the process
methods.
The lifecycle actions have to include a constructor which uses an org.jboss.soa.esb.helpers.ConfigTree
instance as a parameter. The ConfigTree
refers to the configuration of the action.
Now let's look at some examples of lifecycle actions.
The simplest lifecycle action that you're likely to ever see is included in the "helloworld" quickstart. The lifecycle action is referenced in the jboss-esb.xml
file as shown:
<action name="action1" class="org.jboss.soa.esb.samples.quickstart.helloworld.MyJMSListenerAction" process="displayMessage" />
What are the properties defined for this action? They are as follows:
name
: A unique (again, unique within the given service) name for the actionclass
: The full class name for the custom action. We'll look at the source code for this action in a moment.process
: Remember how we talked about overriding the process
method? This is an example.Here's the source for the action (the file is under the quickstart helloworld
directory: src/org/jboss/soa/esb/samples/quickstart/helloworld/MyJMSListenerAction.java
):
package org.jboss.soa.esb.samples.quickstart.helloworld; import org.jboss.soa.esb.actions.AbstractActionLifecycle; import org.jboss.soa.esb.helpers.ConfigTree; import org.jboss.soa.esb.message.Message; public class MyJMSListenerAction extends AbstractActionLifecycle { protected ConfigTree _config; public MyJMSListenerAction(ConfigTree config) { _config = config; } public Message displayMessage(Message message) throws Exception{ System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"); System.out.println("Body: " + message.getBody().get()) ; System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"); return message; } }
Let's take a closer look at this code:
import
statements bring in the AbstractActionLifecycle
and ESB's message. The ConfigTree
is needed by ESB to access the action's set of attributes, parse its XML configuration, and so on. The action's constructor must initialize the ConfigTree
.AbstractActionLifecycle
interface.displayMessage
method definition corresponds to the overridden process
method defined in the listener's definition in jboss-esb.xml
.The custom_action
quickstart demonstrates these other types of lifecycle actions:
process
when the action fires:<action class="org.jboss.soa.esb.samples.quickstart.customaction.MyBasicAction" exceptionMethod="exceptionHandler" />
process
property are executed in sequence, when the action fires:<action class="org.jboss.soa.esb.samples.quickstart.customaction.StatefulAction" process="methodOne,methodTwo,displayCount" exceptionMethod="exceptionHandler" />
<action class="org.jboss.soa.esb.samples.quickstart.customaction.CustomConfigAction" process="displayConfig" myStuff="rocks"moreStuff="rocks harder"> <subElement1>Value of 1</subElement1> <subElement2>Value of 2</subElement2> <subElement3>Value of 3</subElement3> </action>
The second approach for building custom actions is to create JavaBean actions. These actions implement the org.jboss.soa.esb.actions.BeanConfiguredAction
interface. These actions are differentiated from the lifecycle actions in several ways, as follows:
initialize
, destroy
, process
, processSuccess
, and processException
). Instead, these actions are instantiated when a message is processed by the action pipeline.process
methods are always executed through Java reflection.An example of this type of action is also illustrated in the custom_action
quickstart:
<action name="seventh" class="org.jboss.soa.esb.samples.quickstart.customaction.CustomBeanConfigAction"> <property name="information" value="Hola Mundo" /> <property name="repeatCount" value="5"/> </action>
In this action definition, the bean's setter methods will be invoked with the properties defined in the jboss-esb.xml
file.
And here's a fragment from the org.jboss.soa.esb.samples.quickstart.customaction.CustomBeanConfigAction
class. Note that the setter methods correspond to the property names:
public void setInformation(String information) { this.information = information; } public void setRepeatCount(Integer repeatCount) { this.repeatCount = repeatCount; } public Message process(Message message) throwsActionProcessingException { System.out.println("[" + serviceCategory + ":" +serviceName + "] Repeat message: " + information +" " + repeatCount + " times:"); for (int i=0; i < repeatCount; i++) { System.out.println(information); } return message; }
One common trend in Java programing is that of simplifying complex structures and technologies with annotations. For example, EJB3 removed several of the complex and annoying requirements of EJB2 through the use of annotations. The third approach to creating custom actions that JBoss ESB provides is an annotation mechanism through which an action class can be created and configured. In order for the class to be identified as an annotated action, one or more of its public methods must be annotated with the org.jboss.soa.esb.actions.annotation.Process
annotation.
When using an annotated action class it is no longer necessary to handle the ConfigTree
, the configuration being handled through annotating fields or setter methods from within the class. There are some restrictions in the configuration types which can be used through this mechanism.
Fields and setter methods should be annotated with the
org.jboss.soa.esb.configure.ConfigProperty
annotation, for example:
@ConfigProperty private int intConfig; @ConfigProperty(use=Use.OPTIONAL) private String stringConfig; @ConfigProperty(name="AlternativeName") public void setEnumConfig(final MyEnum value) { ... }
The ConfigProperty
annotation can be configured through the following annotation elements:
name
: An optional element which defines the ConfigTree
attribute name. If this element is not specified then the attribute name will match the name of the field or will be derived from the setter method using the JavaBean conventions.use
: An optional element which defines whether the property is REQUIRED
or OPTIONAL
, defaulting to REQUIRED
. It is an error if a REQUIRED
property cannot be assigned a value.DefaultVal
: An optional element which defines the default value to use for the property if a value has not been specified within the action configuration.Choice
: An optional element which restricts the property values to a specified set. It is an error if a property is configured to a value not present within the choice set.Note that annotations using the
ConfigTree
annotation can only be configured using simple name
/value
properties, it is not possible to use hierarchical configurations nor to traverse the ConfigTree
hierarchy. The property types must be primitive or must define a constructor taking a single String parameter.
An annotated action can define any number of public initialization methods, each of which must be annotated with the org.jboss.soa.esb.lifecycle.annotation.Initialize
annotation. For example:
@Initialize public void firstInit() { ... } @Initialize public void secondInit(final ConfigTree configTree) throws ActionLifecycleException { ... }
The Initialize
methods may take an optional ConfigTree
parameter and may throw an ActionLifecycleException
.
An annotated action can also define any number of public destroy methods, each being annotated with the
org.jboss.soa.esb.lifecycle.annotation.Destroy
annotation. For example:
@Destroy public void firstDestroy() { ... } @Destroy public void secondDestroy(final ConfigTree configTree) throws ActionLifecycleException { ... }
The Destroy
methods may take an optional
ConfigTree
parameter and may throw an ActionLifecycleException
.
An annotated action can define any number of public process
methods, each being annotated with the
org.jboss.soa.esb.actions.annotation.Process
annotation, however only one method may be executed and this must be chosen through the action configuration.
@Process public Message process(final Message message)throws ActionProcessingException { ... }
Process
methods may be defined with any number of parameters and may choose not to return a value, for example:
// method expecting the default message payload to be of type MyType, // returning a String value for the response payload. @Process public String processMyType(final MyType payload) { ... } // method accessing the full message without updating // the response payload @Process public void processMessage(final Message message) { ... } // method ignoring message contents and updating response payload @Process public MyType process() { ... }
The parameter values passed to a process
method may be configured using a number of annotations; if none are present then the values are resolved as follows:
Examples of these annotations are as follows:
public String processAnnotations(@BodyParam("MyBody") MyBodyType body, @PropertyParam("MyProperty") Integer property, @AttachmentParam("MyAttachment") byte[] attachment) { ... }
An annotated action can define any number of public
processSuccess
methods, each of which must be annotated using the org.jboss.soa.esb.actions.annotation.OnSuccess
annotation. Each processSuccess
method will be invoked when a successful invocation of the pipeline has occurred and may be defined with no parameters or with a single Message
parameter, for example:
@OnSuccess public void firstSuccess() { ... } @OnSuccess public void secondSuccess(final Message message) { ... }
An annotated action may also define any number of public processException
methods, each of which must be annotated using the org.jboss.soa.esb.actions.annotation.OnException
annotation. Each processException
method will be invoked when an exception is raised during an invocation of the pipeline and may be defined with no parameters, a single Message
parameter or a Message
and Throwable
parameters, for example:
@OnException public void firstException() { ... } @OnException public void secondException(final Message message) { ... } @OnException public void thirdException(final Message message, final Throwable th) { ... }