Building Tasklets and Jobs with Soafaces
Join the DZone community and get the full member experience.
Join For FreeSoafaces is a framework for building highly modular server-side components called Tasklets that can be run in a job processing container. With soafaces you can create back-end workflow powered jobs and services that can be scheduled and run on the back-end with easy access to web services and can be customized using GWT customizer web GUIs.
This arcticles walks through the process of building a very simple soafaces component (called a Bundle). The Bundle implements a Tasklet and also includes a Weblet GUI interface for customizing the Tasklet's input JavaBean properties. You can then take this Bundle and run it as a self contained server-side module/component and combine it with other Bundles to form a job and run this within a scheduling engine such as JobServer. JobServer is a scheduling engine that uses soafaces as its plugin API for building and running jobs.
Follow these steps to learn how to develop, package and deploy your own SOAFaces Bundle (Bundle):
- Implement Tasklet Interface
- Implement Weblet Interface (Optional)
- Define an Input JavaBean for Tasklet (Optional)
- Define an Output JavaBean for Tasklet (Optional)
- Create MANIFEST.MF File
- Package Contents into a Bundle JAR file
- Deploy the new Bundle to a SOAFaces Container (like JobServer)
Step 1: Implement Tasklet Interface
Using the Tasklet interface, implement the org.soafces.bundle.workflow.Tasklet interface by implementing the onExecute method. Here is a simple example:
public class HelloWorld implements Tasklet
{
public HelloWorld()
{
super();
}
/**
* The outBean is null in this example because there is no
* output JavaBean defined.
*
*/
public void onExecute(SOATaskInputContext inputContext, SOATaskOutputContext outputContext)
{
//If your Tasklet uses an input and/or output JavaBean then it is
//best to get them and cast to the appropriate class type.
HelloWorldInput inputBean = (HelloWorldInput) inputContext.getInputBean();
/**
* Create and name a logger in order to log all messages. The name
* can be anything you like. These logs messages can be viewed
* using the JobServer Tracker tool which privides a history of
* all Taskelets and Jobs that have run.
*
* JobServer captures all Log4J and Java Logging API messages
* made by the Tasklet and makes them available for viewing from
* the Job Tracker tool. Note that Log4J Logger must support
* additivity=true for logs to be captured and reported by Job Tracker.
*/
Logger myLogger = Logger.getLogger("myLogger");
myLogger.log(Level.INFO, "Hello " + inputBean.getPersonName());
return;
}
}
The onExecute method will be called when the above Tasklet is run as part of a job.
Step 2: Implement Weblet Interface (Optional)
This step is optional when creating Tasklets. The Weblet GUI will be used to customize the input JavaBean that is used as input to the Tasklet when the job is run on the server-side. Here is what the Weblet implementation might look like:
public class HelloWorldCustomizer extends SimplePOJOWeblet
{
private TextBox _oNameField = new TextBox();
private HelloWorldInput _oMyInputBean = null;
/**
*/
public HelloWorldCustomizer() {}
protected void populateMainPanelPOJO(IsSerializable inputBean)
{
_oMyInputBean = (HelloWorldInput) inputBean;
VerticalPanel vSpacer = null;
getMainPanel().add(new Label("Simple hello world example. Constructs a simple " +
"hello world message and logs it using Java " +
"Logging API when the Tasklet is run in a Job"));
vSpacer = new VerticalPanel(); vSpacer.setHeight("20");
getMainPanel().add(vSpacer);
getMainPanel().add(new Label("(Enter your name to put into hello world message)"));
vSpacer = new VerticalPanel(); vSpacer.setHeight("2");
getMainPanel().add(vSpacer);
getMainPanel().add(new Label("Name: "));
getMainPanel().add(_oNameField);
vSpacer = new VerticalPanel(); vSpacer.setHeight("20");
getMainPanel().add(vSpacer);
//Initialize the field to the value in the input JavaBean
_oNameField.setText(_oMyInputBean.getPersonName());
}
/**
* This method should be used to save the state
* of the GUI to the input JavaBean and perform
* any necessary validation checks. If the validation
* is not proper it should return false.
*
* The Container hosting the Weblet will call
* this method in order to save the input JavaBean
* to its persistent store. The saved state of the
* input JavaBean is what is referenced when the
* Tasklet/Job is run.
*/
public void onSaveInputBean(SuccessFailCallback callback)
{
try {
//Save the changes made by the user/gui to the input JavaBean
//person name
if(_oNameField.getText() == null || _oNameField.getText().trim().equals(""))
{
Window.alert("Error: No name specified");
callback.returnFailure();
return;
}
else
{
_oMyInputBean.setPersonName(_oNameField.getText());
}
callback.returnSuccess();
}catch(Throwable ex) {
callback.returnFailure();
}
}
}
Step 3: Define an Input JavaBean for Tasklet (Optional)
The input JavaBean can be edited by the Weblet GUI and used as input by the Tasklet when the job is run. An example would be like this:
/**
* Note that an Input JavaBean must implement IsSerializable to be used
* by GWT clients.
*/
public class HelloWorldInput implements IsSerializable
{
private String personName="";
public HelloWorldInput()
{
super();
}
/**
* Name of person to put in the Hello world sentence.
*/
public void setPersonName(String value)
{
personName=value;
}
public String getPersonName()
{
return personName;
}
}
Step 4: Define an Output JavaBean for Tasklet (Optional)
The output JavaBean is used by the Tasklet as output. When the Tasklet is run as part of a job the Tasklet interface can write all relevant output to this JavaBean. An example of an output JavaBean would be like this:
/**
* Note that an Output JavaBean must implement IsSerializable to be used
* by GWT clients.
*/
public class HelloWorldOutput implements IsSerializable
{
private String personName="";
public HelloWorldInput()
{
super();
}
/**
* Name of person to put in the Hello world sentence.
*/
public void setPersonName(String value)
{
personName=value;
}
public String getPersonName()
{
return personName;
}
}
Step 5: Create MANIFEST.MF File
The MANIFEST.MF file contains the meta information that informs a SOAFaces container like JobServer what is contained in the Bundle JAR file and what interfaces are defined by the Bundle. The file is placed in the META-INF directory within the Bundle JAR.
SOAFaces-Name: Hello World 3
SOAFaces-SymbolicName: surda-beansoup-helloworld3
SOAFaces-Version: 1.0.0
SOAFaces-Description: Simple Tasklet with an input JavaBean and MFace Customizer
SOAFaces-Vendor: Grand Logic, Inc
SOAFaces-Category: example
#Tasklet run on the server-side when java/task is run
SOAFaces-Tasklet: org.beansoup.helloworld3.workflow.HelloWorld
#Optional input JavaBean used by the Weblet and Tasklet
SOAFaces-InputBean: org.beansoup.helloworld3.client.HelloWorldInput
#GWT client Module name
SOAFaces-Weblet: org.beansoup.helloworld3.HelloModuleName
Step 6: Package Contents into a Bundle JAR file
All the code and content are packaged and put into a Bundle JAR file with the file extension .sfb. Here is an example directory structure of the JAR file.
META-INF/MANIFEST.MF
classes/org/beansoup/helloworld3/client/
HelloWorldCustomizer.java
HelloWorldCustomzier.class
HelloWorldInput.java
HelloWorldInput.class
classes/org/beansoup/helloworld3/workflow/
HelloWorld.class
lib/
someThirdPartyGWT-Client.jar
someThirdPartyServer-Side.jar
Here is an example of a simple ANT target that can construct the Bundle archive file:
<target name="build_sfb" depends="compile" description="Build Bundle for example Tasklet">
<mkdir dir="build/sfb-dir/org-beansoup-helloworld3-v1-0-0/classes" />
<copy todir="build/sfb-dir/org-beansoup-helloworld3-v1-0-0/classes"
overwrite="false">
<fileset dir="${build.classes.dir}"
includes="org/beansoup/helloworld3/**/*.class"/>
<!-- include source for GWT client code -->
<fileset dir="${src.dir}"
includes="org/beansoup/helloworld3/*.xml
org/beansoup/helloworld3/client/*.java
org/beansoup/helloworld3/public/*.css"/>
</copy>
<jar destfile="build/sfb-dir/org-beansoup-helloworld3-v1-0-0.sfb"
basedir="build/sfb-dir/org-beansoup-helloworld3-v1-0-0"
includes="**">
<manifest>
<attribute name="SOAFaces-Name" value="Hello World 3"/>
<attribute name="SOAFaces-SymbolicName" value="surda-beansoup-helloworld3"/>
<attribute name="SOAFaces-Version" value="1.0.0"/>
<attribute name="SOAFaces-Description" value="Simple Tasklet with an input JavaBean and Weblet customizer"/>
<attribute name="SOAFaces-Vendor" value="Grand Logic, Inc"/>
<attribute name="SOAFaces-Category" value="example"/>
<attribute name="SOAFaces-Tasklet" value="org.beansoup.helloworld3.workflow.HelloWorld"/>
<attribute name="SOAFaces-InputBean" value="org.beansoup.helloworld3.client.HelloWorldInput"/>
<attribute name="SOAFaces-OutputBean" value="org.beansoup.helloworld3.client.HelloWorldOutput"/>
<attribute name="SOAFaces-Weblet" value="org.beansoup.helloworld3.client.HelloWorldCustomizer"/>
<attribute name="SOAFaces-InputViewer" value=""/> <!-- Optional GUI viewer for viewing input of Tasklet -->
<attribute name="SOAFaces-OutputViewer" value=""/> <!-- Optional GUI viewer for viewing output of Tasklet -->
</manifest>
</jar>
</target>
Notice that the client GWT code requires the source also be included in the JAR file. The GWT compiler requires the source and will use this to compile, on the fly, the Weblet into GWT AJAX code. The lib directory can contain any needed third party JARs used by the Weblet or Tasklet code.
Note, that you have the option to include the GWT client code (java source code an compiled code) in the Bundle and let the SOAFaces container compile the GWT client code into javascript for you (frees developer from dealing with GWT compiler) or you can compile the GWT client code yourself (using the GWT compiler) and include the javascript in the Bundle directly. You simply include the compiled GWT client code in the www directory in the Bundle jar.
Step 7: Deploy the new Bundle to a SOAFaces Container (e.g. JobServer)
Name the Bundle JAR something like myhelloworld.sfb and place it in the JobServer soafaces directory. The name must be unique within JobServer. Now go to the JobServer SOAFaces Repository tool and you should see the new Bundle. It can now be used to build a new job!
For more information on Soafaces and building/running jobs and tasklets in a scheduling engine refer to:
Opinions expressed by DZone contributors are their own.
Comments