EJB 3.1: Concurrency Management and (Avoiding) Synchronization

At Avisi we use a custom built EJB 3 based application for scheduling and running (automated) regression tests. This involves a queue from which objects are taken. These objects contain metadata describing the tests to execute. I won’t go into detail as to why we aren’t using Apache ActiveMQ (or a similar library) for this purpose, but I can say that we didn’t need distributed test-executing minions at that time.

Keeping KISS in mind (the principle, not the band), the model for this queue-and-execute feature looks something like this:

The Scheduler is responsible for polling the queue and instructing the Executor to run specific tests. Both tasks are implemented in the Executor for easier queue synchronization. Since the execution of a test is a rather long, we don’t want to wait for completion (that wouldn’t allow us to run tests in parallel). To parallelize the ‘execute(test)‘ method, we use the @Asynchronous annotation introduced in EJB 3.1. Since the nature of our Scheduler is… well, a scheduler, we want it to perform its task about every second. This is achieved using the @Schedule annotation (see the example below). Don’t forget to annotate both classes with @Singleton.

import javax.ejb.*;
import java.util.logging.Logger;

@Singleton
public class Scheduler {
    private final Logger log = Logger.getLogger(getClass().getName());

    @EJB
    private QueueExecutor queueExecutor;

    @Schedule(minute = "*", second = "*", hour = "*", persistent = false)
    public void tick() throws Exception {
        log.info("calling poll()");
        queueExecutor.poll();
        log.info("calling execute()");
        queueExecutor.execute();
    }
}

import javax.ejb.*;
import java.util.logging.Logger;

@Singleton
public class QueueExecutor {
    private final Logger log = Logger.getLogger(getClass().getName());

    public void poll() {
        log.info("poll()");
    }

    @Asynchronous
    public void execute() throws Exception {
        log.info("Execution starting...");

        // Perform some lengthy task
        Thread.sleep(5000);

        log.info("Execution has ended.");
    }
}

All set! Now we’re ready to run those tests in parallel! But while actually deploying these EJB’s, it become apparent that there’s nothing parallel about our setup at all. The problem is in fact that ‘poll( )’ isn’t called before ‘execute(test)’ has completed its task (invoked by the previous scheduler iteration). The issue is that session beans are thread-safe and so are singleton session beans. So, access to both ‘poll( )’ and ‘execute(test)’ is synchronized on class-level, which means our observation that ’poll( )’ isn’t called before ’execute(test)’ finishes, is correct.

Bean synchronization can be controlled using the @ConcurrenyManagement annotation, which defaults to ‘CONTAINER’. The other option is ‘BEAN’, which means the ‘bean developer is responsible for managing concurrent access to the bean instance‘.

Thus, the solution to our not-so-concurrent problem is simply setting @ConcurrenyManagement type to ‘BEAN’:

import javax.ejb.*;
import java.util.logging.Logger;

@Singleton
@ConcurrencyManagement(value = ConcurrencyManagementType.BEAN)
public class QueueExecutor {
    private final Logger log = Logger.getLogger(getClass().getName());

    public void poll() {
        log.info("poll()");
    }

    @Asynchronous
    public void execute() throws Exception {
        log.info("Execution starting...");

        // Perform some lengthy task
        Thread.sleep(5000);

        log.info("Execution has ended.");
    }
}
If you deploy this to an EJB 3.1 enabled container (like Glassfish v3.1.1, in our case), you will be able to run tests in parallel (just don’t forget to check up on your container’s thread pool).
Of course, another way to solve the problem is by separating responsibilities further, which would mean our Executor wouldn’t be in charge of managing the queue in the first place.
If you have experience running parallel tasks in EJB’s or if you have questions, please use the comment section.
  • Facebook
  • LinkedIn

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>