September 26, 2013

How to Configure Scheduled Tasks in Wildfly

This example shows you how to create a scheduled concurrent task that runs every hour, using the Concurrency Utils API, and how to configure the properties of a ManagedScheduledExecutorService within Wildfly.

Example


Firstly, lets create the Runnable task which will define what work is to be done in the background thread.

public class ReportTask implements Runnable {
 
    public void run() {
        try {
            //do your background task
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            //handle execption
        }
    }
}

Now we schedule the executor to run its first execution immediately, and every hour thereafter. This executor will continue to run until you either terminate it, or until an exception it thrown by the task. We call the scheduledAtFixedRate() method, passing through our runnable task, along with the properties defining our schedule, i.e. the delay, the frequency and time unit.

@Stateless
public class ReportBean {
 
    @Resource(lookup="java:jboss/ee/concurrency/scheduler/MyScheduledExectutorService")
    private ManagedScheduledExecutorService executorService;

    public void runReports() {
        ReportTask reportTask = new ReportTask();
        executorService.scheduleAtFixedRate(reportTask, 0L, 1L, TimeUnit.HOURS);
    }
}

Lastly we need to configure our ManagedScheduledExecutorService in Wildfly.  The 'lookup' attribute of the @Resource annotation maps to 'jndi-name' attribute within the <managed-scheduled-executor-service> tag.

<subsystem xmlns="urn:jboss:domain:ee:2.0">
            <spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
            <jboss-descriptor-property-replacement>true</jboss-descriptor-property-replacement>
            <concurrent>
                <context-services>
                    <context-service name="default" jndi-name="java:jboss/ee/concurrency/context/default" use-transaction-setup-provider="true"/>
                </context-services>
                <managed-thread-factories>
                    <managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
                </managed-thread-factories>
                <managed-executor-services>
                    <managed-executor-service name="default" jndi-name="java:jboss/ee/concurrency/executor/default" context-service="default" hung-task-threshold="60000" core-threads="5" max-threads="25" keepalive-time="5000"/>
                </managed-executor-services>
                <managed-scheduled-executor-services>
                    <managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-threshold="60000" core-threads="2" keepalive-time="3000"/>
                    <managed-scheduled-executor-service name="myScheduledExecutorService" jndi-name="java:jboss/ee/concurrency/scheduler/MyScheduledExectutorService" hung-task-threshold="50000" core-threads="4" keepalive-time="5000" reject-policy="RETRY_ABORT"/>
                </managed-scheduled-executor-services>
            </concurrent>
            <default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/ExampleDS" jms-connection-factory="java:jboss/DefaultJMSConnectionFactory" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
</subsystem>

Note: If we were not to specify a any lookup or name attribute in the @Resource annotation, Wildfly would inject the default execution service.


Below is the full list of attributes you can use to configure your managed-scheduled-executor-service in Wildfly.
  • name - This is the name of the resource. This attribute is manditory.
  • jndi-name - The JNDI name for this resource. This attribute is manditory
  • context-service - The context service to use, if not supplied, the default context service is used.
  • thread-factory - The name of the thread factory, if not supplied, the default thread factory is used.
  • hung-task-threshold - how long, in milliseconds, to allow threads to run for before they are considered unresponsive. (default=0, 0=no limit, min=0)
  • long-running-tasks - Whether this is a short or long running thread. (default=false)
  • core-threads - The number of threads within the executors thread pool, including idle threads.  This attribute is mandatory. (min=0)
  • keepalive-time - How long threads can remain idle when the number of threads is greater than the core-thread size. This attribute is manditory. (min=0, default=60000)
  • reject-policy - How to handle a failed task. 'ABORT' will cause an exception to be thrown; 'RETRY_ABORT' which will cause a retry, and then an abort if the retry fails. (default=ABORT) 
To view your executor in the Wildfly Admin console, login, and navigate to Runtime > JNDI View > java:jboss > ee > concurrent.  Here, you can see all the concurrent resources including your newly configured scheduledexecutor.


Note: You will notice that I have used the JNDI name of java:jboss/ee/concurrency/scheduler/MyScheduledExectutorService, which at first glance looks excessively long. Using this path ensures that the resource shows under concurrency/scheduler list in the Wildfly admin console.



5 comments :

  1. May you tell us why do u use the WildFly why not the Glassfish ..?

    ReplyDelete
  2. Dear Chris , Using ManagedScheduledExecutorService .scheduledAtFixedRate() ... is it better than using the EJBTimer ???

    ReplyDelete
    Replies
    1. My understanding of the difference is this:

      An EJBTimer executes in one thread, so if it is a long running task, it may hold up other tasks, where as ManagedScheduledExecutorService can be configured to use any number of threads.

      With a ScheduledExecutorService if an exception is thrown, subsequent tasks will still run, but with an EJBTimer a runtime exception will kill the current task, and future tasks.

      If you find any more, I would be keen to know. :)

      Delete
  3. We are using Quartz Scheduler any Idea is to which one will be best to use Java Concurrency or Quartz. Is there any way they can co-exists. I would be doing research on that though but just incase you have looked into this already it will be a great help you throwing some light on it.

    ReplyDelete
    Replies
    1. When you use the ManagedScheduledExecutorService you are using the standard Java EE API. The Java EE 7 executor services also have access to new threads created and managed by the container (as per the Java EE 7 spec). AFAIK any threads created by the Quartz Scheduler will not be managed by the container (you will need to confirm this) meaning these threads may not have access to enterprise services like transactions and security.

      Personally I stick with the core Java EE API and then, if it is not sufficient, I look at enhancing/replacing it with 3rd party libraries. For your situation, I would say it depends on the code you are executing within your scheduler thread, and whether you are already using Quartz. If you are already using Quartz, is it worth the trouble of replacing it with the Concurrency API? As for using them together, I can not see why they would not both work in same code base (obviously in different places in the code). This would allow you to use the Concurrency Utils in new code, and keep existing code as is. Hope this helps.

      Delete