“Universal” Scheduler implementation for WebSphere Liberty and WebSphere Full Profile

Introduction

In one of our projects, we are creating an integration Java application running on either WebSphere Liberty or WebSphere Application Server full profile. This application has two scheduled components, which is completely normal. But, when we came to the part, which runtime will be used, we were stuck (it was unclear at that moment of the project).

The problem is pretty simple, the scheduler must be implemented on a different way when running on Liberty or full profile. We wanted to avoid creating multiple scheduler implementations either as a seperated component (wrapped in a JAR file or something) or application version.

Struggle with WebSphere runtimes

The problem with Liberty and full profile,  that both are implementing a scheduler on a  different ways to schedule a particular job. WebSphere Liberty leverages the javax.enterprise.concurrent.ManagedScheduledExecutorService which is awesome, but the full profile unfortunately does not support it.

The full profile still (*sigh*) using the old commonj.timers.TimerManager implementation, which is fair enough, but better put it back to the Historical Museum if we can.

So these are the differences between the runtime environments, and this is making difficult to us a bit hard.

Solution: abstract scheduler

We created an abstract class, hiding the implementation from the child classes. Also, this class has two public abstract methods: getInterval() and run(). These methods can be used by the child classes later.

AbstractScheduler#getInterval()

This method has only one purpose, define the frequency of the scheduled task. In other words, this method will return a long type number. In our case, this return only a positive number.

Example from the child class:

@Override
public long getInterval() {
    return 5;
}

AbstractScheduler#run()

This method can declare the actual task, which will be executed by the scheduler.
Example from the child class:

@Override
public void run() {
    … select some values from a database and update those values in
    a different system using REST calls.
    Or whatever...
}

Okay, I see, get me the implementation

Okay, okay. I’m done with intros. Let’s get to the implementation. Let’s create the abstract class with the scheduler implementation, then we will create a child class extending the abstract class.

public abstract class AbstractScheduler implements Serializable, TimerListener {
    private static final long serialVersionUID = -9028396825843511410L;
}

So, right now, we have an abstract class, which implements the Serializable and TimerListener interfaces. The class also has a generated serial version UID. The fun part now comes: add some private fields:

private TimerManager tm = null;
private Timer timer = null;
private ScheduledFuture<?> scheduledFuture = null;

So, we created 3 fields so far. The TimerManager will hold a reference of the TimerManager, when running on full profile. The Timer object will hold a reference of the scheduled task itself, making us able to create a method, which can terminate the earlier scheduled task (see later). Finally, the ScheduledFuture will hold a reference for the scheduled task, when using the ManagedScheduledExecutorService implementation.

Add methods

Now we need to create 5 methods (2 public abstract, 1 private, 1 public and 1 public final). These are the following (in alphabet order):

public final void cancelTask() {}
public abstract long getInterval();
public void initialize() {}
public abstract void run();
private Runnable runRunnable() {}

Please have a look on the following 5 mini chapters to get an understanding why those methods are created.

AbstractScheduler#cancelTask()

This method will help you successfully terminate an earlier scheduled task. This method will be universal, because based on the runtime environment, it will use the proper interfaces and objects, to cancel any kind of task. Useful when you have some listener (ServletContextListener, etc.) classes where you are handling the startup/shutdown scenarios.

AbstractScheduler#getInterval()

This is I mentioned before, determines the frequency of the scheduled task needs to be executed.

AbstractScheduler#initialize()

This method is the main actor of this blog entry. This is where we are going to implement the runtime environment specific scheduler (see later).

AbstractScheduler#run()

This was also mentioned earlier, this method will be implemented by the child classes and define the exact logic, which will be executed on a periodic way by the scheduler.

AbstractScheduler#runRunnable()

This is required, a private method to be able to execute the run() method when going with Liberty runtime. Consider it as a wrapper method.

AbstractScheduler#initialize()

Haha! Still reading? You are really curious. So, we did not (I mean in our project) want to make a decision about the platform using some voodoo magic, so we put a key=value pair in the app’s config file, where the installer will set the proper value. I mean, a property which tells the application that we are running on Liberty or not. For this example, we are going to use a simple boolean variable (true means Liberty, false means Full Profile).

public void initialize() {
    boolean isLiberty = true;
    if(isLiberty) {
        // We are running on liberty
        ManagedScheduledExecutorService executor = (ManagedScheduledExecutorService) new InitialContext().lookup("java:comp/env/tm/default");
        scheduledFuture = executor.scheduleAtFixedRate(runRunnable(), 0, getInterval(), TimeUnit.MINUTES);
    } else {
        // We are running on full profile
        tm = (TimerManager) new InitialContext().lookup("java:comp/env/tm/default");
        timer = tm.scheduleAtFixedRate(this, 0, getInterval() * 60 * 1000);
    }
}

So, simple is that, isn’t it? What is the trick here? The full profile does not want to load the ManagedScheduledExecutorService implementation until the first call on that object is made. So, technically, the implementation completely can be missing on a full profile, this still works. And vica versa in Liberty. Awesome!

Please notice, that TimerManager is using the time value in milliseconds, but the ManagedScheduledExecutorService can be used on different ways (i.e.: TimeUnit.HOURS, TimeUnit.SECONDS etc.).

AbstractScheduler#cancelTask()

This method as I mentioned earlier is required to be able to cancel/demolish/stop the  task scheduled before. This is, where more of the fields are being used. Let’s have a look.

boolean isLiberty = true;
    if (isLiberty) {
        if (!scheduledFuture.isCancelled()) {
            scheduledFuture.cancel(false);
        }
    } else {
        timer.cancel();
    }

The false attribute at the scheduledFuture.cancel() method will order the cancel() method to be graceful. So if there are any task which are currently running by the scheduler, the cancel will wait to finish and then, canceling them.

Unfortunately, the Timer implementation does not have such a graceful kill, so simply kill it.

timerExpired() and run()

Because the AbstractScheduler implements the TimerListener interface, it means that a method must be implemented by our class from the TimerManager. This is  timerExpired() method.

We wanted to hide any implementation specific code from the child objects, so we created a run() method, which will be used by the timerExpired() method as well as the runRunnable() method.

The timerExpired() is very simple, it will just call the run() method. So when the TimerManager is firing, it will execute the timerExpired() method which calls the run() method. Method chaining! 🙂

private void timerExpired(Timer arg0) {
    run();
}

The runRunnable() is a different story, because this will be called when running on Liberty and have to use the ManagedScheduledExecutorService. So technically, the run() method needs to be executed whatever Scheduler implementation we have.

private Runnable runRunnable() {
final AbstractScheduler aSch = this;
return new Runnable() {
public void run() {
aSch.run();
}
};
}

Puting all those together

So, we are done here. Let me show you the full AbstractScheduler looks like.

package test;

import java.io.Serializable;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.enterprise.concurrent.ManagedScheduledExecutorService;
import javax.naming.InitialContext;

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

public abstract class AbstractScheduler implements TimerListener, Serializable {
    private static final long serialVersionUID = -1746069930718614842L;

    private TimerManager tm = null;
    private Timer timer = null;
    private ScheduledFuture<?> scheduledFuture = null;

    private boolean isLiberty = true;

    @Override
    public void timerExpired(Timer arg0) {
        // TODO Auto-generated method stub
        run();
    }

    public void initialize() {
        try {
            if (isLiberty) {
                ManagedScheduledExecutorService executor = (ManagedScheduledExecutorService) new InitialContext()
.lookup("java:comp/env/tm/default");

            // Executor requires minutes based on the TimeUnit
            scheduledFuture = executor.scheduleAtFixedRate(runRunnable(), 0, getInterval(), TimeUnit.MINUTES);
            } else {
                tm = (TimerManager) new InitialContext().lookup("java:comp/env/tm/default");

                // TimerManager requires milliseconds
                timer = tm.scheduleAtFixedRate(this, 0, getInterval() * 60 * 1000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public abstract long getInterval();
    public abstract void run();

    private Runnable runRunnable() {
        final AbstractScheduler aSch = this;
        return new Runnable() {
            public void run() {
                aSch.run();
            }
        };
    }

    public final void cancelTask() {
        try {
            if (isLiberty) {
                if (!scheduledFuture.isCancelled()) {
                    scheduledFuture.cancel(false);
                }
            } else {
                timer.cancel();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

And let’s create a child Scheduler

So we have our AbstractScheduler in place, let’s create a child object (called ChildScheduler), extending the abstract class and doing the dirty job IRL.

package test;

import java.util.Date;

public class ChildScheduler extends AbstractScheduler {
    private static final long serialVersionUID = 848658559170696979L;

    @Override
    public long getInterval() {
        return 1;
    }

    @Override
    public void run() {
        Date current = getCurrentDate();
        System.out.println(current);
    }

private Date getCurrentDate() {
return new Date();
}

}

Result

So, if you put those classes to a J2EE application, and install first on a Liberty profile, the code will use the ManagedScheduledExecutorService to schedule the getCurrentDate() method. If you install it to a full profile WAS, it will use the TimerManager to call the getCurrentDate() method.

So, I created a dummy servlet which starts the scheduler. The scheduler will iterate twice, and in the meantime, I paused the Thread for 90 seconds in total, and stop the scheduler. Please, this is an unsupported way to pause a thread, I used for just the demonstration. The log messages are coming from the server. 🙂

package test;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns = "/_test")
public class TestController extends HttpServlet {
    private static final long serialVersionUID = 8059837220012295812L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Starting child scheduler...");
        ChildScheduler cs = new ChildScheduler();
        cs.initialize();
        System.out.println("Scheduler started...");

        try {
            System.out.println("Get a new date after 1 minute, after second date, waiting 30 seconds...");
            Thread.sleep(90 * 1000);

            System.out.println("---------------------------------");
            System.out.println("Stopping scheduler...");
            cs.cancelTask();
            System.out.println("Scheduler stopped.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

WebSphere Liberty log output:

Starting child scheduler...
Scheduler started...
Tue May 16 22:09:41 CEST 2017
Waiting 90 seconds...
Tue May 16 22:10:41 CEST 2017
---------------------------------
Stopping scheduler...
Scheduler stopped.

And the same code on Full Profile:

[05/16/17 22:14:19:397 EST] 0000001d SystemOut O Starting child scheduler
[05/16/17 22:14:19:397 EST] 0000001d SystemOut O Scheduler started...
[05/16/17 22:14:19:399 EST] 0000001d SystemOut O Tue May 16 22:14:20 CEST 2017
[05/16/17 22:14:19:401 EST] 0000001d SystemOut O Waiting 90 seconds...
[05/16/17 22:14:20:398 EST] 0000076d SystemOut O Tue May 16 22:15:20 CEST 2017
[05/16/17 22:14:59:401 EST] 0000076d SystemOut O Stopping scheduler...
[05/16/17 22:14:59:401 EST] 0000076d SystemOut O Scheduler stopped.

I hope this will help you save some time. And better, more convinient code.

You may also like...