[jboss-cvs] jboss-seam/src/main/org/jboss/seam/pageflow ...

Gavin King gavin.king at jboss.com
Wed Jun 20 01:34:33 EDT 2007


  User: gavin   
  Date: 07/06/20 01:34:33

  Added:       src/main/org/jboss/seam/pageflow     Pageflow.java
  Removed:     src/main/org/jboss/seam/pageflow     PageflowHelper.java
                        PageflowParser.java jbpm.pageflow.cfg.xml
  Log:
  reorg pageflow stuff
  make some things package-private
  
  Revision  Changes    Path
  1.1      date: 2007/06/20 05:34:33;  author: gavin;  state: Exp;jboss-seam/src/main/org/jboss/seam/pageflow/Pageflow.java
  
  Index: Pageflow.java
  ===================================================================
  package org.jboss.seam.pageflow;
  
  import static org.jboss.seam.InterceptionType.NEVER;
  import static org.jboss.seam.annotations.Install.BUILT_IN;
  
  import java.io.Serializable;
  import java.util.List;
  
  import javax.faces.application.FacesMessage;
  import javax.faces.component.UIViewRoot;
  import javax.faces.context.FacesContext;
  import javax.faces.event.PhaseId;
  
  import org.jboss.seam.Component;
  import org.jboss.seam.ScopeType;
  import org.jboss.seam.annotations.Install;
  import org.jboss.seam.annotations.Intercept;
  import org.jboss.seam.annotations.Name;
  import org.jboss.seam.annotations.PerNestedConversation;
  import org.jboss.seam.annotations.Scope;
  import org.jboss.seam.bpm.Jbpm;
  import org.jboss.seam.contexts.Contexts;
  import org.jboss.seam.contexts.Lifecycle;
  import org.jboss.seam.core.AbstractMutable;
  import org.jboss.seam.core.Events;
  import org.jboss.seam.faces.FacesManager;
  import org.jboss.seam.faces.FacesMessages;
  import org.jboss.seam.faces.FacesPage;
  import org.jboss.seam.log.LogProvider;
  import org.jboss.seam.log.Logging;
  import org.jbpm.JbpmContext;
  import org.jbpm.graph.def.Action;
  import org.jbpm.graph.def.Event;
  import org.jbpm.graph.def.Node;
  import org.jbpm.graph.def.ProcessDefinition;
  import org.jbpm.graph.exe.ExecutionContext;
  import org.jbpm.graph.exe.ProcessInstance;
  
  /**
   * A Seam component that manages the current
   * jBPM ProcessInstance used for pageflow.
   * 
   * @author Gavin King
   */
  @Scope(ScopeType.CONVERSATION)
  @PerNestedConversation
  @Name("org.jboss.seam.pageflow.pageflow")
  @Intercept(NEVER)
  @Install(dependencies="org.jboss.seam.bpm.jbpm", precedence=BUILT_IN, classDependencies="javax.faces.context.FacesContext")
  public class Pageflow extends AbstractMutable implements Serializable
  {
     private static final long serialVersionUID = -2337682140346213333L;
  
     private static final LogProvider log = Logging.getLogProvider(Pageflow.class);
     
     private int counter;
     
     private ProcessInstance processInstance;
  
     public boolean isInProcess()
     {
        return processInstance!=null;
     }
  
     public ProcessInstance getProcessInstance() 
     {
        return processInstance;
     }
  
     public void setProcessInstance(ProcessInstance processInstance) 
     {
        this.processInstance = processInstance;
        setDirty();
     }
     
     public static Pageflow instance()
     {
        if ( !Contexts.isConversationContextActive() )
        {
           throw new IllegalStateException("No active conversation context");
        }
        return (Pageflow) Component.getInstance(Pageflow.class, ScopeType.CONVERSATION);
     }
     
     /**
      * Get the current counter value, used for detecting
      * illegal use of the backbutton.
      */
     public int getPageflowCounter()
     {
        return counter;
     }
     
     /**
      * Check that the current state of the pageflow matches
      * what is expected by the faces request.
      */
     public void validatePageflow() 
     {
        if ( processInstance!=null )
        {
           org.jboss.seam.faces.FacesPage page = org.jboss.seam.faces.FacesPage.instance();
           String pageflowName = page.getPageflowName();
           String pageflowNodeName = page.getPageflowNodeName();
           boolean canReposition = getPage().isBackEnabled() && 
                 getSubProcessInstance().getProcessDefinition().getName().equals(pageflowName) && //probably not necessary
                 pageflowNodeName!=null; //probably not necessary
           if (canReposition)
           {
              //check the node name to make sure we are still on the same node
              if ( !pageflowNodeName.equals( getNode().getName() ) )
              {
                 //legal use of back/forward button, so reposition
                 reposition(pageflowNodeName);
              }
           }
           else
           {
              //check the counter to detect illegal use of backbutton
              Integer pageCounter = org.jboss.seam.faces.FacesPage.instance().getPageflowCounter();
              if ( pageCounter!=null && getPageflowCounter()!=pageCounter )
              {
                 illegalNavigationError();
              }
           }
           
        }
     }
  
     private void illegalNavigationError()
     {
        FacesContext context = FacesContext.getCurrentInstance();
        navigate(context);
        illegalNavigation();
        context.renderResponse();
     }
  
     /**
      * Add a message to indicate that illegal navigation
      * occurred. May be overridden by user to perform
      * special processing.
      */
     protected void illegalNavigation()
     {
        FacesMessages.instance().addFromResourceBundleOrDefault( 
              FacesMessage.SEVERITY_WARN, 
              "org.jboss.seam.IllegalNavigation", 
              "Illegal navigation" 
           );
     }
     
     /**
      * Get the current Node of the pageflow.
      */
     public Node getNode()
     {
        if (processInstance==null) return null;
        Node node = getSubProcessInstance().getRootToken().getNode();
        if (node==null) 
        {
           throw new IllegalStateException("pageflow has not yet started");
        }
        return node;
     }
  
     public ProcessInstance getSubProcessInstance()
     {
        return getSubProcess(processInstance);
     }
     
     private static ProcessInstance getSubProcess(ProcessInstance processInstance)
     {
        ProcessInstance subProcess = processInstance.getRootToken().getSubProcessInstance();
        if (subProcess!=null)
        {
           return getSubProcess(subProcess);
        }
        else
        {
           return processInstance;
        }
     }
     
     /**
      * Reposition the pageflow at the named node.
      * 
      * @param nodeName the name of a node
      */
     public void reposition(String nodeName)
     {
        if (processInstance==null)
        {
           throw new IllegalStateException("no pageflow in progress");
        }
        ProcessInstance subProcess = getSubProcessInstance();
        Node node = subProcess.getProcessDefinition().getNode(nodeName);
        if (node==null)
        {
           throw new IllegalArgumentException(
                 "no node named: " + nodeName + 
                 " for pageflow: " + subProcess.getProcessDefinition().getName()
              );
        }
        subProcess.getRootToken().setNode(node);
        setDirty();
     }
     
     /**
      * Get the current Page of the pageflow.
      */
     public Page getPage() 
     {
        Node node = getNode();
        if ( node!=null && !(node instanceof Page) )
        {
           throw new IllegalStateException("pageflow is not currently at a <page> or <start-page> node (note that pageflows that begin during the RENDER_RESPONSE phase should use <start-page> instead of <start-state>)");
        }
        return (Page) node;
     }
     
     /**
      * Navigate to the current page.
      */
     protected void navigate(FacesContext context) 
     {
        Page page = getPage();
        if ( !page.isRedirect() )
        {
           render(context, page);
        }
        else
        {
           redirect(page);
        }
  
        counter++;
        setDirty();
     }
  
     /**
      * Redirect to the Page.
      */
     protected void redirect(Page page)
     {
        FacesManager.instance().redirect( getViewId(page) );
     }
  
     /**
      * Proceed to render the Page.
      */
     protected void render(FacesContext context, Page page)
     {
        UIViewRoot viewRoot = context.getApplication().getViewHandler()
              .createView( context, getViewId(page) );
        context.setViewRoot(viewRoot);
     }
  
     /**
      * Allows the user to extend this class and use some
      * logical naming of pages other than the JSF view id
      * in their pageflow.
      * 
      * @param page the Page object
      * @return a JSF view id
      */
     protected String getViewId(Page page)
     {
        return page.getViewId();
     }
     
     /**
      * Get the JSF view id of the current page in the
      * pageflow.
      */
     public String getPageViewId()
     {
        Page page = getPage();
        return page==null ? null : getViewId(page);
     }
  
     /**
      * Does the current node have a default transition?
      */
     public boolean hasDefaultTransition()
     {
        //we don't use jBPM's default transition,
        //instead we use the "anonymous" transition
        return getNode().getLeavingTransition(null)!=null;
     }
     
     private boolean isNullOutcome(String outcome) 
     {
        return outcome==null || "".equals(outcome);
     }
  
     public boolean hasTransition(String outcome)
     {
        return isNullOutcome(outcome) ? 
              hasDefaultTransition() : 
              getNode().getLeavingTransition(outcome)!=null;
     }
  
     /**
      * Given the JSF action outcome, perform navigation according
      * to the current pageflow.
      */
     public void navigate(FacesContext context, String outcome) 
     {
        ProcessInstance subProcess = getSubProcessInstance();
        if ( isNullOutcome(outcome) )
        {
           //if it has a default transition defined, trigger it,
           //otherwise just redisplay the page
           if ( hasDefaultTransition() )
           {
              //we don't use jBPM's default transition,
              //instead we use the "anonymous" transition
              Pageflow.signal(subProcess, null);
              navigate(context);
           }
        }
        else
        {
           //trigger the named transition
           Pageflow.signal(subProcess, outcome);
           navigate(context);
        }
        
        raiseEndEventIfNecessary();
     }
  
     protected void raiseEndEventIfNecessary()
     {
        if ( processInstance.hasEnded() )
        {
           Events.instance().raiseEvent(
                    "org.jboss.seam.endPageflow." + 
                    processInstance.getProcessDefinition().getName()
                 );
        }
     }
  
     /**
      * Process events defined in the pageflow.
      * 
      * @param type one of: "process-validations", "update-model-values",
      *                     "invoke-application", "render-response"
      */
     public void processEvents(String type)
     {
        Event event = getNode().getEvent(type);
        if (event!=null)
        {
           for ( Action action: (List<Action>) event.getActions() )
           {
              try
              {
                 action.execute( ExecutionContext.currentExecutionContext() );
              }
              catch (Exception e)
              {
                 throw new RuntimeException(e);
              }
           }
        }
     }
     
     /**
      * Begin executing a pageflow.
      * 
      * @param pageflowDefinitionName the name of the pageflow definition
      */
     public void begin(String pageflowDefinitionName)
     {
        if ( log.isDebugEnabled() )
        {
           log.debug("beginning pageflow: " + pageflowDefinitionName);
        }
        
        processInstance = Pageflow.createInstance( getPageflowProcessDefinition(pageflowDefinitionName) );
        
        //if ( Lifecycle.getPhaseId().equals(PhaseId.RENDER_RESPONSE) ) 
        //{
      	  //if a pageflow starts during the render response phase
      	  //(as a result of a @Create method), we know the navigation
      	  //handler will not get called, so we should force the
      	  //pageflow out of the start state immediately
          //TODO: this is not actually completely true, what about <s:actionLink/>
      	  //pi.signal();
        //}
        
        setDirty();
        
        raiseBeginEvent(pageflowDefinitionName);
        
        storePageflowToViewRootIfNecessary();
  
     }
  
     protected void raiseBeginEvent(String pageflowDefinitionName)
     {
        Events.instance().raiseEvent("org.jboss.seam.beginPageflow." + pageflowDefinitionName);
     }
  
     private void storePageflowToViewRootIfNecessary()
     {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        if ( facesContext!=null && Lifecycle.getPhaseId()==PhaseId.RENDER_RESPONSE )
        {
           FacesPage.instance().storePageflow();
        }
     }
     
     public String getNoConversationViewId(String pageflowName, String pageflowNodeName)
     {
        ProcessDefinition pageflowProcessDefinition = getPageflowProcessDefinition(pageflowName);
        Node node = pageflowProcessDefinition.getNode(pageflowNodeName);
        if (node!=null && node instanceof Page)
        {
           return ( (Page) node ).getNoConversationViewId();
        }
        else
        {
           return null;
        }
     }
  
     protected ProcessDefinition getPageflowProcessDefinition(String pageflowName)
     {
        ProcessDefinition pageflowProcessDefinition = Jbpm.instance().getPageflowProcessDefinition(pageflowName);
        if (pageflowProcessDefinition==null)
        {
           throw new IllegalArgumentException("pageflow definition not found: " + pageflowName);
        }
        return pageflowProcessDefinition;
     }
     
     @Override
     public String toString()
     {
        String name = processInstance==null ? 
              "null" : processInstance.getProcessDefinition().getName();
        return "Pageflow(" + name + ")";
     }
  
     private static ProcessInstance createInstance(ProcessDefinition processDefinition) 
     {
        JbpmContext jbpmContext = Jbpm.createPageflowContext();
        try 
        {
           log.debug( "new pageflow instance for definition: " + processDefinition.getName() );
           return processDefinition.createProcessInstance();
        } 
        finally 
        {
           jbpmContext.close();
        }
     }
  
     private static void signal(ProcessInstance processInstance, String outcome) 
     {
        JbpmContext jbpmContext = Jbpm.createPageflowContext();
        try 
        {
           log.debug("signaling pageflow transition for outcome: " + outcome);
           processInstance.signal(outcome);
        } 
        finally 
        {
           jbpmContext.close();
        }
     }
  
  }
  
  
  



More information about the jboss-cvs-commits mailing list