Monday, March 26, 2007

The difference of Timer implementation in WebLogic 8.x and 9.x

eDocs of wl8.x http://edocs.bea.com/wls/docs81/jmx/timer.html
eDocs of wl9.x http://edocs.bea.com/wls/docs90/jmxinst/timer.html

In the 8.1, the timer uses a simple way to add and remove notification while 9.x uses a service to do schedule.

Sunday, March 25, 2007

Scheduling Jobs in WebLogic Server

(Comes form http://wldj.sys-con.com/read/48929.htm)
(Chinese Edition http://dev2dev.bea.com.cn/techdoc/200508639.html)

The need to run scheduled jobs is increasing and becoming common across all J2EE applications. The current J2EE specification doesn't provide an easy way to schedule jobs inside an enterprise application.

We can broadly classify scheduled J2EE jobs into two categories: Server-specific scheduled jobs and application-specific scheduled jobs. This article explores how to schedule application-specific jobs inside a WebLogic Application Server.

Server-Specific vs. Application-Specific Scheduled JobsServer-specific scheduled jobs are associated with the lifecycle of the application server. Typically these jobs are started during the server's startup and will be active until it's shut down. As a consequence, all the resources needed for scheduled jobs have to be specified in the server's classpath.

Application-specific scheduled jobs are associated with the lifecycle of the enterprise application. They are started after the application is deployed and will be active until the application is undeployed. The resources required for application-specific scheduled jobs are packaged in the enterprise application (EAR - enterprise archive resource).

Application jobs are preferable to server jobs for the following reasons:
Any changes to the application jobs can be redeployed easily with the application (EAR file). For server jobs the server has to be restarted for the new changes to take effect, and this may not be desirable as it impacts the availability of other applications deployed on that server.
Since the scheduled jobs usually invoke business functionality, it's more logical to package them with in the EAR file.

By associating the resources with the EAR file, we can deploy the EAR file on another instances of WebLogic Server without having to reconfigure the resources for that server.
Schedule Jobs Inside an Enterprise ApplicationWe will look at an example of how to implement application-specific scheduled jobs. Here we schedule two jobs that invoke business functions at certain specified intervals.

Order submission - invoked every day.
Inventory submission - invoked once a week.

In this example we're going to use WebLogic's Application Lifecycle Events in tandem with WebLogic's Timer Notifications.

These are the three steps involved in setting up scheduled jobs:
Implement the Timer Notification Listener, which is responsible for adding, listening, and handling the timer notifications.
Implement the Application Lifecycle Listener, which instantiates the Timer Notification Listener on desired application lifecycle events.

Register the Application Lifecycle Listener in weblogic-application.xml Implement the MyAppJobSchedulerMyAppJobScheduler will implement the javax.management.NotificationListener interface.

public final class MyAppJobScheduler implements NotificationListener
This class will instantiate weblogic.management.timer.Timer class and register itself as a listener for timer notifications. The Timer class is a WebLogic implementation of javax.management.TimerMBean.

timer = new Timer();
timer.addNotificationListener(this, null, "some handback object");


The order notification and inventory notification are added to the timer before the timer starts.

timer.addNotification("OrderSubmission", "Order Submission",
this,orderSubmissionDate, DAILY_PERIOD);
timer.addNotification("InventorySubmission",
"Inventory Submission", this,inventorySubmissionDate,
WEEKLY_PERIOD);


We will schedule the jobs so the Order job will run every day at 5 p.m. and the Inventory job will run once a week, every Friday at 10:30 p.m. Please check the code listing for details on how to construct the Date object with the specified time.

The callback method will get the notification. Based on the notification, a business process component (EJB) can be invoked to fulfill the scheduled job.

public void handleNotification(Notification notif, Object handback)
{
String type = notif.getType();
if ( type.equals("OrderSubmission") )
{
// invoke the component or ejb that does the order processing and submission
}
else if ( type.equals("InventorySubmission") )
{
// invoke the component or ejb that does the inventory processing and submission
}
}


The cleanUp method of MyAppJobScheduler stops the timer, and removes all the notifications that were added to it. We can invoke the cleanUp method as shown above from the preStop method, or it can be invoked from the finalize method of MyAppJobScheduler.

public synchronized void cleanUp()
{
System.out.println(">>> MyAppJobScheduler cleanUp method called.");
try
{
timer.stop();
timer.removeNotification(orderNotificationId);
timer.removeNotification(inventoryNotificationId);
System.out.println(">>> MyAppJobScheduler Scheduler stopped.");
}
catch (InstanceNotFoundException e)
{
e.printStackTrace();
}
}


Here is how the final method of MyAppJobScheduler will look:
protected void finalize() throws Throwable
{
System.out.println(">>> MyAppJobScheduler finalize called.");
cleanUp();
super.finalize();
}


Implement MyAppListenerWe will write a class MyAppListener that extends weblogic.application.ApplicationLifeCyleListener. Application lifecycle listener events provide handles that developers can use to control behavior during deployment, undeployment, and redeployment.

public class MyAppListener extends ApplicationLifecycleListener
We want to start the scheduled jobs as soon as the application is deployed. So we will invoke the MyAppJobScheduler in the postStart method of MyAppListener. If you want to invoke the Scheduler before the startup, the invocation will be in the preStart method.
public void postStart(ApplicationLifecycleEvent evt)
{
System.out.println( "MyAppListener:postStart Event");
//Start the Scheduler
myAppJobScheduler = new MyAppJobScheduler();
}


We want to unschedule the jobs before the application is undeployed. So we invoke the MyAppJobScheduler's cleanUp method in the preStop method of MyAppListener.
public void preStop(ApplicationLifecycleEvent evt)
{
System.out.println( "MyAppListener:preStop Event");
//Stop the Scheduler
myAppJobScheduler.cleanUp();
}


Register MyAppListenerIn the weblogic-application.xml, register MyAppListener class for application lifecycle events.
<listener>
<listener-class>MyAppListener</listener-class>
</listener>
Include MyAppJobScheduler, MyAppListener in the application classpath, or alternatively you can specify the jar file using parameter.
<listener>
<listener-class>MyAppListener</listener-class>
<listener-uri>scheduler.jar</listener-uri>
</listener>

References
WebLogic Server: Programming Application Lifecycle Events: http://e-docs.bea.com/wls/docs90/programming/lifecycle.html
WebLogic Timer:
http://e-docs.bea.com/wls/docs90/javadocs/weblogic/management/timer/Timer.html
J2EE Notification Listener:
http://java.sun.com/j2ee/1.4/docs/api/javax/management/NotificationListener.html

Scheduling Jobs in WebLogic Server - src

The source code is in the following link:
http://photos.sys-con.com/story/res/48929/source.html

MyAppJobScheduler.java
/* * MyAppJobScheduler.java * */
import java.util.*;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.InstanceNotFoundException;
import weblogic.management.timer.Timer;
// Implementing NotificationListenerpublic final class MyAppJobScheduler implements NotificationListener
{
private static final long DAILY_PERIOD = Timer.ONE_DAY;
private static final long WEEKLY_PERIOD = 7 * Timer.ONE_DAY;
private Timer timer;
private Integer orderNotificationId;
private Integer inventoryNotificationId;
public MyAppJobScheduler() {
// Instantiating the Timer MBean
timer = new Timer();
// Registering this class as a listener
timer.addNotificationListener(this, null, "some handback ?object");
// These values should be read from a property file
int orderSubmissionHour=17;
int orderSubmissionMinute=0;
int inventorySubmissionDay = 6;
int inventorySubmissionHour = 22;
int inventorySubmissionMinute = 30;
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY,orderSubmissionHour);
calendar.set(Calendar.MINUTE,orderSubmissionMinute);
Date orderSubmissionDate = calendar.getTime();
calendar.set(Calendar.DAY_OF_WEEK, inventorySubmissionDay);
calendar.set(Calendar.HOUR_OF_DAY,inventorySubmissionHour);
calendar.set(Calendar.MINUTE,inventorySubmissionHour);
Date inventorySubmissionDate = calendar.getTime();
// Add the order notification which should run every day at ?5:00 PM
orderNotificationId = timer.addNotification("OrderSubmission", "Order Submission",
this,orderSubmissionDate, DAILY_PERIOD);
// Add the inventory notification which should run every Friday ?at 10:30 PM
inventoryNotificationId = timer.addNotification?("InventorySubmission", "Inventory Submission",
this,? inventorySubmissionDate, WEEKLY_PERIOD);
timer.start();
System.out.println( ">>> MyAppJobScheduler started." );
}

protected void finalize() throws Throwable {
System.out.println(">>> MyAppJobScheduler finalize called.");
cleanUp();
super.finalize(); }

public synchronized void cleanUp() {
System.out.println(">>> MyAppJobScheduler cleanUp method ?called.");
try {
timer.stop();
timer.removeNotification(orderNotificationId);
timer.removeNotification(inventoryNotificationId);
System.out.println(">>> MyAppJobScheduler Scheduler ?stopped.");
} catch (InstanceNotFoundException e){
e.printStackTrace();
}
}
/* callback method */
public void handleNotification(Notification notif, Object handback) {
String type = notif.getType();
if ( type.equals("OrderSubmission") ) {
// invoke the component or ejb that does the order processing ?and submission
} else if ( type.equals("InventorySubmission") ) {
// invoke the component or ejb that does the inventory ?processing and submission
}
}

public static void main(String[] args) {
MyAppJobScheduler myAppJobScheduler = new MyAppJobScheduler(); }
}


MyAppListener.java
/* * MyAppListener.java * */
import weblogic.application.ApplicationLifecycleListener;
import weblogic.application.ApplicationLifecycleEvent;
public class MyAppListener extends ApplicationLifecycleListener{
MyAppJobScheduler myAppJobScheduler;
public void preStart(ApplicationLifecycleEvent evt) {
System.out.println("MyAppListener:preStart Event");
}
public void postStart(ApplicationLifecycleEvent evt) {
System.out.println( "MyAppListener:postStart Event");
// Start the Scheduler myAppJobScheduler = new MyAppJobScheduler();
}

public void preStop(ApplicationLifecycleEvent evt) {
System.out.println( "MyAppListener:preStop Event");
// Stop the Scheduler
myAppJobScheduler.cleanUp();
}

public void postStop(ApplicationLifecycleEvent evt) {
System.out.println( "MyAppListener:postStop Event");
}
public static void main(String[] args) {
System.out.println( "MyAppListener:main method"); }
}

weblogic-application.xml
<weblogic-application>
<listener>
<listener-class>MyAppListener</listener-class>
</listener>
</weblogic-application>