[seam-commits] Seam SVN: r10631 - in modules/trunk: bpm and 7 other directories.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Fri Apr 24 19:24:15 EDT 2009


Author: shane.bryzak at jboss.com
Date: 2009-04-24 19:24:14 -0400 (Fri, 24 Apr 2009)
New Revision: 10631

Added:
   modules/trunk/bpm/
   modules/trunk/bpm/pom.xml
   modules/trunk/bpm/src/
   modules/trunk/bpm/src/main/
   modules/trunk/bpm/src/main/java/
   modules/trunk/bpm/src/main/java/org/
   modules/trunk/bpm/src/main/java/org/jboss/
   modules/trunk/bpm/src/main/java/org/jboss/seam/
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/Actor.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/BusinessProcess.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/BusinessProcessInterceptor.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/Jbpm.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/JbpmELResolver.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/ManagedJbpmContext.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PageflowDeploymentHandler.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PageflowParser.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PooledTask.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PooledTaskInstanceList.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/ProcessInstance.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/ProcessInstanceFinder.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/SeamExpressionEvaluator.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/SeamUserCodeInterceptor.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstance.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstanceList.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstanceListForType.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstancePriorityList.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/Transition.java
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/jbpm.pageflow.cfg.xml
   modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/package-info.java
Log:
initial import of bpm module

Added: modules/trunk/bpm/pom.xml
===================================================================
--- modules/trunk/bpm/pom.xml	                        (rev 0)
+++ modules/trunk/bpm/pom.xml	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,32 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" 
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">   
+   <parent>
+      <artifactId>seam-parent</artifactId>
+      <groupId>org.jboss.seam</groupId>      
+      <version>3.0.0-SNAPSHOT</version>
+   </parent>    
+  
+   <modelVersion>4.0.0</modelVersion>   
+   <groupId>org.jboss.seam</groupId>
+   <artifactId>seam-bpm</artifactId>
+   <packaging>jar</packaging>
+   <version>3.0.0-SNAPSHOT</version>
+   <name>Seam BPM</name>
+         
+   <dependencies>
+      <dependency>
+         <groupId>org.jboss.webbeans</groupId>
+         <artifactId>jsr299-api</artifactId>
+      </dependency>              
+      <dependency>
+         <groupId>org.jboss.webbeans</groupId>
+         <artifactId>webbeans-logging</artifactId>
+      </dependency>
+      <dependency>
+         <groupId>org.jbpm</groupId>
+         <artifactId>jbpm-jpdl</artifactId>
+      </dependency>      
+   </dependencies>
+
+</project>

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/Actor.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/Actor.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/Actor.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,105 @@
+package org.jboss.seam.bpm;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.annotation.Named;
+import javax.context.SessionScoped;
+
+/**
+ * Allows the application to specify the jBPM actorId
+ * during the login cycle.
+ * 
+ * @author Gavin King
+ */
+ at Named
+ at SessionScoped
+public class Actor extends AbstractMutable implements Serializable
+{
+   private static final long serialVersionUID = -6515302276074415520L;
+   private String id;
+   private Set<String> groupActorIds = new HashSet<String>()
+   {
+      @Override
+      public boolean add(String o)
+      {
+         boolean dirty = super.add(o);
+         if (dirty) setDirty();
+         return dirty;
+      }
+      
+      @Override
+      public void clear()
+      {
+         if (size() > 0) setDirty();
+         super.clear();
+      }
+      
+      @Override
+      public boolean remove(Object o)
+      {
+         boolean dirty = super.remove(o);
+         if (dirty) setDirty();
+         return dirty;
+      }
+      
+      @Override
+      public Iterator<String> iterator()
+      {
+         final Iterator<String> it = super.iterator();
+         return new Iterator<String>()
+         {
+
+            public boolean hasNext()
+            {
+               return it.hasNext();
+            }
+
+            public String next()
+            {
+               return it.next();
+            }
+
+            public void remove()
+            {
+               setDirty();
+               it.remove();
+            }
+            
+         };
+      }
+   };
+   
+   public String getId() 
+   {
+      return id;
+   }
+   public void setId(String id) 
+   {
+      setDirty(this.id, id);
+      this.id = id;
+   }
+ 
+   public Set<String> getGroupActorIds()
+   {
+      return groupActorIds;
+   }
+   public static Actor instance()
+   {
+      if ( !Contexts.isSessionContextActive() )
+      {
+         throw new IllegalStateException("No active session context");
+      }
+      return (Actor) Component.getInstance(Actor.class);
+   }
+   
+   @Override
+   public String toString()
+   {
+      return "Actor(" + id + ")";
+   }
+}
+
+               
\ No newline at end of file

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/BusinessProcess.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/BusinessProcess.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/BusinessProcess.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,421 @@
+package org.jboss.seam.bpm;
+
+import java.io.Serializable;
+
+import javax.annotation.Named;
+import javax.context.ConversationScoped;
+
+import org.jboss.seam.international.StatusMessage;
+import org.jboss.seam.international.StatusMessages;
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.graph.exe.ProcessInstance;
+import org.jbpm.taskmgmt.exe.TaskInstance;
+
+/**
+ * Holds the task and process ids for the current conversation,
+ * and provides programmatic control over the business process.
+ * 
+ * @author Gavin King
+ *
+ */
+ at Named
+ at ConversationScoped
+public class BusinessProcess extends AbstractMutable implements Serializable 
+{
+   private static final long serialVersionUID = 4722350870845851070L;
+   
+   private Long processId;
+   private Long taskId;
+   
+   public static BusinessProcess instance()
+   {
+      if ( !Contexts.isConversationContextActive() )
+      {
+         throw new IllegalStateException("No active conversation context");
+      }
+      return (BusinessProcess) Component.getInstance(BusinessProcess.class, ScopeType.CONVERSATION);
+   }
+   
+   /**
+    * Is there a process instance associated with 
+    * the current conversation?
+    */
+   public boolean hasCurrentProcess()
+   {
+      return processId!=null;
+   }
+   
+   /**
+    * Is there a process instance that has not ended 
+    * associated with the current conversation?
+    */
+   public boolean hasActiveProcess()
+   {
+      return hasCurrentProcess() && 
+            !org.jboss.seam.bpm.ProcessInstance.instance().hasEnded();
+   }
+   
+   /**
+    * Is there a task instance associated with 
+    * the current conversation?
+    */
+   public boolean hasCurrentTask()
+   {
+      return taskId!=null;
+   }
+   
+   /**
+    * The jBPM process instance id associated with
+    * the current conversation.
+    */
+   public Long getProcessId() 
+   {
+      return processId;
+   }
+   
+   /**
+    * Set the process instance id, without validating
+    * that the process instance actually exists.
+    */
+   public void setProcessId(Long processId) 
+   {
+      setDirty(this.processId, processId);
+      this.processId = processId;
+   }
+   
+   /**
+    * The jBPM task instance id associated with
+    * the current conversation.
+    */
+   public Long getTaskId() 
+   {
+      return taskId;
+   }
+   
+   /**
+    * Set the task instance id, without validating
+    * that the task instance actually exists.
+    */
+   public void setTaskId(Long taskId) 
+   {
+      setDirty(this.taskId, taskId);
+      this.taskId = taskId;
+   }
+   
+   /**
+    * Create a process instance and associate it with the
+    * current conversation.
+    * 
+    * @param processDefinitionName the jBPM process definition name
+    */
+   public void createProcess(String processDefinitionName)
+   {
+       createProcess(processDefinitionName, true);
+   }
+
+   /**
+    * Create a process instance and associate it with the
+    * current conversation.
+    * 
+    * @param processDefinitionName the jBPM process definition name
+    */
+   public void createProcess(String processDefinitionName, boolean shouldSignalProcess)
+   {
+       ProcessInstance process = ManagedJbpmContext.instance().newProcessInstanceForUpdate(processDefinitionName);
+       afterCreateProcess(processDefinitionName, process, shouldSignalProcess);
+   }
+   
+   /**
+    * Create a process instance and associate it with the
+    * current conversation.
+    * 
+    * @param processDefinitionName the jBPM process definition name
+    * @param businessKey the business key of the new process definition
+    */
+   public void createProcess(String processDefinitionName, String businessKey)
+   {
+      /*ProcessInstance process = ManagedJbpmContext.instance().getGraphSession()
+               .findLatestProcessDefinition(processDefinitionName)
+               .createProcessInstance(Collections.EMPTY_MAP, businessKey);*/
+      ProcessInstance process = ManagedJbpmContext.instance().newProcessInstanceForUpdate(processDefinitionName);
+      process.setKey(businessKey);
+      afterCreateProcess(processDefinitionName, process, true);
+   }
+   
+   private void afterCreateProcess(String processDefinitionName, ProcessInstance process, boolean shouldSignalProcess)
+   {
+      setProcessId( process.getId() );
+      // need to set process variables before the signal
+      Contexts.getBusinessProcessContext().flush();
+      if (shouldSignalProcess) {
+          process.signal();
+      }
+      
+      Events.instance().raiseEvent("org.jboss.seam.createProcess." + processDefinitionName);
+   }
+   
+   /**
+    * Start the current task, using the current actor id
+    * 
+    * @see Actor
+    */
+   public void startTask()
+   {
+      String actorId = Actor.instance().getId();
+      TaskInstance task = org.jboss.seam.bpm.TaskInstance.instance();
+      if ( actorId != null )
+      {
+         task.start(actorId);
+      }
+      else
+      {
+         task.start();
+      }
+      
+      Events.instance().raiseEvent("org.jboss.seam.startTask." + task.getTask().getName());
+   }
+   
+   /**
+    * End the current task, via the given transition. If no transition name 
+    * is given, check the Transition component for a transition, or use the 
+    * default transition.
+    * 
+    * @param transitionName the jBPM transition name, or null
+    */
+   public void endTask(String transitionName)
+   {
+      TaskInstance task = org.jboss.seam.bpm.TaskInstance.instance();
+      if (task==null)
+      {
+         throw new IllegalStateException( "no task instance associated with context" );
+      }
+      
+      if ( transitionName==null || "".equals(transitionName) )
+      {
+         transitionName = Transition.instance().getName();
+      }
+      
+      if (transitionName==null)
+      {
+         task.end();
+      }
+      else
+      {
+         task.end(transitionName);
+      }
+      
+      setTaskId(null); //TODO: do I really need this???!
+      
+      Events.instance().raiseEvent("org.jboss.seam.endTask." + task.getTask().getName());
+      ProcessInstance process = org.jboss.seam.bpm.ProcessInstance.instance();
+      if ( process.hasEnded() )
+      {
+         Events.instance().raiseEvent("org.jboss.seam.endProcess." + process.getProcessDefinition().getName());
+      }
+   }
+   
+   /**
+    * Signal the given transition for the current process instance.
+    * 
+    * @param transitionName the jBPM transition name 
+    */
+   public void transition(String transitionName)
+   {
+      ProcessInstance process = org.jboss.seam.bpm.ProcessInstance.instance();
+      process.signal(transitionName);
+      if ( process.hasEnded() )
+      {
+         Events.instance().raiseEvent("org.jboss.seam.endProcess." + process.getProcessDefinition().getName());
+      }
+   }
+   
+   /**
+    * Associate the task instance with the given id with the current
+    * conversation.
+    * 
+    * @param taskId the jBPM task instance id
+    * @return true if the task was found and was not ended
+    */
+   public boolean resumeTask(Long taskId)
+   {
+      setTaskId(taskId);
+      TaskInstance task = org.jboss.seam.bpm.TaskInstance.instance();
+      if (task==null)
+      {
+         taskNotFound(taskId);
+         return false;
+      }
+      else if ( task.hasEnded() )
+      {
+         taskEnded(taskId);
+         return false;
+      }
+      else
+      {
+         setProcessId( task.getTaskMgmtInstance().getProcessInstance().getId() );
+         Events.instance().raiseEvent("org.jboss.seam.initTask." + task.getTask().getName());
+         return true;
+      }
+   }
+   
+   /**
+    * Associate the process instance with the given id with the 
+    * current conversation.
+    * 
+    * @param processId the jBPM process instance id
+    * @return true if the process was found and was not ended
+    */
+   public boolean resumeProcess(Long processId)
+   {
+      setProcessId(processId);
+      ProcessInstance process = org.jboss.seam.bpm.ProcessInstance.instance();
+      return afterResumeProcess(processId, process);
+   }
+   
+   /**
+    * Associate the process instance with the given business key 
+    * with the current conversation.
+    * 
+    * @param processDefinition the jBPM process definition name
+    * @param key the jBPM process instance key
+    * @return true if the process was found and was not ended
+    */
+   public boolean resumeProcess(String processDefinition, String key)
+   {
+      ProcessDefinition definition = ManagedJbpmContext.instance().getGraphSession().findLatestProcessDefinition(processDefinition);
+      ProcessInstance process = definition==null ? 
+               null : ManagedJbpmContext.instance().getProcessInstanceForUpdate(definition, key);
+      if (process!=null) setProcessId( process.getId() );
+      return afterResumeProcess(key, process);
+   }
+
+   private boolean afterResumeProcess(long processId, ProcessInstance process)
+   {
+      if ( process==null )
+      {
+         processNotFound(processId);
+         return false;
+      }
+      else if ( process.hasEnded() )
+      {
+         processEnded(processId);
+         return false;
+      }
+      else
+      {
+         Events.instance().raiseEvent("org.jboss.seam.initProcess." + process.getProcessDefinition().getName());
+         return true;
+      }
+   }
+   
+   private boolean afterResumeProcess(String processKey, ProcessInstance process)
+   {
+      if ( process==null )
+      {
+         processNotFound(processKey);
+         return false;
+      }
+      else if ( process.hasEnded() )
+      {
+         processEnded(processKey);
+         return false;
+      }
+      else
+      {
+         Events.instance().raiseEvent("org.jboss.seam.initProcess." + process.getProcessDefinition().getName());
+         return true;
+      }
+   }
+   
+   /**
+    * Check that the task currently associated with the conversation
+    * exists and has not ended.
+    * 
+    * @return true if the task exists and was not ended
+    */
+   public boolean validateTask()
+   {
+      if ( !hasCurrentTask() )
+      {
+         taskNotFound(taskId);
+         return false;
+      }
+      else if ( org.jboss.seam.bpm.TaskInstance.instance().hasEnded() )
+      {
+         taskEnded(taskId);
+         return false;
+      }
+      else
+      {
+         return true;
+      }
+   }
+   
+   protected void taskNotFound(Long taskId)
+   {
+      StatusMessages.instance().addFromResourceBundleOrDefault(
+            StatusMessage.Severity.WARN, 
+            "org.jboss.seam.TaskNotFound", 
+            "Task #0 not found", 
+            taskId
+         );
+   }
+   
+   protected void taskEnded(Long taskId)
+   {
+      StatusMessages.instance().addFromResourceBundleOrDefault(
+            StatusMessage.Severity.WARN, 
+            "org.jboss.seam.TaskEnded", 
+            "Task #0 already ended", 
+            taskId
+         );
+   }
+   
+   protected void processEnded(Long processId)
+   {
+      StatusMessages.instance().addFromResourceBundleOrDefault(
+            StatusMessage.Severity.WARN, 
+            "org.jboss.seam.ProcessEnded", 
+            "Process #0 already ended", 
+            processId
+         );
+   }
+   
+   protected void processNotFound(Long processId)
+   {
+      StatusMessages.instance().addFromResourceBundleOrDefault(
+            StatusMessage.Severity.WARN, 
+            "org.jboss.seam.ProcessNotFound", 
+            "Process #0 not found", 
+            processId
+         );
+   }
+   
+   protected void processEnded(String key)
+   {
+      StatusMessages.instance().addFromResourceBundleOrDefault(
+            StatusMessage.Severity.WARN, 
+            "org.jboss.seam.ProcessEnded", 
+            "Process #0 already ended", 
+            key
+         );
+   }
+   
+   protected void processNotFound(String key)
+   {
+      StatusMessages.instance().addFromResourceBundleOrDefault(
+            StatusMessage.Severity.WARN, 
+            "org.jboss.seam.ProcessNotFound", 
+            "Process #0 not found", 
+            key
+         );
+   }
+
+   
+   @Override
+   public String toString()
+   {
+      return "BusinessProcess(processId=" + processId + ",taskId=" + taskId + ")";
+   }
+   
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/BusinessProcessInterceptor.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/BusinessProcessInterceptor.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/BusinessProcessInterceptor.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,182 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.bpm;
+
+import java.lang.reflect.Method;
+
+import org.jboss.seam.annotations.bpm.BeginTask;
+import org.jboss.seam.annotations.bpm.CreateProcess;
+import org.jboss.seam.annotations.bpm.EndTask;
+import org.jboss.seam.annotations.bpm.ResumeProcess;
+import org.jboss.seam.annotations.bpm.StartTask;
+import org.jboss.seam.annotations.intercept.AroundInvoke;
+import org.jboss.seam.annotations.intercept.Interceptor;
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.core.BijectionInterceptor;
+import org.jboss.seam.core.Expressions;
+import org.jboss.seam.core.Init;
+import org.jboss.seam.intercept.AbstractInterceptor;
+import org.jboss.seam.intercept.InvocationContext;
+import org.jboss.seam.log.LogProvider;
+import org.jboss.seam.log.Logging;
+import org.jboss.seam.util.Strings;
+import org.jboss.seam.web.Parameters;
+
+/**
+ * Implements annotation-based business-process demarcation.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole</a>
+ * @author Gavin King
+ */
+ at Interceptor(stateless=true, around=BijectionInterceptor.class)
+public class BusinessProcessInterceptor extends AbstractInterceptor
+{
+   private static final long serialVersionUID = 758197867958840918L;
+   
+   private static final LogProvider log = Logging.getLogProvider( BusinessProcessInterceptor.class );
+
+   @AroundInvoke
+   public Object aroundInvoke(InvocationContext invocation) throws Exception
+   {
+      if ( !beforeInvocation(invocation) )
+      {
+         return null;
+      }
+      else
+      {
+         return afterInvocation( invocation, invocation.proceed() );
+      }
+   }
+
+   private boolean beforeInvocation(InvocationContext invocationContext) 
+   {
+      Method method = invocationContext.getMethod();
+      if ( method.isAnnotationPresent(StartTask.class) ) 
+      {
+         log.trace( "encountered @StartTask" );
+         StartTask tag = method.getAnnotation(StartTask.class);
+         Long taskId = getProcessOrTaskId( tag.taskIdParameter(), tag.taskId() );
+         return BusinessProcess.instance().resumeTask(taskId);
+      }
+      else if ( method.isAnnotationPresent(BeginTask.class) ) 
+      {
+         log.trace( "encountered @BeginTask" );
+         BeginTask tag = method.getAnnotation(BeginTask.class);
+         Long taskId = getProcessOrTaskId( tag.taskIdParameter(), tag.taskId() );
+         return BusinessProcess.instance().resumeTask(taskId);
+      }
+      else if ( method.isAnnotationPresent(ResumeProcess.class) ) 
+      {
+         log.trace( "encountered @ResumeProcess" );
+         ResumeProcess tag = method.getAnnotation(ResumeProcess.class);
+         if ( tag.processKey().equals("") )
+         {
+            Long processId = getProcessOrTaskId( tag.processIdParameter(), tag.processId() );
+            return BusinessProcess.instance().resumeProcess(processId);
+         }
+         else
+         {
+            return BusinessProcess.instance().resumeProcess( tag.definition(), getProcessKey( tag.processKey() ) );
+         }
+      }
+      if ( method.isAnnotationPresent(EndTask.class) )
+      {
+         log.trace( "encountered @EndTask" );
+         return BusinessProcess.instance().validateTask();
+      }
+      else
+      {
+         return true;
+      }
+   }
+
+   private Object afterInvocation(InvocationContext invocation, Object result)
+   {
+      Method method = invocation.getMethod();
+      if ( result!=null || method.getReturnType().equals(void.class) ) //interpreted as "redisplay"
+      {
+         if ( method.isAnnotationPresent(CreateProcess.class) )
+         {
+            log.trace( "encountered @CreateProcess" );
+            CreateProcess tag = method.getAnnotation(CreateProcess.class);
+            if ( tag.processKey().equals("") )
+            {
+               BusinessProcess.instance().createProcess( tag.definition() );
+            }
+            else
+            {
+               BusinessProcess.instance().createProcess( tag.definition(), getProcessKey( tag.processKey() ) );
+            }
+         }
+         if ( method.isAnnotationPresent(StartTask.class) )
+         {
+            log.trace( "encountered @StartTask" );
+            BusinessProcess.instance().startTask();
+         }
+         if ( method.isAnnotationPresent(EndTask.class) )
+         {
+            log.trace( "encountered @EndTask" );
+            BusinessProcess.instance().endTask( method.getAnnotation(EndTask.class).transition() );
+         }
+         if ( method.isAnnotationPresent(org.jboss.seam.annotations.bpm.Transition.class) )
+         {
+            log.trace( "encountered @Transition" );
+            String transitionName = method.getAnnotation(org.jboss.seam.annotations.bpm.Transition.class).value();
+            if ( "".equals(transitionName) ) transitionName = method.getName();
+            BusinessProcess.instance().transition(transitionName);
+         }
+      }
+      return result;
+   }
+
+   private String getProcessKey(String el)
+   {
+      Object key = Expressions.instance().createValueExpression(el).getValue();
+      if (key==null)
+      {
+         throw new IllegalStateException("process business key may not be null");
+      }
+      return key.toString();
+   }
+
+   private Long getProcessOrTaskId(String paramName, String el)
+   {
+      Object id;
+      if ( Strings.isEmpty(paramName) )
+      {
+         id = Expressions.instance().createValueExpression(el).getValue();
+      }
+      else
+      {
+         String[] values = Parameters.instance().getRequestParameters().get(paramName);
+         id = values!=null && values.length==1 ? values[0] : null;
+      }
+      
+      if (id==null)
+      {
+         throw new IllegalStateException("task/process id may not be null");
+      }
+      else if (id instanceof Long)
+      {
+         return (Long) id;
+      }
+      else if (id instanceof String)
+      {
+         return new Long( (String) id );
+      }
+      else
+      {
+         throw new IllegalArgumentException("task/process id must be a string or long");
+      }
+    }
+   
+   public boolean isInterceptorEnabled()
+   {
+      return Contexts.isApplicationContextActive() && Init.instance().isJbpmInstalled();
+   }
+   
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/Jbpm.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/Jbpm.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/Jbpm.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,367 @@
+package org.jboss.seam.bpm;
+
+import java.io.InputStream;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.annotation.Named;
+import javax.context.ApplicationScoped;
+import javax.inject.Initializer;
+import javax.naming.NamingException;
+
+import org.dom4j.Element;
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.lob.ReaderInputStream;
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.core.Init;
+import org.jboss.seam.core.ResourceLoader;
+import org.jboss.seam.deployment.DeploymentStrategy;
+import org.jboss.seam.deployment.FileDescriptor;
+import org.jboss.seam.deployment.StandardDeploymentStrategy;
+import org.jboss.webbeans.log.LogProvider;
+import org.jboss.webbeans.log.Logging;
+import org.jboss.seam.util.Naming;
+import org.jboss.seam.util.Resources;
+import org.jbpm.JbpmConfiguration;
+import org.jbpm.JbpmContext;
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.graph.node.DbSubProcessResolver;
+import org.jbpm.graph.node.ProcessState;
+import org.jbpm.graph.node.SubProcessResolver;
+import org.jbpm.instantiation.UserCodeInterceptorConfig;
+import org.jbpm.jpdl.JpdlException;
+import org.jbpm.jpdl.el.impl.JbpmExpressionEvaluator;
+import org.jbpm.persistence.db.DbPersistenceServiceFactory;
+import org.xml.sax.InputSource;
+
+/**
+ * A seam component that boostraps a JBPM SessionFactory
+ * 
+ * @author Gavin King
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole</a>
+ * @author Norman Richards
+ * @author <a href="mailto:theute at jboss.org">Thomas Heute</a>
+ */
+ at Named
+ at ApplicationScoped
+public class Jbpm 
+{
+   private static final LogProvider log = Logging.getLogProvider(Jbpm.class);
+   
+   private JbpmConfiguration jbpmConfiguration;
+   private String jbpmConfigurationJndiName;
+   private String[] processDefinitions;
+   private String[] pageflowDefinitions;
+   private Map<String, ProcessDefinition> pageflowProcessDefinitions = new HashMap<String, ProcessDefinition>();
+
+   @Initializer
+   public void startup() throws Exception
+   {
+      log.trace( "Starting jBPM" );
+      ProcessState.setDefaultSubProcessResolver( new SeamSubProcessResolver() );
+      installProcessDefinitions();
+      installPageflowDefinitions();
+      //JbpmExpressionEvaluator.setVariableResolver( new SeamVariableResolver() );
+      //JbpmExpressionEvaluator.setFunctionMapper( new SeamFunctionMapper() );
+      JbpmExpressionEvaluator.setExpressionEvaluator( new SeamExpressionEvaluator() );
+      UserCodeInterceptorConfig.setUserCodeInterceptor( new SeamUserCodeInterceptor() );
+   }
+
+   @Destroy
+   public void shutdown()
+   {
+      if (jbpmConfiguration!=null) 
+      {
+         jbpmConfiguration.close();
+      }
+   }
+   
+   public JbpmConfiguration getJbpmConfiguration()
+   {
+      if (jbpmConfiguration==null)
+      {
+         initJbpmConfiguration();
+      }
+      return jbpmConfiguration;
+   }
+
+   private void initJbpmConfiguration()
+   {
+      if (jbpmConfigurationJndiName==null)
+      {
+         jbpmConfiguration = JbpmConfiguration.getInstance();
+      }
+      else
+      {
+         try
+         {
+            jbpmConfiguration = (JbpmConfiguration) Naming.getInitialContext().lookup(jbpmConfigurationJndiName);
+         }
+         catch (NamingException ne)
+         {
+            throw new IllegalArgumentException("JbpmConfiguration not found in JNDI", ne);
+         }
+      }
+
+      DbPersistenceServiceFactory dbpsf = (DbPersistenceServiceFactory) jbpmConfiguration.getServiceFactory("persistence");
+      if (Naming.getInitialContextProperties()!=null)
+      {
+         // Prefix regular JNDI properties for Hibernate
+         Hashtable<String, String> hash = Naming.getInitialContextProperties();
+         Properties prefixed = new Properties();
+         for (Map.Entry<String, String> entry: hash.entrySet() )
+         {
+            prefixed.setProperty( Environment.JNDI_PREFIX + "." + entry.getKey(), entry.getValue() );
+         }
+         
+         try
+         {
+            dbpsf.getConfiguration().getProperties().putAll(prefixed);
+         }
+         catch (HibernateException he)
+         {
+            log.info("could not set JNDI properties for jBPM persistence: " + he.getMessage());
+         }
+      }
+   }
+
+   public ProcessDefinition getPageflowProcessDefinition(String pageflowName)
+   {
+      return pageflowProcessDefinitions.get(pageflowName);
+   }
+   
+   public boolean isPageflowProcessDefinition(String pageflowName)
+   {
+      return pageflowProcessDefinitions.containsKey(pageflowName);
+   }
+   
+   public static ProcessDefinition getPageflowDefinitionFromResource(String resourceName)
+   {
+      InputStream resource = ResourceLoader.instance().getResourceAsStream(resourceName);
+      if (resource==null)
+      {
+         throw new IllegalArgumentException("pageflow resource not found: " + resourceName);
+      }
+      try
+      {
+         return Jbpm.parseInputSource( new InputSource(resource) );
+      }
+      catch (JpdlException e)
+      {
+         throw new JpdlException("Unable to parse process definition " + resourceName, e);
+      } finally {
+          Resources.closeStream(resource);
+      }
+   }
+   
+   public ProcessDefinition getProcessDefinitionFromResource(String resourceName) 
+   {
+      InputStream resource = ResourceLoader.instance().getResourceAsStream(resourceName);
+      if (resource==null)
+      {
+         throw new IllegalArgumentException("process definition resource not found: " + resourceName);
+      }
+      
+      try {
+          return ProcessDefinition.parseXmlInputStream(resource);
+      } finally {
+          Resources.closeStream(resource);
+      }
+   }
+
+   public String[] getPageflowDefinitions() 
+   {
+      return pageflowDefinitions;
+   }
+
+   public void setPageflowDefinitions(String[] pageflowDefinitions) 
+   {
+      this.pageflowDefinitions = pageflowDefinitions;
+   }
+   
+   public String[] getProcessDefinitions() 
+   {
+      return processDefinitions;
+   }
+
+   public void setProcessDefinitions(String[] processDefinitions) 
+   {
+      this.processDefinitions = processDefinitions;
+   }
+   
+   /**
+    * Dynamically deploy a page flow definition, if a pageflow with an 
+    * identical name already exists, the pageflow is updated.
+    * 
+    * @return true if the pageflow definition has been updated
+    */
+   public boolean deployPageflowDefinition(ProcessDefinition pageflowDefinition) 
+   {
+      return pageflowProcessDefinitions.put( pageflowDefinition.getName(), pageflowDefinition )!=null;
+   }
+   
+   /**
+    * Read a pageflow definition
+    * 
+    * @param pageflowDefinition the pageflow as an XML string
+    */
+   public ProcessDefinition getPageflowDefinitionFromXml(String pageflowDefinition)
+   {
+      InputStream stream = null;
+      try {
+          stream = new ReaderInputStream(new StringReader(pageflowDefinition));
+          return Jbpm.parseInputSource(new InputSource(stream));
+      } finally {
+          Resources.closeStream(stream);
+      }       
+   }
+   
+   /**
+    * Read a process definition
+    * 
+    * @param processDefinition the process as an XML string
+    */
+   public ProcessDefinition getProcessDefinitionFromXml(String processDefinition)
+   {
+       InputStream stream = null;
+       try {
+           stream = new ReaderInputStream(new StringReader(processDefinition));
+           return ProcessDefinition.parseXmlInputStream(stream);
+       } finally {
+           Resources.closeStream(stream);
+       }
+   }
+   
+   /**
+    * Remove a pageflow definition
+    * 
+    * @param pageflowName Name of the pageflow to remove
+    * @return true if the pageflow definition has been removed
+    */
+   public boolean undeployPageflowDefinition(String pageflowName) 
+   {     
+      return pageflowProcessDefinitions.remove(pageflowName)!=null;
+   }
+   
+   private void installPageflowDefinitions() {
+      Set<String> mergedPageflowDefinitions = new LinkedHashSet<String>();
+      if ( pageflowDefinitions!=null )
+      {
+         mergedPageflowDefinitions.addAll(Arrays.asList(pageflowDefinitions));
+      }
+      
+      for (FileDescriptor fileDescriptor : ((PageflowDeploymentHandler) ((DeploymentStrategy) Contexts.getEventContext().get(StandardDeploymentStrategy.NAME)).getDeploymentHandlers().get(PageflowDeploymentHandler.NAME)).getResources())
+      {
+         mergedPageflowDefinitions.add(fileDescriptor.getName());
+      }
+      
+      for (String pageflow: mergedPageflowDefinitions)
+      {
+         ProcessDefinition pd = getPageflowDefinitionFromResource(pageflow);
+         pageflowProcessDefinitions.put( pd.getName(), pd );
+      }
+   }
+
+   private void installProcessDefinitions()
+   {
+      if ( isProcessDeploymentEnabled() )
+      {
+         JbpmContext jbpmContext = getJbpmConfiguration().createJbpmContext();
+         try
+         {
+            if (processDefinitions!=null)
+            {
+               for ( String definitionResource : processDefinitions )
+               {
+                  deployProcess(jbpmContext, definitionResource);
+               }
+            }
+         }
+         catch (RuntimeException e)
+         {
+            throw new RuntimeException("could not deploy a process definition", e);
+         }
+         finally
+         {
+            jbpmContext.close();
+         }
+      }
+   }
+
+   private void deployProcess(JbpmContext jbpmContext, String definitionResource)
+   {
+      ProcessDefinition processDefinition = ProcessDefinition.parseXmlResource(definitionResource);
+      if (log.isDebugEnabled())
+      {
+         log.debug( "deploying process definition : " + processDefinition.getName() );
+      }
+      jbpmContext.deployProcessDefinition(processDefinition);
+   }
+
+   protected boolean isProcessDeploymentEnabled()
+   {
+      return processDefinitions!=null && processDefinitions.length>0;
+   }
+   
+   public static Jbpm instance()
+   {
+      if ( !Contexts.isApplicationContextActive() )
+      {
+         throw new IllegalStateException("No application context active");
+      }
+      if ( !Init.instance().isJbpmInstalled() )
+      {
+         throw new IllegalStateException("jBPM support is not installed (use components.xml to install it)");
+      }
+      return (Jbpm) Component.getInstance(Jbpm.class, ScopeType.APPLICATION);
+   }
+
+   protected String getJbpmConfigurationJndiName()
+   {
+      return jbpmConfigurationJndiName;
+   }
+
+   protected void setJbpmConfigurationJndiName(String jbpmConfigurationJndiName)
+   {
+      this.jbpmConfigurationJndiName = jbpmConfigurationJndiName;
+   }
+   
+   public static JbpmConfiguration pageflowConfiguration = JbpmConfiguration.parseResource("org/jboss/seam/bpm/jbpm.pageflow.cfg.xml");
+
+   public static JbpmContext createPageflowContext() 
+   {
+      return pageflowConfiguration.createJbpmContext();
+   }
+
+   public static ProcessDefinition parseInputSource(InputSource inputSource) 
+   {
+      JbpmContext jbpmContext = createPageflowContext();
+      try 
+      {
+         return new PageflowParser(inputSource).readProcessDefinition();
+      }
+      finally 
+      {
+         jbpmContext.close();
+      }
+   }
+
+   private static final DbSubProcessResolver DB_SUB_PROCESS_RESOLVER = new DbSubProcessResolver();
+   class SeamSubProcessResolver implements SubProcessResolver
+   {
+      public ProcessDefinition findSubProcess(Element element)
+      {
+         String subProcessName = element.attributeValue("name");
+         ProcessDefinition pageflow = pageflowProcessDefinitions.get(subProcessName);
+         return pageflow==null ? DB_SUB_PROCESS_RESOLVER.findSubProcess(element) : pageflow;
+      }
+   }
+   
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/JbpmELResolver.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/JbpmELResolver.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/JbpmELResolver.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,69 @@
+package org.jboss.seam.bpm;
+
+import java.beans.FeatureDescriptor;
+import java.util.Iterator;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+
+import org.jbpm.jpdl.el.VariableResolver;
+
+/**
+ * Resolves jBPM variables for Unified EL
+ * 
+ * @author Gavin King
+ *
+ */
+final class JbpmELResolver extends ELResolver
+{
+   private final VariableResolver resolver;
+
+   JbpmELResolver(VariableResolver resolver)
+   {
+      this.resolver = resolver;
+   }
+
+   @Override
+   public Object getValue(ELContext context, Object base, Object property) 
+   {
+      if ( base==null && property!=null )
+      {
+         return resolver.resolveVariable( (String) base );
+      }
+      else
+      {
+         return null;
+      }
+   }
+
+   @Override
+   public boolean isReadOnly(ELContext context, Object base, Object property) 
+   {
+      return true;
+   }
+
+   @Override
+   public Class<?> getCommonPropertyType(ELContext context, Object base)
+   {
+      throw new UnsupportedOperationException();
+   }
+
+   @Override
+   public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base)
+   {
+      throw new UnsupportedOperationException();
+   }
+
+   @Override
+   public Class<?> getType(ELContext context, Object base, Object property) 
+   {
+      throw new UnsupportedOperationException();
+   }
+
+   @Override
+   public void setValue(ELContext context, Object base, Object property, Object value)
+   {
+      throw new UnsupportedOperationException();
+   }
+   
+}
\ No newline at end of file

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/ManagedJbpmContext.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/ManagedJbpmContext.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/ManagedJbpmContext.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,168 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.bpm;
+
+import static org.jboss.seam.annotations.Install.BUILT_IN;
+
+import javax.naming.NamingException;
+import javax.transaction.RollbackException;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+
+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.annotations.Unwrap;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.contexts.Lifecycle;
+import org.jboss.seam.log.LogProvider;
+import org.jboss.seam.log.Logging;
+import org.jboss.seam.transaction.Transaction;
+import org.jboss.seam.transaction.UserTransaction;
+import org.jbpm.JbpmContext;
+import org.jbpm.persistence.db.DbPersistenceServiceFactory;
+import org.jbpm.svc.Services;
+
+/**
+ * Manages a reference to a JbpmContext.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole</a>
+ * @author Gavin King
+ */
+ at Scope(ScopeType.EVENT)
+ at Name("org.jboss.seam.bpm.jbpmContext")
+ at BypassInterceptors
+ at Install(precedence=BUILT_IN, dependencies="org.jboss.seam.bpm.jbpm")
+public class ManagedJbpmContext implements Synchronization
+{
+   private static final LogProvider log = Logging.getLogProvider(ManagedJbpmContext.class);
+
+   private JbpmContext jbpmContext;
+   private boolean synchronizationRegistered;
+
+   @Create
+   public void create() throws NamingException, RollbackException, SystemException
+   {
+      jbpmContext = Jbpm.instance().getJbpmConfiguration().createJbpmContext();
+      assertNoTransactionManagement();
+      log.debug( "created seam managed jBPM context");
+   }
+
+   private void assertNoTransactionManagement()
+   {
+      DbPersistenceServiceFactory dpsf = (DbPersistenceServiceFactory) jbpmContext.getJbpmConfiguration()
+            .getServiceFactory(Services.SERVICENAME_PERSISTENCE);
+      if ( dpsf.isTransactionEnabled() )
+      {
+         throw new IllegalStateException("jBPM transaction management is enabled, disable in jbpm.cfg.xml");
+      }
+   }
+
+   @Unwrap
+   public JbpmContext getJbpmContext() throws NamingException, RollbackException, SystemException
+   {
+      joinTransaction();
+      return jbpmContext;
+   }
+
+   private void joinTransaction() throws SystemException
+   {
+      UserTransaction transaction = Transaction.instance();
+      
+      if ( !transaction.isActiveOrMarkedRollback() )
+      {
+         throw new IllegalStateException("JbpmContext may only be used inside a transaction");
+      }
+      
+      if ( !synchronizationRegistered && !Lifecycle.isDestroying() && transaction.isActive() )
+      {
+         jbpmContext.getSession().isOpen();
+         try //TODO: what we really want here is if (!cmt)
+         {
+            transaction.registerSynchronization(this);
+         }
+         catch (UnsupportedOperationException uoe)
+         {
+            jbpmContext.getSession().getTransaction().registerSynchronization(this);
+         }
+         synchronizationRegistered = true;
+      }
+   }
+   
+   public void beforeCompletion()
+   {
+      log.debug( "flushing seam managed jBPM context" );
+      /*org.jbpm.graph.exe.ProcessInstance processInstance = ProcessInstance.instance();
+      if (processInstance!=null)
+      {
+         jbpmContext.save(processInstance);
+      }*/
+      if ( Contexts.isBusinessProcessContextActive() )
+      {
+         //in requests that come through SeamPhaseListener,
+         //transactions are committed before the contexts are
+         //destroyed, flush here:
+         Contexts.getBusinessProcessContext().flush();
+      }
+      jbpmContext.getSession().flush();
+      log.debug( "done flushing seam managed jBPM context" );
+   }
+   
+   public void afterCompletion(int status) 
+   {
+      synchronizationRegistered = false;
+      if ( !Contexts.isEventContextActive() )
+      {
+         //in calls to MDBs and remote calls to SBs, the 
+         //transaction doesn't commit until after contexts
+         //are destroyed, so wait until the transaction
+         //completes before closing the session
+         //on the other hand, if we still have an active
+         //event context, leave it open
+         closeContext();
+      }
+   }
+   
+   @Destroy
+   public void destroy()
+   {
+      if ( !synchronizationRegistered )
+      {
+         //in requests that come through SeamPhaseListener,
+         //there can be multiple transactions per request,
+         //but they are all completed by the time contexts
+         //are dstroyed
+         //so wait until the end of the request to close
+         //the session
+         //on the other hand, if we are still waiting for
+         //the transaction to commit, leave it open
+         closeContext();
+      }
+   }
+
+   private void closeContext()
+   {
+      log.debug( "destroying seam managed jBPM context" );
+      jbpmContext.close();
+      log.debug( "done destroying seam managed jBPM context" );
+   }
+      
+   public static JbpmContext instance()
+   {
+      if ( !Contexts.isEventContextActive() )
+      {
+         throw new IllegalStateException("no active event context");
+      }
+      return (JbpmContext) Component.getInstance(ManagedJbpmContext.class, ScopeType.EVENT);
+   }
+
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PageflowDeploymentHandler.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PageflowDeploymentHandler.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PageflowDeploymentHandler.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,81 @@
+package org.jboss.seam.bpm;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.jboss.seam.deployment.AbstractDeploymentHandler;
+import org.jboss.seam.deployment.DeploymentMetadata;
+import org.jboss.seam.deployment.FileDescriptor;
+import org.jboss.seam.log.LogProvider;
+import org.jboss.seam.log.Logging;
+import org.jboss.seam.util.Resources;
+import org.jboss.seam.util.XML;
+
+
+public class PageflowDeploymentHandler extends AbstractDeploymentHandler
+{
+   
+   private static DeploymentMetadata NAMESPACE_METADATA = new DeploymentMetadata()
+   {
+
+      public String getFileNameSuffix()
+      {
+         return ".jpdl.xml";
+      }
+      
+   };
+
+   private static LogProvider log = Logging.getLogProvider(PageflowDeploymentHandler.class);
+   
+   public static final String NAME = "org.jboss.seam.bpm.PageflowDeploymentHandler";
+   
+   public String getName()
+   {
+      return NAME;
+   }
+
+   @Override
+   public void postProcess(ClassLoader classLoader)
+   {
+      Set<FileDescriptor> files = new HashSet<FileDescriptor>();
+      for (FileDescriptor fileDescriptor : getResources())
+      {
+         try
+         {
+            InputStream inputStream = fileDescriptor.getUrl().openStream();
+            try 
+            {
+               
+               Element root = XML.getRootElementSafely(inputStream);
+               if ("pageflow-definition".equals(root.getName()))
+               {
+                  files.add(fileDescriptor);
+               }
+            }
+            catch (DocumentException e) 
+            {
+               log.debug("Unable to parse " + fileDescriptor.getName(), e);
+            }
+            finally 
+            {
+               Resources.closeStream(inputStream);
+            }
+         } catch (IOException e)
+         {
+            log.trace("Error loading " + fileDescriptor.getName());
+         }
+         
+      }
+      setResources(files);
+   }
+   
+   public DeploymentMetadata getMetadata()
+   {
+      return NAMESPACE_METADATA;
+   }
+
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PageflowParser.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PageflowParser.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PageflowParser.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,65 @@
+package org.jboss.seam.bpm;
+
+import java.io.Reader;
+
+import org.dom4j.Element;
+import org.jbpm.graph.def.Node;
+import org.jbpm.graph.def.NodeCollection;
+import org.jbpm.jpdl.xml.JpdlXmlReader;
+import org.jbpm.jpdl.xml.ProblemListener;
+import org.xml.sax.InputSource;
+
+/**
+ * A jPDL parser for Seam pageflow definitions
+ * 
+ * @author Tom Baeyens
+ *
+ */
+public class PageflowParser extends JpdlXmlReader 
+{
+
+  private static final long serialVersionUID = 1L;
+
+  public PageflowParser(InputSource inputSource, ProblemListener problemListener) 
+  {
+     super(inputSource, problemListener);
+  }
+
+  public PageflowParser(InputSource inputSource) 
+  {
+     super(inputSource);
+  }
+
+  public PageflowParser(Reader reader) 
+  {
+     super(reader);
+  }
+  
+  @Override
+  public void readNodes(Element nodeCollectionElement, NodeCollection nodeCollection) 
+  {
+     super.readNodes(nodeCollectionElement, nodeCollection);
+    
+     if ( "pageflow-definition".equals( nodeCollectionElement.getName() ) ) 
+     {
+        String startPageName = nodeCollectionElement.attributeValue("start-page");
+        if (startPageName==null) 
+        {
+           Element startPageElement = nodeCollectionElement.element("start-page");
+           if (startPageElement!=null) 
+           {
+              startPageName = startPageElement.attributeValue("name");
+           }
+        }
+        if (startPageName!=null) 
+        {
+           Node startPage = getProcessDefinition().getNode(startPageName);
+           if (startPage!=null) 
+           {
+              getProcessDefinition().setStartState(startPage);
+           }
+        }
+     }
+  }
+  
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PooledTask.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PooledTask.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PooledTask.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,120 @@
+package org.jboss.seam.bpm;
+
+import static org.jboss.seam.annotations.Install.BUILT_IN;
+
+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.Transactional;
+import org.jboss.seam.web.Parameters;
+import org.jbpm.taskmgmt.exe.TaskInstance;
+
+/**
+ * Support for assigning tasks in the pooled task list.
+ * 
+ * @see TaskInstanceList
+ * @author Gavin King
+ */
+ at Name("org.jboss.seam.bpm.pooledTask")
+ at Scope(ScopeType.APPLICATION)
+ at Install(precedence=BUILT_IN, dependencies="org.jboss.seam.bpm.jbpm")
+public class PooledTask
+{
+   
+   /**
+    * Assign the TaskInstance with the id passed
+    * in the request parameter named "taskId" to
+    * the current actor.
+    * 
+    * @see Actor
+    * @return a null outcome only if the task was not found
+    */
+   @Transactional
+   public String assignToCurrentActor()
+   {
+      Actor actor = Actor.instance();
+      if ( actor.getId()==null )
+      {
+         throw new IllegalStateException("no current actor id defined");
+      }
+      TaskInstance taskInstance = getTaskInstance();
+      if (taskInstance!=null)
+      {
+         taskInstance.setActorId( actor.getId() );
+         return "taskAssignedToActor";
+      }
+      else
+      {
+         return null;
+      }
+   }
+   
+   /**
+    * Assign the TaskInstance with the id passed
+    * in the request parameter named "taskId" to
+    * the given actor id.
+    * 
+    * @param actorId the jBPM actor id
+    * @return a null outcome only if the task was not found
+    */
+   @Transactional
+   public String assign(String actorId)
+   {
+      TaskInstance taskInstance = getTaskInstance();
+      if (taskInstance!=null)
+      {
+         taskInstance.setActorId(actorId);
+         return "taskAssigned";
+      }
+      else
+      {
+         return null;
+      }
+   }
+   
+   /**
+    * Unassign the TaskInstance with the id passed
+    * in the request parameter named "taskId" from
+    * the actor to which it is assigned, and return
+    * it to the pool it came from.
+    * 
+    * @return a null outcome only if the task was not found
+    */
+   @Transactional
+   public String unassign()
+   {
+      TaskInstance taskInstance = getTaskInstance();
+      if (taskInstance!=null)
+      {
+         taskInstance.setActorId(null);
+         return "taskUnassigned";
+      }
+      else
+      {
+         return null;
+      }
+   }
+   
+   /**
+    * @return the TaskInstance with the id passed
+    * in the request parameter named "taskId".
+    */
+   @Transactional
+   public TaskInstance getTaskInstance()
+   {
+      String[] values = Parameters.instance().getRequestParameters().get("taskId");
+      if ( values==null || values.length!=1 ) 
+      {
+         return null;
+      }
+      else
+      {
+         String taskId = values[0];
+         return taskId==null ? 
+               null : 
+               ManagedJbpmContext.instance().getTaskInstanceForUpdate( Long.parseLong(taskId) );
+      }
+   }
+   
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PooledTaskInstanceList.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PooledTaskInstanceList.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/PooledTaskInstanceList.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,40 @@
+package org.jboss.seam.bpm;
+
+import static org.jboss.seam.annotations.Install.BUILT_IN;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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.Transactional;
+import org.jboss.seam.annotations.Unwrap;
+import org.jbpm.taskmgmt.exe.TaskInstance;
+
+/**
+ * Support for the pooled task list.
+ * 
+ * @see TaskInstanceList
+ * @author Gavin King
+ */
+ at Name("org.jboss.seam.bpm.pooledTaskInstanceList")
+ at Scope(ScopeType.APPLICATION)
+ at Install(precedence=BUILT_IN, dependencies="org.jboss.seam.bpm.jbpm")
+public class PooledTaskInstanceList
+{
+   
+   @Unwrap
+   @Transactional
+   public List<TaskInstance> getPooledTaskInstanceList()
+   {
+      Actor actor = Actor.instance();
+      String actorId = actor.getId();
+      if ( actorId == null ) return null;
+      ArrayList groupIds = new ArrayList( actor.getGroupActorIds() );
+      groupIds.add(actorId);
+      return ManagedJbpmContext.instance().getGroupTaskList(groupIds);
+   }
+   
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/ProcessInstance.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/ProcessInstance.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/ProcessInstance.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.bpm;
+
+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.Unwrap;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.util.Work;
+
+/**
+ * A Seam component that allows injection of the current
+ * jBPM ProcessInstance.
+ * 
+ * @author Gavin King
+ */
+ at Scope(ScopeType.STATELESS)
+ at Name("org.jboss.seam.bpm.processInstance")
+ at BypassInterceptors
+ at Install(precedence=BUILT_IN, dependencies="org.jboss.seam.bpm.jbpm")
+public class ProcessInstance 
+{
+   
+   @Unwrap
+   public org.jbpm.graph.exe.ProcessInstance getProcessInstance() throws Exception
+   {
+      if ( !Contexts.isConversationContextActive() ) return null;
+      
+      return new Work<org.jbpm.graph.exe.ProcessInstance>()
+      {
+         
+         @Override
+         protected org.jbpm.graph.exe.ProcessInstance work() throws Exception
+         {         
+            Long processId = BusinessProcess.instance().getProcessId();
+            if (processId!=null)
+            {
+               //TODO: do we need to cache this??
+               return ManagedJbpmContext.instance().getProcessInstanceForUpdate(processId);
+            }
+            else
+            {
+               return null;
+            }
+         }
+         
+      }.workInTransaction();
+      
+   }
+   
+   public static org.jbpm.graph.exe.ProcessInstance instance()
+   {
+      if ( !Contexts.isConversationContextActive() || !BusinessProcess.instance().hasCurrentProcess() ) return null; //so we don't start a txn
+      
+      return (org.jbpm.graph.exe.ProcessInstance) Component.getInstance(ProcessInstance.class, ScopeType.STATELESS);
+   }
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/ProcessInstanceFinder.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/ProcessInstanceFinder.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/ProcessInstanceFinder.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,105 @@
+package org.jboss.seam.bpm;
+
+import static org.hibernate.criterion.Order.asc;
+import static org.hibernate.criterion.Order.desc;
+import static org.hibernate.criterion.Restrictions.isNotNull;
+import static org.hibernate.criterion.Restrictions.isNull;
+import static org.jboss.seam.annotations.Install.BUILT_IN;
+
+import java.util.List;
+
+import org.hibernate.Criteria;
+import org.hibernate.criterion.Restrictions;
+import org.jboss.seam.annotations.Factory;
+import org.jboss.seam.annotations.Install;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Transactional;
+import org.jbpm.graph.exe.ProcessInstance;
+
+/**
+ * Support for the process list.
+ * 
+ * @author Gavin King
+ */
+ at Name("org.jboss.seam.bpm.processInstanceFinder")
+ at Install(precedence=BUILT_IN, dependencies="org.jboss.seam.bpm.jbpm")
+public class ProcessInstanceFinder
+{
+   
+   private String processDefinitionName;
+   private String nodeName;
+   private Boolean processInstanceEnded = false;
+   private Boolean sortDescending = false;
+   
+   @Factory(value="org.jboss.seam.bpm.processInstanceList", autoCreate=true)
+   @Transactional
+   public List<ProcessInstance> getProcessInstanceList()
+   {
+      Criteria query = ManagedJbpmContext.instance().getSession()
+                  .createCriteria(ProcessInstance.class);
+      if ( processInstanceEnded!=null )
+      {
+         query.add( processInstanceEnded ? isNotNull("end") : isNull("end") );
+      }
+      
+      if (processDefinitionName!=null)
+      {
+         query.createCriteria("processDefinition")
+               .add( Restrictions.eq("name", processDefinitionName) );
+      }
+      
+      query = query.createCriteria("rootToken");
+      if (sortDescending!=null)
+      {
+         query.addOrder( sortDescending ? desc("nodeEnter") : asc("nodeEnter") );
+      }
+      if (nodeName!=null)
+      {
+         query.createCriteria("node")
+               .add( Restrictions.eq("name", nodeName) );
+      }
+      
+      return query.list();
+   }
+
+   protected String getNodeName()
+   {
+      return nodeName;
+   }
+
+   protected void setNodeName(String nodeName)
+   {
+      this.nodeName = nodeName;
+   }
+
+   protected String getProcessDefinitionName()
+   {
+      return processDefinitionName;
+   }
+
+   protected void setProcessDefinitionName(String processDefinitionName)
+   {
+      this.processDefinitionName = processDefinitionName;
+   }
+
+   protected Boolean isSortDescending()
+   {
+      return sortDescending;
+   }
+
+   protected void setSortDescending(Boolean sortDescending)
+   {
+      this.sortDescending = sortDescending;
+   }
+
+   protected Boolean getProcessInstanceEnded()
+   {
+      return processInstanceEnded;
+   }
+
+   protected void setProcessInstanceEnded(Boolean ended)
+   {
+      this.processInstanceEnded = ended;
+   }
+   
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/SeamExpressionEvaluator.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/SeamExpressionEvaluator.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/SeamExpressionEvaluator.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,167 @@
+package org.jboss.seam.bpm;
+
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.el.CompositeELResolver;
+import javax.el.ELContext;
+import javax.el.MethodExpression;
+import javax.el.MethodNotFoundException;
+import javax.el.PropertyNotFoundException;
+import javax.el.ValueExpression;
+
+import org.jboss.seam.el.EL;
+import org.jboss.seam.el.SeamFunctionMapper;
+import org.jboss.seam.log.LogProvider;
+import org.jboss.seam.log.Logging;
+import org.jbpm.jpdl.el.ELException;
+import org.jbpm.jpdl.el.Expression;
+import org.jbpm.jpdl.el.ExpressionEvaluator;
+import org.jbpm.jpdl.el.FunctionMapper;
+import org.jbpm.jpdl.el.VariableResolver;
+
+/**
+ * Plugs the JBoss EL expression language and Seam
+ * EL resolvers into jBPM. Note that this current 
+ * implementation does not allow jBPM to see stuff
+ * defined only by the JSF ELResolvers.
+ * 
+ * @author Gavin King
+ * @author Pete Muir
+ *
+ */
+public class SeamExpressionEvaluator 
+    extends ExpressionEvaluator
+{
+   
+    private static LogProvider log = Logging.getLogProvider(SeamExpressionEvaluator.class);
+
+    @Override
+    public Object evaluate(String expression, Class returnType, final VariableResolver resolver, FunctionMapper mapper)
+        throws ELException
+    {
+        return createExpression(expression, returnType, mapper).evaluate(resolver);
+    }
+    
+    @Override
+    public Expression parseExpression(final String expression, final Class returnType, FunctionMapper mapper)
+        throws ELException
+    {
+        return createExpression(expression, returnType, mapper);
+    }
+    
+    private static Expression createExpression(final String expression, final Class returnType, final FunctionMapper mapper)
+    {
+        
+        return new Expression() 
+        {
+            private ELContext elContext = EL.createELContext();
+
+            private MethodExpression me;
+            private ValueExpression ve;
+                
+            private void initMethodExpression() 
+            {
+                if (me == null || ve == null)
+                {
+                    me = EL.EXPRESSION_FACTORY.createMethodExpression(elContext, expression, returnType, new Class[0]);
+                }
+            }
+                
+            private void initValueExpression() 
+            {
+                if (me == null || ve == null)
+                {
+                    ve = EL.EXPRESSION_FACTORY.createValueExpression(elContext, expression, returnType);
+                }
+            }
+                
+            @Override
+            public Object evaluate(VariableResolver resolver) throws ELException
+            {
+                List<javax.el.ELException> exceptions = new ArrayList<javax.el.ELException>();
+                try
+                {
+                    initMethodExpression(); 
+                    if (me != null)
+                    {
+                        try
+                        {
+                            return me.invoke(createELContext(resolver, mapper), new Object[0]);
+                        }
+                        catch (MethodNotFoundException e)
+                        {
+                            exceptions.add(e);
+                        }
+                    }
+                }
+                catch (javax.el.ELException e) 
+                {
+                    exceptions.add(e);
+                }
+                 
+                try
+                {
+                    initValueExpression();
+                    if (ve != null)
+                    {
+                        try
+                        {
+                            return ve.getValue(createELContext(resolver, mapper));
+                        }
+                        catch (PropertyNotFoundException e)
+                        {
+                            exceptions.add(e);
+                        }
+                    }
+                }
+                catch (javax.el.ELException e)
+                {
+                    exceptions.add(e);
+                }
+                
+                if (exceptions.size() == 1)
+                {
+                   throw new ELException("Error evaluating " + expression, exceptions.get(0));
+                }
+                else if (exceptions.size() > 1)
+                {
+                   log.error("Exceptions occurred when parsing " + expression);
+                   for (javax.el.ELException e : exceptions)
+                   {
+                      log.error("Possible cause", e);
+                   }
+                }
+                if (me == null && ve ==  null)
+                {
+                   log.error("Error parsing " + expression);
+                   throw new ELException("Error parsing " + expression + "; not a valid EL expression");
+                }
+                throw new ELException("Error evaluating " + expression + "; possible causes are logged at debug level");
+            }
+        };
+    }
+   
+    private static javax.el.FunctionMapper decorateFunctionMapper(final FunctionMapper functionMapper)
+    {
+        return new SeamFunctionMapper( new javax.el.FunctionMapper() 
+        {
+            @Override
+            public Method resolveFunction(String prefix, String localName)
+            {
+                return functionMapper.resolveFunction(prefix, localName);
+            }
+        });
+    }
+    
+    private static ELContext createELContext(VariableResolver resolver, FunctionMapper functionMapper)
+    {
+        CompositeELResolver composite = new CompositeELResolver();
+        composite.add(EL.EL_RESOLVER);
+        composite.add( new JbpmELResolver(resolver) );
+        return EL.createELContext(composite, decorateFunctionMapper(functionMapper));
+    }
+    
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/SeamUserCodeInterceptor.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/SeamUserCodeInterceptor.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/SeamUserCodeInterceptor.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,153 @@
+package org.jboss.seam.bpm;
+
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.contexts.Lifecycle;
+import org.jbpm.context.exe.ContextInstance;
+import org.jbpm.graph.def.Action;
+import org.jbpm.graph.exe.ExecutionContext;
+import org.jbpm.graph.exe.Token;
+import org.jbpm.instantiation.UserCodeInterceptor;
+import org.jbpm.taskmgmt.def.AssignmentHandler;
+import org.jbpm.taskmgmt.def.TaskControllerHandler;
+import org.jbpm.taskmgmt.exe.Assignable;
+import org.jbpm.taskmgmt.exe.TaskInstance;
+
+/**
+ * Intercepts calls to user code coming from jBPM, sets up
+ * Seam contexts and associates the process and task instances
+ * with the contexts.
+ * 
+ * @author Gavin King
+ *
+ */
+class SeamUserCodeInterceptor implements UserCodeInterceptor
+{
+   abstract static class ContextualCall
+   {
+      abstract void process() throws Exception;
+      
+      void run() throws Exception
+      {
+         if ( Contexts.isEventContextActive() || Contexts.isApplicationContextActive() ) //not sure about the second bit (only needed at init time!)
+         {
+            process();
+         }
+         else
+         {
+            Lifecycle.beginCall();
+            try
+            {
+               process();
+            }
+            finally
+            {
+               Lifecycle.endCall();
+            }
+         }
+      }
+      
+      void runAndWrap()
+      {
+         try
+         {
+            run();
+         }
+         catch (RuntimeException re)
+         {
+            throw re;
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException(e);
+         }
+      }
+   }
+
+   public void executeAction(final Action action, final ExecutionContext context) throws Exception
+   {
+      if ( isPageflow(context) )
+      {
+         action.execute(context);
+      }
+      else
+      {
+         new ContextualCall()
+         {
+            @Override
+            void process() throws Exception
+            {
+               initProcessAndTask(context);
+               action.execute(context);
+            }
+         }.run();
+      }
+   }
+
+   private boolean isPageflow(final ExecutionContext context)
+   {
+      return Contexts.isConversationContextActive() && 
+            Jbpm.instance().isPageflowProcessDefinition( context.getProcessDefinition().getName() );
+   }
+
+   public void executeAssignment(final AssignmentHandler handler, final Assignable assignable, 
+            final ExecutionContext context)
+            throws Exception
+   {
+      new ContextualCall()
+      {
+         @Override
+         void process() throws Exception
+         {
+            initProcessAndTask(context);
+            handler.assign(assignable, context);
+         }
+      }.run();
+   }
+
+   public void executeTaskControllerInitialization(final TaskControllerHandler handler, final TaskInstance task,
+            final ContextInstance context, final Token token)
+   {
+      new ContextualCall()
+      {
+         @Override
+         void process() throws Exception
+         {
+            initProcessAndTask(task);
+            handler.initializeTaskVariables(task, context, token);
+         }
+      }.runAndWrap();
+   }
+
+   public void executeTaskControllerSubmission(final TaskControllerHandler handler, final TaskInstance task,
+            final ContextInstance context, final Token token)
+   {
+      new ContextualCall()
+      {
+         @Override
+         void process() throws Exception
+         {
+            initProcessAndTask(task);
+            handler.submitTaskVariables(task, context, token);
+         }
+      }.runAndWrap();
+   }
+
+   private static void initProcessAndTask(ExecutionContext context)
+   {
+      BusinessProcess businessProcess = BusinessProcess.instance();
+      businessProcess.setProcessId( context.getProcessInstance().getId() );
+      TaskInstance taskInstance = context.getTaskInstance();
+      if (taskInstance!=null)
+      {
+         businessProcess.setTaskId( taskInstance.getId() );
+      }
+   }
+
+   private static void initProcessAndTask(TaskInstance task)
+   {
+      BusinessProcess businessProcess = BusinessProcess.instance();
+      businessProcess.setProcessId( task.getProcessInstance().getId() );
+      businessProcess.setTaskId( task.getId() );
+   }
+
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstance.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstance.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstance.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,53 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.bpm;
+
+import javax.annotation.Named;
+import javax.inject.Produces;
+
+/**
+ * A Seam component that allows injection of the current
+ * jBPM TaskInstance.
+ * 
+ * @author Gavin King
+ */
+public class TaskInstance 
+{   
+   @Produces
+   public org.jbpm.taskmgmt.exe.TaskInstance getTaskInstance() throws Exception
+   {
+      if ( !Contexts.isConversationContextActive() ) return null;
+      
+      return new Work<org.jbpm.taskmgmt.exe.TaskInstance>()
+      {
+         
+         @Override
+         protected org.jbpm.taskmgmt.exe.TaskInstance work() throws Exception
+         {         
+            Long taskId = BusinessProcess.instance().getTaskId();
+            if (taskId!=null)
+            {
+               //TODO: do we need to cache this??
+               return ManagedJbpmContext.instance().getTaskInstanceForUpdate(taskId);
+            }
+            else
+            {
+               return null;
+            }
+         }
+         
+      }.workInTransaction();
+   }
+   
+   public static org.jbpm.taskmgmt.exe.TaskInstance instance()
+   {
+      if ( !Contexts.isConversationContextActive() || !BusinessProcess.instance().hasCurrentTask() ) return null; //so we don't start a txn
+      
+      return (org.jbpm.taskmgmt.exe.TaskInstance) Component.getInstance(TaskInstance.class, ScopeType.STATELESS);
+   }
+   
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstanceList.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstanceList.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstanceList.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,33 @@
+package org.jboss.seam.bpm;
+
+import java.util.List;
+
+import javax.context.ApplicationScoped;
+import javax.inject.Produces;
+
+import org.jbpm.taskmgmt.exe.TaskInstance;
+
+/**
+ * Support for the task list.
+ * 
+ * @see TaskInstanceListForType
+ * @see PooledTask
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole</a>
+ * @author Gavin King
+ */
+public class TaskInstanceList
+{
+   @Produces
+   public List<TaskInstance> getTaskInstanceList()
+   {
+      return getTaskInstanceList( Actor.instance().getId() );
+   }
+
+   private List<TaskInstance> getTaskInstanceList(String actorId)
+   {
+      if ( actorId == null ) return null;
+
+      return ManagedJbpmContext.instance().getTaskList(actorId);
+   }
+   
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstanceListForType.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstanceListForType.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstanceListForType.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,49 @@
+package org.jboss.seam.bpm;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.context.ApplicationScoped;
+
+import org.jbpm.taskmgmt.exe.TaskInstance;
+
+/**
+ * Support for a list of tasks of a particular type.
+ * 
+ * @see TaskInstanceList
+ * @author Gavin King
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+ at ApplicationScoped
+public class TaskInstanceListForType
+{   
+   @Unwrap
+   @Transactional
+   public Map<String,List<TaskInstance>> getTaskInstanceList()
+   {
+      return getTaskInstanceList( Actor.instance().getId() );
+   }
+
+   private Map<String,List<TaskInstance>> getTaskInstanceList(String actorId)
+   {
+      if ( actorId == null ) return null;
+
+      Map<String, List<TaskInstance>> map = new HashMap<String, List<TaskInstance>>();
+      List<TaskInstance> taskInstances = ManagedJbpmContext.instance().getTaskList(actorId);
+      for ( TaskInstance task: taskInstances )
+      {
+         String name = task.getName();
+         List<TaskInstance> list = map.get(name);
+         if (list==null)
+         {
+            list = new ArrayList<TaskInstance>();
+            map.put(name, list);
+         }
+         list.add(task);
+      }
+      return map;
+   }
+   
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstancePriorityList.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstancePriorityList.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/TaskInstancePriorityList.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,45 @@
+package org.jboss.seam.bpm;
+
+import java.util.List;
+
+import javax.context.ApplicationScoped;
+import javax.inject.Produces;
+
+import org.hibernate.criterion.Order;
+import org.hibernate.criterion.Restrictions;
+import org.jbpm.taskmgmt.exe.TaskInstance;
+
+/**
+ * Support for a task list ordered by priority.
+ * 
+ * @see TaskInstanceList
+ * @see PooledTask
+ * @author Gavin King
+ */
+public class TaskInstancePriorityList
+{
+   
+   //TODO: we really need to cache the list in the event context,
+   //      but then we would need some events to refresh it
+   //      when tasks end, which is non-trivial to do....
+   @Produces @Transactional
+   public List<TaskInstance> getTaskInstanceList()
+   {
+      return getTaskInstanceList( Actor.instance().getId() );
+   }
+
+   private List<TaskInstance> getTaskInstanceList(String actorId)
+   {
+      if ( actorId == null ) return null;
+
+      return ManagedJbpmContext.instance().getSession()
+         .createCriteria(TaskInstance.class)
+         .add( Restrictions.eq("actorId", actorId) )
+         .add( Restrictions.eq("isOpen", true) )
+         .add( Restrictions.ne("isSuspended", true) )
+         .addOrder( Order.asc("priority") )
+         .setCacheable(true)
+         .list();
+   }
+   
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/Transition.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/Transition.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/Transition.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,63 @@
+package org.jboss.seam.bpm;
+
+import static org.jboss.seam.annotations.Install.BUILT_IN;
+
+import java.io.Serializable;
+
+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.contexts.Contexts;
+import org.jboss.seam.core.AbstractMutable;
+
+/**
+ * Allows the application to set the jBPM transition to be used when
+ * an <tt>@EndTask</tt> method is encountered.
+ * 
+ * @see org.jboss.seam.annotations.bpm.EndTask
+ * 
+ * @author Gavin King
+ */
+ at Name("org.jboss.seam.bpm.transition")
+ at Scope(ScopeType.CONVERSATION)
+ at BypassInterceptors
+ at Install(precedence=BUILT_IN, dependencies="org.jboss.seam.bpm.jbpm")
+public class Transition extends AbstractMutable implements Serializable 
+{
+   private static final long serialVersionUID = -3054558654376670239L;
+   
+   private String name;
+   
+   public String getName() 
+   {
+      return name;
+   }
+   
+   /**
+    * Set the jBPM transition name
+    */
+   public void setName(String name) 
+   {
+      setDirty(this.name, name);
+      this.name = name;
+   }
+   
+   public static Transition instance()
+   {
+      if ( !Contexts.isApplicationContextActive() )
+      {
+         throw new IllegalStateException("No active application context");
+      }
+      return (Transition) Component.getInstance(Transition.class, ScopeType.CONVERSATION);
+   }
+   
+   @Override
+   public String toString()
+   {
+      return "Transition(" + name + ")";
+   }
+   
+}

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/jbpm.pageflow.cfg.xml
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/jbpm.pageflow.cfg.xml	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/jbpm.pageflow.cfg.xml	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,3 @@
+<pageflow-configuration>
+  <jbpm-context />
+</pageflow-configuration>

Added: modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/package-info.java
===================================================================
--- modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/package-info.java	                        (rev 0)
+++ modules/trunk/bpm/src/main/java/org/jboss/seam/bpm/package-info.java	2009-04-24 23:24:14 UTC (rev 10631)
@@ -0,0 +1,17 @@
+/**
+ * A set of Seam components for business process management
+ * via jBPM, including control of process and task instances,
+ * rendering of task lists, and integration with jBPM.
+ * 
+ * The application may call components in this package
+ * directly, or via EL, or may use them indirectly via
+ * the annotations in org.jboss.seam.annotations.
+ * 
+ * @see org.jboss.seam.annotations.bpm
+ */
+ at Namespace(value="http://jboss.com/products/seam/bpm", prefix="org.jboss.seam.bpm")
+ at AutoCreate
+package org.jboss.seam.bpm;
+
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.Namespace;




More information about the seam-commits mailing list