Here is another take on my previous post—this time using a @Singleton EJB with bean-managed concurrency. First, I introduce a valid interface, but one which is essentially identical to Executor, with the added restriction that execution must be asynchronous (while this is a common thing for “regular” Executors to do, it is not strictly speaking required of them). I do this for the essential bits of @Asynchronous dispatching (once you see that code below you’ll see why). While you could certainly use this as a business interface, I wouldn’t really expect you to. Here’s the AsynchronousExecutor interface:
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)@Local({Executor.class, ExecutorService.class, AsynchronousExecutor.class})@Singletonpublic class ExecutorServiceBean extends AbstractExecutorService implements ExecutorService, AsynchronousExecutor {@Resourceprivate SessionContext sessionContext;@Overridepublic void execute(final Runnable runnable) {if (runnable != null) {if (this.sessionContext != null) {final AsynchronousExecutor self = this.sessionContext.getBusinessObject(AsynchronousExecutor.class);assert self != null;self.executeAsynchronously(runnable);} else {runnable.run();}}}@Asynchronous@Overridepublic void executeAsynchronously(final Runnable runnable) {if (runnable != null) {runnable.run();}}@Overridepublic boolean awaitTermination(final long timeout, final TimeUnit unit) {return false;}@Overridepublic boolean isTerminated() {return false;}@Overridepublic boolean isShutdown() {return false;}@Overridepublic void shutdown() {}@Overridepublic List<Runnable> shutdownNow() {return Collections.emptyList();}}
- First, I took advantage of the fact that
AbstractExecutorService actually arranges all the work for you such that really all you have to override is the Executor#execute(Runnable) method. Specifically, I no longer override the AbstractExecutorService#submit(Callable) method. - Second, I made sure that the invocation of the execute(Runnable) method is not itself declared to be @Asynchronous, but delegates this call to the executeAsynchronously(Runnable) method, which is declared to be @Asynchronous. This is because the innards of the AbstractExecutorService class call the execute(Runnable) method directly. Since the asynchronous behavior of an @Asynchronous-annotated method can only be accomplished if the control flow progresses through the container, this cumbersome construct is necessary. One big assumption I made here is that access to the SessionContext object is already properly synchronized by the container, so I performed no additional locking here. I didn’t find anything in the EJB specification to support or contradict this assumption.
- The bean is now a @Singleton with bean-managed concurrency. Even though it has bean-managed concurrency, there are no explicit synchronization primitives anywhere, because they are locked up (ha ha) inside the AbstractExecutorService innards, which do whatever they do, in disturbingly precise and awesome Doug Lea fashion.
- The bean’s set of @Local business interfaces now includes AsynchronousExecutor.class, so that the container dispatch by way of SessionContext#getBusinessObject(Class) can work. Like I said earlier, you could use this as a business interface, but it probably would be excessively narrow.