package org.jboss.seam.async; import static org.jboss.seam.annotations.Install.BUILT_IN; import java.util.Date; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.jboss.seam.Component; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Create; import org.jboss.seam.annotations.Destroy; import org.jboss.seam.annotations.Install; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.intercept.InvocationContext; /** * Dispatcher implementation that uses a java.util.concurrent * ScheduledThreadPoolExecutor. * * @author Gavin King * */ @Scope(ScopeType.APPLICATION) @Name("org.jboss.seam.async.dispatcher") @Install(precedence=BUILT_IN) public class ThreadPoolDispatcher extends AbstractDispatcher<Future, TimerSchedule> { private int threadPoolSize = 10; private ScheduledExecutorService executor; @Create public void startup() { executor = Executors.newScheduledThreadPool(threadPoolSize); } public Future scheduleAsynchronousEvent(String type, Object... parameters) { RunnableAsynchronous runnableAsynchronous = new RunnableAsynchronous( new AsynchronousEvent(type, parameters) ); Future future = executor.submit(runnableAsynchronous); runnableAsynchronous.setFuture(future); return future; } public Future scheduleTimedEvent(String type, TimerSchedule schedule, Object... parameters) { return scheduleWithExecutorService( schedule, new RunnableAsynchronous( new AsynchronousEvent(type, parameters) ) ); } public Future scheduleInvocation(InvocationContext invocation, Component component) { return scheduleWithExecutorService( createTimerSchedule(invocation), new RunnableAsynchronous( new AsynchronousInvocation(invocation, component) ) ); } private static long toDuration(Date expiration) { return expiration.getTime() - new Date().getTime(); } private Future scheduleWithExecutorService(TimerSchedule schedule, RunnableAsynchronous runnable) { Future future = null; if ( schedule.getIntervalDuration()!=null ) { if ( schedule.getExpiration()!=null ) { future = executor.scheduleAtFixedRate( runnable, toDuration( schedule.getExpiration() ), schedule.getIntervalDuration(), TimeUnit.MILLISECONDS ); } else if ( schedule.getDuration()!=null ) { future = executor.scheduleAtFixedRate( runnable, schedule.getDuration(), schedule.getIntervalDuration(), TimeUnit.MILLISECONDS ); } else { future = executor.scheduleAtFixedRate( runnable, 0l, schedule.getIntervalDuration(), TimeUnit.MILLISECONDS ); } } else if ( schedule.getExpiration()!=null ) { future = executor.schedule( runnable, toDuration( schedule.getExpiration() ), TimeUnit.MILLISECONDS ); } else if ( schedule.getDuration()!=null ) { future = executor.schedule( runnable, schedule.getDuration(), TimeUnit.MILLISECONDS ); } else { future = executor.schedule(runnable, 0l, TimeUnit.MILLISECONDS); } runnable.setFuture(future); return future; } @Destroy public void destroy() { executor.shutdown(); try { executor.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException ie) { } } static class RunnableAsynchronous implements Runnable { private Asynchronous async; private Future future; RunnableAsynchronous(Asynchronous async) { this.async = async; } public void run() { try { async.execute(future); } catch (Exception exception) { async.handleException(exception, future); } } public void setFuture(Future future) { this.future = future; } } public int getThreadPoolSize() { return threadPoolSize; } public void setThreadPoolSize(int threadPoolSize) { this.threadPoolSize = threadPoolSize; } }