JBoss AS7.1 and RESTEasy: upgrading Jackson
Door Avisi / mei 2013 / 1 Min
Door Avisi / / 1 min
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.");
}
}
| Software Development
Door Avisi / okt 2024
Dan denken we dat dit ook wat voor jou is.