Friday, June 10, 2011

WSO2 ESB Tips & Tricks 07: Cron Legacy

SOA architects and system integrators often come across situations where they want to schedule a particular task to be executed periodically. It could be a task which polls a message queue or a database at regular intervals to see whether any new data has been received. It could be a task which invokes a web service to retrieve a live feed or to check the status of an external system. Or it could be a task which performs regular house keeping activities such as cleaning up log files, updating configuration files and obtaining data backups.
WSO2 ESB provides a simple but powerful framework based on Quartz for scheduling and managing periodic tasks. The user can program tasks using Java and deploy them to the ESB as jar files. Such custom developed tasks must implement the Task interface of Apache Synapse which consists of a single method named execute(). Within this method user can implement his/her business logic which will then get executed periodically by the ESB. The compiled jar files containing the task implementation classes should be placed in the repository/components/lib directory of the ESB. These jar files will get converted into OSGi bundles at server startup and get deployed into the ESB runtime.
When it comes to scheduling tasks, the user has two options. The user can configure a simple trigger by simply specifying the number of times the task should be executed along with the period between successive runs. The following example shows how the task named foo has been scheduled to be executed every 5 seconds for 1000 times. Here the task implementation class is named org.wso2.testing.tasks.FooTask (to keep the task running until the server is shutdown, just get rid of the count attribute).
<task class="org.wso2.testing.tasks.FooTask" name="foo">
<trigger interval="5" count="1000"/>
</task>
Alternatively user can configure tasks using the well known ‘Cron’ syntax. This way it is possible to specify more complex timing requirements. The following example shows how the task foo has been scheduled to be executed every Friday at 5pm. (To learn more about Cron syntax refer this tutorial)
<task class="org.wso2.testing.tasks.FooTask" name="foo">
<trigger cron="0 0 17 ? ? FRI"/>
</task>
WSO2 ESB ships with a sample task implementation class named MessageInjector. This can be used to inject a message into the service bus periodically. Injected messages will be handled by the ‘main’ sequence so they can be routed and mediated as if they were received from a remote client. The same technique can be extended to implement a scheduled task which periodically invokes a web service. Sample 300 of WSO2 ESB documentation shows how to put the MessageInjector task to use.
<task class="org.apache.synapse.startup.tasks.MessageInjector" name="CheckPrice">
<property name="to" value="http://localhost:9000/services/SimpleStockQuoteService"/>
<property name="soapAction" value="urn:getQuote"/>
<property name="message">
<m0:getQuote xmlns:m0="http://services.samples">
<m0:request>
<m0:symbol>IBM</m0:symbol>
</m0:request>
</m0:getQuote>
</property>
<trigger interval="5"/>
</task>
Note how a set of properties are passed into the task implementation class from the XML configuration. This can be done with user developed custom tasks as well. Let’s assume the task implementation class has a private attribute named ‘studentName’. If you want to pass a value for this attribute from the task configuration you should implement a getter method and a setter method for the above attribute following the standard Java bean conventions (ie getStudentName and setStudentName). Then you can have the following property definition under the task configuration to initialize the studentName attribute.
<property name="studentName" value="Hiranya"/>
This technique can be used to initialize any primitive type attribute of the task implementation class.
WSO2 ESB scheduled tasks also support a concept called ‘pinned servers’. This comes in handy when deploying scheduled tasks in a cluster of ESB servers. In such an environment it might be required that the task get deployed only on some of the nodes and not all the nodes. But since nodes in a cluster usually share all the configuration files this becomes little tricky. The solution is to specify the pinned servers attribute in the task configuration. With that the task will get deployed only on the specified set of servers. Basically this feature allows you to pin a scheduled task to a subset of the nodes in a cluster.
<task class="org.wso2.testing.tasks.FooTask" name="foo" pinnedServers="host1.wso2.org, host2.wso2.org">
<trigger cron="0 0 17 ? ? FRI"/>
</task>
In the above example the task will get deployed only if the server hostnames match the ones specified in the pinnedServers attribute.
WSO2 ESB management console can be used to create and manage scheduled tasks. The ESB artifact uploader UI (added in ESB 3.x) can be used to upload jar files containing tasks to the ESB without restarting the server. Unfortunately current releases of WSO2 ESB do not enable the user to temporarily suspend the execution of a task. To do that user must remove the task from the ESB configuration. This limitation will be addressed in a future release.

3 comments:

Unknown said...

Really Good and Simple way of Explanation

Shaman said...

Hi Hiranya,

How do we set a server name in the WSO2 ESB? Synapse documentation says that by default if the server name is not set then the hostname of the server is used ('localhost' should as well work). I have tried with hostname and 'localhost' as the value of pinnedServers, but I get the following error when I try to run a task.

Server name not in pinned servers list. Not starting Task :

Thanks,
Shaman

Hiranya Jayathilaka said...

IIRC there's a parameter in axis2.xml to set the server name. Look for something like "SynapseConfig.ServerName"