[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