[seam-commits] Seam SVN: r8871 - in trunk: doc/Seam_Reference_Guide/en-US and 1 other directories.
seam-commits at lists.jboss.org
seam-commits at lists.jboss.org
Mon Sep 1 17:43:28 EDT 2008
Author: pete.muir at jboss.org
Date: 2008-09-01 17:43:28 -0400 (Mon, 01 Sep 2008)
New Revision: 8871
Added:
trunk/src/main/org/jboss/seam/async/AsynchronousExceptionHandler.java
Modified:
trunk/doc/Seam_Reference_Guide/en-US/Jms.xml
trunk/seam21migration.txt
trunk/src/main/org/jboss/seam/async/Asynchronous.java
trunk/src/main/org/jboss/seam/async/AsynchronousEvent.java
trunk/src/main/org/jboss/seam/async/AsynchronousInvocation.java
trunk/src/main/org/jboss/seam/async/QuartzDispatcher.java
trunk/src/main/org/jboss/seam/async/ThreadPoolDispatcher.java
Log:
JBSEAM-2607, JBSEAM-2889, and make Future injectable
Modified: trunk/doc/Seam_Reference_Guide/en-US/Jms.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Jms.xml 2008-09-01 16:16:22 UTC (rev 8870)
+++ trunk/doc/Seam_Reference_Guide/en-US/Jms.xml 2008-09-01 21:43:28 UTC (rev 8871)
@@ -352,7 +352,63 @@
business process context is propagated to the asynchronous thread.
</para>
</sect2>
+
+ <sect2>
+ <title>Handling exceptions from asynchronous calls</title>
+
+ <para>
+ Each asynchronous dispatcher behaves differently when an
+ exception propagates through it. For example, the
+ <literal>java.util.concurrent</literal> dispatcher will suspend
+ further executions of a call which repeats, and the EJB3 timer
+ service will swallow the exception. Seam therefore catches any
+ exception which propagates out of the asynchronous call before
+ it reaches the dispatcher.
+ </para>
+
+ <para>
+ By default, any exception which propagates out from an
+ asynchronous execution will be caught and logged at error level.
+ You can customize this behavior globally by overriding the
+ <literal>org.jboss.seam.async.asynchronousExceptionHandler</literal>
+ component:
+ </para>
+
+ <programlisting role="JAVA"><![CDATA[@Scope(ScopeType.STATELESS)
+ at Name("org.jboss.seam.async.asynchronousExceptionHandler")
+public class MyAsynchronousExceptionHandler extends AsynchronousExceptionHandler {
+ @Logger Log log;
+
+ @In Future timer;
+
+ @Override
+ public void handleException(Exception exception) {
+ log.debug(exception);
+ timer.cancel(false);
+ }
+
+}]]></programlisting>
+
+ <para>
+ Here, for example, using <literal>java.util.concurrent</literal>
+ dispatcher, we inject it's control object and cancel all future
+ invocations when an exception is encountered
+ </para>
+
+ <para>
+ You can also alter this behavior for an individual component by
+ implementing the method
+ <literal>public void handleAsynchronousException(Exception exception);</literal>
+ on the component. For example:
+ </para>
+
+ <programlisting role="JAVA"><![CDATA[ public void handleAsynchronousException(Exception exception) {
+ log.fatal(exception);
+ }]]></programlisting>
+
+ </sect2>
+
</sect1>
<sect1>
Modified: trunk/seam21migration.txt
===================================================================
--- trunk/seam21migration.txt 2008-09-01 16:16:22 UTC (rev 8870)
+++ trunk/seam21migration.txt 2008-09-01 21:43:28 UTC (rev 8871)
@@ -119,3 +119,11 @@
<value>org.jboss.seam.persistence.ManagedEntityIdentityInterceptor</value>
</core:interceptors>
</core:init>
+
+
+Asynchronous Exception Handling
+----------------------
+
+All asynchronous are now wrapped in exception handling. By default, any
+exceptions which propagate out of the asynchronous call are caught and logged at
+error level. The reference manual describes how to customize this behaviour.
\ No newline at end of file
Modified: trunk/src/main/org/jboss/seam/async/Asynchronous.java
===================================================================
--- trunk/src/main/org/jboss/seam/async/Asynchronous.java 2008-09-01 16:16:22 UTC (rev 8870)
+++ trunk/src/main/org/jboss/seam/async/Asynchronous.java 2008-09-01 21:43:28 UTC (rev 8871)
@@ -19,62 +19,80 @@
*/
public abstract class Asynchronous implements Serializable
{
- private static final LogProvider log = Logging.getLogProvider(Asynchronous.class);
+
static final long serialVersionUID = -551286304424595765L;
+ private static final LogProvider log = Logging.getLogProvider(Asynchronous.class);
+
private Long processId;
private Long taskId;
- protected Asynchronous()
+ public Asynchronous()
{
if ( Init.instance().isJbpmInstalled() )
{
BusinessProcess businessProcess = BusinessProcess.instance();
processId = businessProcess.getProcessId();
taskId = BusinessProcess.instance().getTaskId();
- }
+ }
}
- public void execute(Object timer)
+ protected abstract class ContextualAsynchronousRequest
{
- boolean createContexts = !Contexts.isEventContextActive() && !Contexts.isApplicationContextActive();
- if (createContexts) Lifecycle.beginCall();
- Contexts.getEventContext().set(AbstractDispatcher.EXECUTING_ASYNCHRONOUS_CALL, true);
- try
+
+ private Object timer;
+ private boolean createContexts;
+
+ public ContextualAsynchronousRequest(Object timer)
{
- executeInContexts(timer);
+ this.timer = timer;
+ this.createContexts = !Contexts.isEventContextActive() && !Contexts.isApplicationContextActive();
}
- finally
- {
- Contexts.getEventContext().remove(AbstractDispatcher.EXECUTING_ASYNCHRONOUS_CALL);
- if (createContexts) Lifecycle.endCall();
- }
- }
-
- private void executeInContexts(Object timer)
- {
- if (taskId!=null)
+ private void setup()
{
- BusinessProcess.instance().resumeTask(taskId);
+ if (createContexts) Lifecycle.beginCall();
+ Contexts.getEventContext().set(AbstractDispatcher.EXECUTING_ASYNCHRONOUS_CALL, true);
+ if (taskId!=null)
+ {
+ BusinessProcess.instance().resumeTask(taskId);
+ }
+ else if (processId!=null)
+ {
+ BusinessProcess.instance().resumeProcess(processId);
+ }
+
+ if (timer!=null)
+ {
+ Contexts.getEventContext().set("timer", timer);
+ }
+
+ log.debug("executing: " + this);
}
- else if (processId!=null)
- {
- BusinessProcess.instance().resumeProcess(processId);
- }
- if (timer!=null)
+ protected abstract void process();
+
+ public void run()
{
- Contexts.getEventContext().set("timer", timer);
+ setup();
+ try
+ {
+ process();
+ }
+ finally
+ {
+ cleanup();
+ }
}
- if ( log.isDebugEnabled() )
+ private void cleanup()
{
- log.debug("executing: " + this);
+ Contexts.getEventContext().remove(AbstractDispatcher.EXECUTING_ASYNCHRONOUS_CALL);
+ if (createContexts) Lifecycle.endCall();
}
-
- call();
- }
+ }
- protected abstract void call();
+ public abstract void execute(Object timer);
+
+ protected abstract void handleException(Exception exception, Object timer);
}
\ No newline at end of file
Modified: trunk/src/main/org/jboss/seam/async/AsynchronousEvent.java
===================================================================
--- trunk/src/main/org/jboss/seam/async/AsynchronousEvent.java 2008-09-01 16:16:22 UTC (rev 8870)
+++ trunk/src/main/org/jboss/seam/async/AsynchronousEvent.java 2008-09-01 21:43:28 UTC (rev 8871)
@@ -22,9 +22,18 @@
}
@Override
- public void call()
+ public void execute(Object timer)
{
- Events.instance().raiseEvent(type, parameters);
+ new ContextualAsynchronousRequest(timer)
+ {
+
+ @Override
+ protected void process()
+ {
+ Events.instance().raiseEvent(type, parameters);
+ }
+
+ }.run();
}
@Override
@@ -37,5 +46,19 @@
{
return type;
}
+
+ @Override
+ protected void handleException(final Exception exception, Object timer)
+ {
+ new ContextualAsynchronousRequest(timer)
+ {
+ @Override
+ protected void process()
+ {
+ AsynchronousExceptionHandler.instance().handleException(exception);
+ }
+ }.run();
+
+ }
}
\ No newline at end of file
Added: trunk/src/main/org/jboss/seam/async/AsynchronousExceptionHandler.java
===================================================================
--- trunk/src/main/org/jboss/seam/async/AsynchronousExceptionHandler.java (rev 0)
+++ trunk/src/main/org/jboss/seam/async/AsynchronousExceptionHandler.java 2008-09-01 21:43:28 UTC (rev 8871)
@@ -0,0 +1,33 @@
+package org.jboss.seam.async;
+
+import static org.jboss.seam.annotations.Install.BUILT_IN;
+
+import org.jboss.seam.Component;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.Install;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+import org.jboss.seam.log.LogProvider;
+import org.jboss.seam.log.Logging;
+
+ at Scope(ScopeType.STATELESS)
+ at Name("org.jboss.seam.async.asynchronousExceptionHandler")
+ at Install(precedence=BUILT_IN)
+ at BypassInterceptors
+public class AsynchronousExceptionHandler
+{
+
+ private LogProvider log = Logging.getLogProvider(AsynchronousExceptionHandler.class);
+
+ public void handleException(Exception throwable)
+ {
+ log.error("Exeception thrown whilst executing asynchronous call", throwable);
+ }
+
+ public static AsynchronousExceptionHandler instance()
+ {
+ return (AsynchronousExceptionHandler) Component.getInstance(AsynchronousExceptionHandler.class);
+ }
+
+}
Property changes on: trunk/src/main/org/jboss/seam/async/AsynchronousExceptionHandler.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/src/main/org/jboss/seam/async/AsynchronousInvocation.java
===================================================================
--- trunk/src/main/org/jboss/seam/async/AsynchronousInvocation.java 2008-09-01 16:16:22 UTC (rev 8870)
+++ trunk/src/main/org/jboss/seam/async/AsynchronousInvocation.java 2008-09-01 21:43:28 UTC (rev 8871)
@@ -4,6 +4,8 @@
import org.jboss.seam.Component;
import org.jboss.seam.intercept.InvocationContext;
+import org.jboss.seam.log.LogProvider;
+import org.jboss.seam.log.Logging;
import org.jboss.seam.util.Reflections;
/**
@@ -16,6 +18,8 @@
{
static final long serialVersionUID = 7426196491669891310L;
+ private LogProvider log = Logging.getLogProvider(AsynchronousInvocation.class);
+
private String methodName;
private Class[] argTypes;
private Object[] args;
@@ -35,21 +39,31 @@
}
@Override
- protected void call()
+ public void execute(Object timer)
{
- Object target = Component.getInstance(componentName);
-
- Method method;
- try
+ new ContextualAsynchronousRequest(timer)
{
- method = target.getClass().getMethod(methodName, argTypes);
- }
- catch (NoSuchMethodException nsme)
- {
- throw new IllegalStateException(nsme);
- }
+
+ @Override
+ protected void process()
+ {
+ Object target = Component.getInstance(componentName);
+
+ Method method;
+ try
+ {
+ method = target.getClass().getMethod(methodName, argTypes);
+ }
+ catch (NoSuchMethodException nsme)
+ {
+ throw new IllegalStateException(nsme);
+ }
+
+ Reflections.invokeAndWrap(method, target, args);
+ }
+
+ }.run();
- Reflections.invokeAndWrap(method, target, args);
}
@Override
@@ -57,4 +71,28 @@
{
return "AsynchronousInvocation(" + componentName + '.' + methodName + "())";
}
+
+ @Override
+ protected void handleException(final Exception exception, Object timer)
+ {
+ new ContextualAsynchronousRequest(timer)
+ {
+ @Override
+ protected void process()
+ {
+ Object target = Component.getInstance(componentName);
+ try
+ {
+ Method method = target.getClass().getMethod("handleAsynchronousException", Exception.class);
+ log.trace("Using asynchronous exception handler " + componentName + ".handleAsynchronsException;");
+ method.invoke(target, exception);
+ }
+ catch (Exception e)
+ {
+ log.trace("Using default asynchronous exception handler");
+ AsynchronousExceptionHandler.instance().handleException(exception);
+ }
+ }
+ }.run();
+ }
}
\ No newline at end of file
Modified: trunk/src/main/org/jboss/seam/async/QuartzDispatcher.java
===================================================================
--- trunk/src/main/org/jboss/seam/async/QuartzDispatcher.java 2008-09-01 16:16:22 UTC (rev 8870)
+++ trunk/src/main/org/jboss/seam/async/QuartzDispatcher.java 2008-09-01 21:43:28 UTC (rev 8871)
@@ -237,7 +237,15 @@
{
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
async = (Asynchronous)dataMap.get("async");
- async.execute(new QuartzTriggerHandle(context.getTrigger().getName()));
+ QuartzTriggerHandle handle = new QuartzTriggerHandle(context.getTrigger().getName());
+ try
+ {
+ async.execute(handle);
+ }
+ catch (Exception e)
+ {
+ async.handleException(e, handle);
+ }
}
}
Modified: trunk/src/main/org/jboss/seam/async/ThreadPoolDispatcher.java
===================================================================
--- trunk/src/main/org/jboss/seam/async/ThreadPoolDispatcher.java 2008-09-01 16:16:22 UTC (rev 8870)
+++ trunk/src/main/org/jboss/seam/async/ThreadPoolDispatcher.java 2008-09-01 21:43:28 UTC (rev 8871)
@@ -34,7 +34,10 @@
public Future scheduleAsynchronousEvent(String type, Object... parameters)
{
- return executor.submit( new RunnableAsynchronous( new AsynchronousEvent(type, 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)
@@ -55,47 +58,50 @@
return expiration.getTime() - new Date().getTime();
}
- private Future scheduleWithExecutorService(TimerSchedule schedule, Runnable runnable)
+ private Future scheduleWithExecutorService(TimerSchedule schedule, RunnableAsynchronous runnable)
{
+ Future future = null;
if ( schedule.getIntervalDuration()!=null )
{
if ( schedule.getExpiration()!=null )
{
- return executor.scheduleAtFixedRate( runnable,
+ future = executor.scheduleAtFixedRate( runnable,
toDuration( schedule.getExpiration() ),
schedule.getIntervalDuration(),
TimeUnit.MILLISECONDS );
}
else if ( schedule.getDuration()!=null )
{
- return executor.scheduleAtFixedRate( runnable,
+ future = executor.scheduleAtFixedRate( runnable,
schedule.getDuration(),
schedule.getIntervalDuration(),
TimeUnit.MILLISECONDS );
}
else
{
- return executor.scheduleAtFixedRate( runnable, 0l,
+ future = executor.scheduleAtFixedRate( runnable, 0l,
schedule.getIntervalDuration(),
TimeUnit.MILLISECONDS );
}
}
else if ( schedule.getExpiration()!=null )
{
- return executor.schedule( runnable,
+ future = executor.schedule( runnable,
toDuration( schedule.getExpiration() ),
TimeUnit.MILLISECONDS );
}
else if ( schedule.getDuration()!=null )
{
- return executor.schedule( runnable,
+ future = executor.schedule( runnable,
schedule.getDuration(),
TimeUnit.MILLISECONDS );
}
else
{
- return executor.schedule(runnable, 0l, TimeUnit.MILLISECONDS);
+ future = executor.schedule(runnable, 0l, TimeUnit.MILLISECONDS);
}
+ runnable.setFuture(future);
+ return future;
}
@Destroy
@@ -116,6 +122,8 @@
{
private Asynchronous async;
+ private Future future;
+
RunnableAsynchronous(Asynchronous async)
{
this.async = async;
@@ -123,8 +131,21 @@
public void run()
{
- async.execute(null);
+ try
+ {
+ async.execute(future);
+ }
+ catch (Exception exception)
+ {
+ async.handleException(exception, future);
+ }
}
+
+ public void setFuture(Future future)
+ {
+ this.future = future;
+ }
+
}
public int getThreadPoolSize()
More information about the seam-commits
mailing list