Java-EE-compatible AbstractExecutorService Implementation

I had one of those should-have-seen-it-years-ago moments today.

I was reading through the EJB specification and was paying particular to one of my favorite sections, good ol’ section 21.2.2, which has a part in it that goes a little something like this:

An enterprise bean must not use thread synchronization primitives to synchronize execution of multiple instances, except if it is a Singleton session bean with bean-managed concurrency.

I have read this section probably, what, dozens of times, and have taken away from it that in general a session bean shouldn’t use the synchronized keyword, or various lock classes.  Then usually I move on.

Today I paused for a moment, and I’m glad I did.  What I realized—which is probably obvious to all of you—is that this does not say that a session bean cannot use the synchronized keyword.  It says that it cannot use the synchronized keyword to synchronize its own innards, because this might prevent the container from spreading calls out to bean instances across multiple hosts.

For probably years I’ve been studiously careful to avoid synchronization primitives or libraries that use them inside my various session beans, because I was just blindly following what I thought the specification said.  Turns out I was wrong.

So I turned around and immediately wrote this, which I’ve needed for eons:

@Local({Executor.class, ExecutorService.class})
@Stateless(name = “ExecutorService”)
public class ExecutorServiceBean extends AbstractExecutorService implements ExecutorService {

  @Resource
  private SessionContext sessionContext;

  @Override
  public void execute(final Runnable runnable) {
    if (runnable != null) {
      runnable.run();
    }
  }

  @Override
  public <T> Future<T> submit(final Callable<T> callable) {
    RunnableFuture<T> returnValue = null;
    if (this.sessionContext == null || !this.sessionContext.wasCancelCalled()) {
      returnValue = this.newTaskFor(callable);
      returnValue.run();
    }
    return returnValue;
  }

  @Override
  public boolean awaitTermination(final long timeout, final TimeUnit unit) {
    return false;
  }

  @Override
  public boolean isTerminated() {
    return false;
  }

  @Override
  public boolean isShutdown() {
    return false;
  }

  @Override
  public void shutdown() {

  }

  @Override
  public List<Runnable> shutdownNow() {
    return Collections.emptyList();
  }

}

It’s an AbstractExecutorService that is implemented as a stateless session bean itself via the @Asynchronous annotation.  The use of synchronized is confined to the AbstractExecutorService‘s underlying CompletionService, which is based around a LinkedBlockingQueue.  You can’t shut it down or terminate it, obviously, and less obviously you can’t portably keep track of the tasks in flight (although I’m thinking that either an @ApplicationScoped list of such tasks or a @SessionScoped list of such tasks could be @Injected here to do so) but short of that it should honor the contract.  Throw tomatoes.
Advertisements

5 thoughts on “Java-EE-compatible AbstractExecutorService Implementation

  1. ALRubinger

    Well, I’ll give you points for creativity.Unfortunately, what you’ve done here is re-interpret the directive based on the given reason why: "Synchronization would not work if the EJB container distributedenterprise bean???s instances across multiple JVMs." Unfortunately, there are other reasons. The rule itself is: "don’t use synchronization primitives." Period. No "synchronized", no "volatile", no "Object.wait()/notifty()", no "new Thread().run()", and no higher-level components which encapsulate the above. Any code path that leads to thread mucking is really disallowed (except for BMC Singleton).When you opt into a container, you surrender the runtime environment. Threading, scheduling, pooling…these are all aspects of the runtime. And by digging into these on your own, you create the potential to lock up Threads or keep the JVM from shutting down, because now you might have non-daemon Threads in process which the container has no idea about. What WOULD be a decent solution, IMO, is if you created an ExecutorService and injected a backing Thread pool to be supplied by the container. Or injected an ExecutorService to be supplied by the container (these are not spec options, but for instance JBossAS has jboss-threads which can supply this).That said, what I’ve outlined above is a strict interpretation of the specification. If you are aware of the consequences of going outside the container sandbox, … just make sure you test it and it could end up working just fine.S,ALR

    Reply
  2. Anonymous

    Before I go further, let me say I believe you.However, what was interesting to me???and what motivated this whole thing???was the actual wording of the specification itself. The specification does NOT say: don’t use the synchronized keyword, or libraries that make use of it. That would be completely unambiguous. Instead, it adds a qualifier. Your statement of it is exactly what I thought it said as well, but then I actually read it. That qualifier???"to synchronize execution of multiple instances"???seems important to me. I guess what you’re saying is that the qualifier is meaningless (i.e. this is just rather crummy technical writing).(In any event redoing this as a @Singleton with BMC would be effortless and would give up nothing, right? I’ll do that.)

    Reply
  3. Anonymous

    Oh, and I should mention just for completeness that I never quoted the "reason". I was well aware that that was only one reason among possibly many. What I did do was quote the qualifier which presumably served to illustrate just what uses of the synchronized keyword (and volatile, etc.) were prohibited. Anyhow, thanks for chiming in.

    Reply
  4. Anonymous

    Actually, one last thing while I’m thinking about it. I’m NOT going outside the container, and I DON’T have "non-daemon Threads in process which the container has no idea about"???the whole point is that the threads that are being launched are those which the container is launching.The only use of the synchronization primitives are inside the bowels of the LinkedBlockingQueue that is used (eventually) by the AbstractExecutorService implementation of invokeAny().In any event, I’m prepared to trust Doug Lea’s code to not lock up anything. 🙂

    Reply
  5. ALRubinger

    You make a decent point; by extending AbstractExecutorService instead of, say, ThreadPoolExecutor, all Threads are created and managed by the container in by way of the @Asynchronous call. This is actually a really interesting approach. I still hold that it’s prohibited by spec, but I’m now failing to see the potential for harm here.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s