Pages

Tuesday, September 22, 2009

Job scheduling in Weblogic

This blog is about how you can run a batchjob on a specific time in the Weblogic application server and as extra, I made an ADF page in which you can stop or start the jobs. This job schedular can start for example some Soa processes at a specific time.
The scheduling is done with the help of the CommonJ API which is standard in Weblogic. This example works perfectly in a managed node but if you want to do the same in a Weblogic Cluster then you should not read this blog and go the James Bayer's blog . And for more information about Timer API see the official Weblogic documentation.

Very important, this job scheduling only works within in a web application.
First we start by adding the TimerManager to the web.xml

<resource-ref>
<res-ref-name>tm/TimerManager</res-ref-name>
<res-type>commonj.timers.TimerManager</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Unshareable</res-sharing-scope>
</resource-ref>
</web-app>

Now we done this we can add a servlet which start this TimerManager and its jobs. Important that the servlet is automatically started when the webapp is started.

<servlet>
<display-name>timer</display-name>
<servlet-name>timer</servlet-name>
<servlet-class>nl.whitehorses.wls.schedular.TimerServlet</servlet-class>
<load-on-startup>100</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>timer</servlet-name>
<url-pattern>/timer</url-pattern>
</servlet-mapping>

Then the servlet code which start the TimeManager and the two example batches. In this example the job is started again when it is finished after 30 seconds. If you want to do this at a specific time then use scheduleAtFixedRate

package nl.whitehorses.wls.schedular;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import commonj.timers.*;

/**
* TimerServlet demonstrates a simple use of commonj timers
*/
public class TimerServlet extends HttpServlet {


public void init(ServletConfig config) throws ServletException {

super.init(config);
System.out.println("timer servlet is initialized ");
try {
InitialContext ic = new InitialContext();
TimerManager tm = (TimerManager)ic.lookup("java:comp/env/tm/TimerManager");

Timer batchRun1Timer = null;
Boolean batchRun1TimerIsRunning = false;
Timer batchRun2Timer = null;
Boolean batchRun2TimerIsRunning = false;

// Execute timer every 30 seconds starting immediately
batchRun1Timer = tm.schedule(new Batch1(), 0, 30 * 1000);
batchRun1TimerIsRunning = true;

batchRun2Timer = tm.schedule(new Batch2(), 0, 30 * 1000);
batchRun2TimerIsRunning = true;

config.getServletContext().setAttribute("batch1",batchRun1Timer);
config.getServletContext().setAttribute("batch2",batchRun2Timer);
config.getServletContext().setAttribute("batch1Running",batchRun1TimerIsRunning);
config.getServletContext().setAttribute("batch2Running",batchRun2TimerIsRunning);

} catch (NamingException ne) {
ne.printStackTrace();
}
}

public void service(HttpServletRequest req, HttpServletResponse res) throws IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("Timer servlet is working!");
}
}

Here is an example of a batch job. The timerExpired method is fired every time when the job time has passed. Here you can put in your own code and when the job is canceled then the TimerCancel method is fired.

package nl.whitehorses.wls.schedular;

import commonj.timers.*;
import java.io.Serializable;

public class Batch1 implements Serializable, TimerListener, CancelTimerListener {

public void timerExpired(Timer timer) {
System.out.println("Batch1 timer expired called on " + timer);
}
public void timerCancel(Timer timer) {
System.out.println("Batch1 timer cancelled called on " + timer);

}
}

And finally the JSF page with its backing bean to control the jobs.


<?xml version='1.0' encoding='windows-1252'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
<jsp:directive.page contentType="text/html;charset=windows-1252"/>
<f:view>
<af:document id="d1">
<af:form id="f1">
<af:panelHeader text="Timers" id="ph1">
<af:panelFormLayout id="pfl1">
<af:panelGroupLayout id="pgl6" layout="horizontal">
<af:panelGroupLayout id="pgl8" layout="vertical">
<af:poll id="poll1">
<af:panelGroupLayout id="pgl5" layout="vertical">
<af:outputLabel value="#{TimerBean.tmStatus}" id="ol4"
partialTriggers="poll1"/>
<af:outputLabel value="#{TimerBean.batch1Status}" id="o22"
partialTriggers="poll1"/>
<af:outputLabel value="#{TimerBean.batch2Status}" id="o23"
partialTriggers="poll1"/>
</af:panelGroupLayout>
</af:poll>
</af:panelGroupLayout>
<af:panelGroupLayout id="pgl7" layout="vertical">
<af:commandButton text="Time Manager On / Off" id="cb1"
actionListener="#{TimerBean.timerManager}"/>
<af:commandButton text="Batch 1 On / Off" id="cb2"
actionListener="#{TimerBean.Batch1}"/>
<af:commandButton text="Batch 2 On / Off" id="cb3"
actionListener="#{TimerBean.Batch2}"/>
</af:panelGroupLayout>
</af:panelGroupLayout>
</af:panelFormLayout>
</af:panelHeader>
</af:form>
</af:document>
</f:view>
</jsp:root>




package nl.whitehorses.wls.backing;

import commonj.timers.Timer;
import commonj.timers.TimerManager;

import javax.faces.event.ActionEvent;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import nl.whitehorses.wls.schedular.Batch1;
import nl.whitehorses.wls.schedular.Batch2;

import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;

public class TimerBean {


private InitialContext ic = null;
private TimerManager tm = null;

private Timer batchRun1Timer = null;
public Boolean batchRun1TimerIsRunning = false;
private Timer batchRun2Timer = null;
public Boolean batchRun2TimerIsRunning = false;


public TimerBean() {
try {
ic = new InitialContext();
tm = (TimerManager)ic.lookup("java:comp/env/tm/TimerManager");

FacesContext ctx = FacesContext.getCurrentInstance();
ServletContext servletContext = (ServletContext) ctx.getExternalContext().getContext();

batchRun1Timer = (Timer)servletContext.getAttribute("batch1");
batchRun2Timer = (Timer)servletContext.getAttribute("batch2");
batchRun1TimerIsRunning = (Boolean)servletContext.getAttribute("batch1Running");
batchRun2TimerIsRunning = (Boolean)servletContext.getAttribute("batch2Running");
System.out.println("init end");

} catch (NamingException e) {
e.printStackTrace();
}
}

public void timerManager(ActionEvent actionEvent) {
// Add event code here...
if ( tm.isSuspended() ) {
tm.resume();
} else {
tm.suspend();
}
}

public void Batch1(ActionEvent actionEvent) {
// Add event code here...
if ( batchRun1TimerIsRunning ) {
batchRun1Timer.cancel();
batchRun1TimerIsRunning = false;
} else {
batchRun1Timer = tm.schedule(new Batch1(), 0, 10 * 1000);
batchRun1TimerIsRunning = true;
}

}

public void Batch2(ActionEvent actionEvent) {
// Add event code here...
if ( batchRun2TimerIsRunning ) {
batchRun2Timer.cancel();
batchRun2TimerIsRunning = false;
} else {
batchRun2Timer = tm.schedule(new Batch2(), 0, 10 * 1000);
batchRun2TimerIsRunning = true;
}
}

public String getTmStatus () {
if ( tm.isSuspended() ) {
return "TimerManager is stopped";
} else {
return "TimerManager is running";
}
}

public String getBatch1Status () {
Long time = batchRun1Timer.getScheduledExecutionTime();
java.util.Date date = new java.util.Date(time);
if ( batchRun1TimerIsRunning ) {
return "Batch1 scheduled time "+date.toString();
} {
return "Batch1 stopped";
}
}

public String getBatch2Status () {
Long time = batchRun2Timer.getScheduledExecutionTime();
java.util.Date date = new java.util.Date(time);
if ( batchRun2TimerIsRunning ) {
return "Batch2 scheduled time "+date.toString();
} {
return "Batch2 stopped";
}
}


public Timer getBatchRun1Timer(){
return batchRun1Timer;
}

public void setBatchRun1Timer(Timer batchRun1Timer ){
this.batchRun1Timer = batchRun1Timer;
}

public Timer getBatchRun2Timer(){
return batchRun2Timer;
}

public void setBatchRun2timer(Timer batchRun2Timer ){
this.batchRun2Timer = batchRun2Timer;
}
}


Here is the example workspace.

7 comments:

  1. Oracle weblogic is a thing which is preferred by many of the software developers. Java Developers use to find it good to work and embed Oracle things to Java with their high compatibility and functionality.

    ReplyDelete
  2. I am trying to configure job scheduling in weblogic server.i wrote Listener ,JobScheduler and i configured listener class in weblogic-application.xml.. but iam unable to find out that schudling after deployed my application.

    ReplyDelete
  3. Hi,

    can you provide me more information and you mean web.xml and is the listener automatically started and add some logging info in your code.

    in wls you can redirect system output to the wls logging.

    thanks

    ReplyDelete
  4. Hi Edwin,
    I am working with a WebLogic 10.3.5 clustered environment. My application includes an commonj.timers.TimerManager provided by WebLogic with JNDI lookup for "weblogic.JobScheduler" to schedule task and there are few existing tasks which will make an entry to WEBLOGIC_TIMERS table with every schedule or with update ,but when i tried to add new task along with existing service entry is not happening in WEBLOGIC_TIMERS table and am unable locate from where entry[save] is happening for existing service into WEBLOGIC_TIMERS table. Example: TaskA [Existing],TaskB [Existing],TaskC [New] - On Scheduling does not makes entry into WEBLOGIC_TIMERS table May i know how this schedulers are making entry [save] into WEBLOGIC_TIMERS table ?

    ReplyDelete
    Replies
    1. Hi,

      Sorry I don't, maybe you can use a java decompiler and do some reverse engineering

      thanks

      Delete
  5. Great blog Edwin! Still works these days, thanks a lot!!

    ReplyDelete
  6. I'm struggling to understand why you would do all of this when you could use EJB Timer Service to accomplish the same thing. Is there a reason why you choose to use CommonJ over EJB Timer Services?

    ReplyDelete