Package org.jbpm.pvm

Source Code of org.jbpm.pvm.ProcessFactory

/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jbpm.pvm;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.jbpm.PvmException;
import org.jbpm.pvm.impl.EventListenerReference;
import org.jbpm.pvm.impl.CompositeElementImpl;
import org.jbpm.pvm.impl.EventImpl;
import org.jbpm.pvm.impl.ExceptionHandlerImpl;
import org.jbpm.pvm.impl.NodeImpl;
import org.jbpm.pvm.impl.ObjectReference;
import org.jbpm.pvm.impl.ObservableElementImpl;
import org.jbpm.pvm.impl.ProcessDefinitionImpl;
import org.jbpm.pvm.impl.ProcessElementImpl;
import org.jbpm.pvm.impl.TransitionImpl;
import org.jbpm.wire.Descriptor;
import org.jbpm.wire.descriptor.StringDescriptor;

/** factory for process definitions.
*
* <p>Use this factory as a <a href="http://martinfowler.com/bliki/FluentInterface.html">fluent interface</a>
* for building a process definition.  To use it in this way, start with instantiating a ProcessFactory object.
* Then a number of methods can be invoked concatenated with dots cause all the methods return
* the same process factory object.  When done, end that sequence with
* {@link #done()} to get the constructed ProcessDefinition. 
* </p>
*
* <p>The idea is that this results into a more compact and more readable
* code to build process definitions as opposed to including xml inline.  For example :
* </p>
* <pre>
*     ProcessDefinition processDefinition = ProcessFactory.build()
*         .initial().behaviour(new WaitState())
*           .transition("normal").to("a")
*           .transition("shortcut").to("c")
*         .node("a").behaviour(new WaitState())
*           .transition().to("b")
*         .node("b").behaviour(new WaitState())
*           .transition().to("c")
*         .node("c").behaviour(new WaitState())
*     .done();
* </pre>
*
* <hr />
*
* <p>If more control is needed over the creation of the process definition
* objects, then consider using the concrete implementation classes from
* package {@link org.jbpm.pvm.impl} directly.  The implementation code
* of this class might be a good guide to get you on your way.
* </p>
*
* @author Tom Baeyens
*/
public class ProcessFactory {
 
  // static factory methods ///////////////////////////////////////////////////
 
  protected ProcessDefinitionImpl processDefinition;
  protected NodeImpl node;
  protected TransitionImpl transition;
  protected List<DestinationReference> destinationReferences;
  protected ObservableElementImpl observableElement;
  protected EventImpl event;
  protected EventListenerReference eventListenerReference;
  protected ExceptionHandlerImpl exceptionHandler;
  protected CompositeElementImpl compositeElement;
  protected Stack<CompositeElementImpl> compositeElementStack;
 
  /** start building a process definition without a name. */
  protected ProcessFactory() {
    this(null);
  }

  /** start building a process definition with the given name. */
  protected ProcessFactory(String processName) {
    this(processName, null);
  }

  /** start building a process definition with the given name. */
  protected ProcessFactory(String processName, ProcessDefinitionImpl processDefinition) {
    if (processDefinition!=null) {
      this.processDefinition = processDefinition;
    } else {
      this.processDefinition = instantiateProcessDefinition();
    }
    this.processDefinition.setName(processName);
    this.observableElement = this.processDefinition;
    this.compositeElement = this.processDefinition;
  }
 
  /** starts building a process definition */
  public static ProcessFactory build() {
    return new ProcessFactory();
  }
 
  /** starts building a process definition */
  public static ProcessFactory build(String processName) {
    return new ProcessFactory(processName);
  }

  /** starts populating a given process definition */
  public static ProcessFactory build(String processName, ProcessDefinitionImpl processDefinition) {
    return new ProcessFactory(processName, processDefinition);
  }

  /** to be overwritten by specific process language factories */
  protected ProcessDefinitionImpl instantiateProcessDefinition() {
    return new ProcessDefinitionImpl();
  }

  /** marks the last created node as the initial node in the process. */
  public ProcessFactory initial() {
    if (node==null) {
      throw new PvmException("no current node");
    }
    if (processDefinition.getInitial()!=null) {
      throw new PvmException("duplicate initial node");
    }
    processDefinition.setInitial(node);
    return this;
  }

  /** creates a node in the current parent.
   * The current parent is either the process definition or a composite node
   * in case method {@link #compositeNode(String)} was called previously. */
  public ProcessFactory node() {
    return node(null);
  }
 
  /** creates a named node.
  * The current parent is either the process definition or a composite node
  * in case method {@link #compositeNode(String)} was called previously. */
  public ProcessFactory node(String nodeName) {
    if (exceptionHandler!=null) {
      exceptionHandler.setNodeName(nodeName);
    } else {
      node = compositeElement.createNode(nodeName);

      observableElement = node;
      event = null;
      eventListenerReference = null;
      transition = null;
      exceptionHandler = null;
    }
    return this;
  }
 
  /** sets the behaviour on the current node.
   * A current node is required. */
  public ProcessFactory behaviour(Activity activity) {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }
    if (node==null) {
      throw new PvmException("no current node");
    }
    node.setBehaviour(activity);
    return this;
  }

  /** sets the asyncExecute property on the current node.
   * A current node is required. */
  public ProcessFactory asyncExecute() {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }
    if (node==null) {
      throw new PvmException("no current node");
    }
    node.setExecutionAsync(true);
    return this;
 

  /** sets the asyncLeave property on the current node.
   * A current node is required. */
  public ProcessFactory asyncLeave() {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }
    if (node==null) {
      throw new PvmException("no current node");
    }
    node.setLeaveAsync(true);
    return this;
 

  /** sets the asyncSignal property on the current node.
   * A current node is required. */
  public ProcessFactory asyncSignal() {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }
    if (node==null) {
      throw new PvmException("no current node");
    }
    node.setSignalAsync(true);
    return this;
 

  /** sets the property needsPrevious on the current node.
   * A current node is required. */
  public ProcessFactory needsPrevious() {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }
    if (node==null) {
      throw new PvmException("no current node");
    }
    node.setPreviousNeeded(true);
    return this;
  }

  /** starts a block in which nested nodes can be created.
   * This block can be ended with {@link #compositeEnd()}.
   * A current node is required. */
  public ProcessFactory compositeNode() {
    return compositeNode(null);
  }

  /** starts a block in which nested nodes can be created.
   * This block can be ended with {@link #compositeEnd()}.
   * A current node is required. */
  public ProcessFactory compositeNode(String nodeName) {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }

    if (compositeElementStack==null) {
      compositeElementStack = new Stack<CompositeElementImpl>();
    }
   
    compositeElementStack.push(compositeElement);
    node(nodeName);
    compositeElement = node;

    return this;
  }
 
  /** ends a block in which nested nodes are created.
   * This method requires that a nested node block was started before
   * with {@link #compositeNode(String)} */
  public ProcessFactory compositeEnd() {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }

    if (compositeElementStack==null) {
      throw new PvmException("no composite node was started");
    }

    compositeElement = compositeElementStack.pop();
   
    if (compositeElementStack.isEmpty()) {
      compositeElementStack = null;
    }
   
    return this;
  }

  /** creates a transition on the current node. 
   * This method requires a current node */
  public ProcessFactory transition() {
    return transition(null);
  }
 
  /** creates a named transition on the current node. 
   * This method requires a current node */
  public ProcessFactory transition(String transitionName) {
    if (exceptionHandler!=null) {
      exceptionHandler.setTransitionName(transitionName);
    } else {
      if (node==null) {
        throw new PvmException("no current node");
      }
      transition = node.createOutgoingTransition(null, transitionName);
      observableElement = transition;
      event = null;
      eventListenerReference = null;
      exceptionHandler = null;
    }
    return this;
  }

  /** sets the takeAsync property on the current transition
   * This method requires a current transition. */
  public ProcessFactory asyncTake() {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }
    if (transition==null) {
      throw new PvmException("no current transition");
    }
    transition.setTakeAsync(true);
    return this;
  }

  /** sets the destination node on the current transition.
   * This method requires a current transition. */
  public ProcessFactory to(String destination) {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }
    if (transition==null) {
      throw new PvmException("no current transition");
    }
    if (destinationReferences==null) {
      destinationReferences = new ArrayList<DestinationReference>();
    }
    destinationReferences.add(new DestinationReference(transition, destination));
    return this;
  }

  /** sets the wait condition on the current transition.
   * This method requires a current transition. */
  public ProcessFactory waitCondition(Condition condition) {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }
    if (transition==null) {
      throw new PvmException("no current transition");
    }
    ObjectReference<Condition> waitConditionReference = new ObjectReference<Condition>(condition);
    transition.setWaitConditionReference(waitConditionReference);
    return this;
  }

  /** sets the guard condition on the current transition.
   * This method requires a current transition. */
  public ProcessFactory guardCondition(Condition condition) {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }
    if (transition==null) {
      throw new PvmException("no current transition");
    }
    ObjectReference<Condition> guardConditionReference = new ObjectReference<Condition>(condition);
    transition.setGuardConditionReference(guardConditionReference);
    return this;
  }

  /** creates the given event on the current process element.
   * This method requires a process element.  A process element is
   * either a process definition or a node.  This method doesn't need to be
   * called for transitions. If you have exception handlers and listeners
   * on an event, make sure that you put the invocations of
   * {@link #exceptionHandler(Class)} first. */
  public ProcessFactory event(String eventName) {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }
    if (observableElement==null) {
      throw new PvmException("no current process element");
    }
    if (observableElement instanceof Transition) {
      throw new PvmException("for actions on transitions, you don't need to call event");
    }
    event = observableElement.createEvent(eventName);
    exceptionHandler = null;
    return this;
  }

  /** creates an exception handler for the given exception class on the current process element;
   * until the {@link #exceptionHandlerEnd()}.  Subsequent invocations of
   * {@link #listener(Activity) listeners} or {@link #transition() transitions} will
   * have the created exception handler as a target.
   *
   * DONT'T FORGET TO CLOSE THE EXCEPTION HANDLER WITH exceptionHandlerEnd. */
  public ProcessFactory exceptionHandler(Class<? extends Exception> exceptionClass) {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }
   
    ProcessElementImpl processElement = null;
    if (eventListenerReference!=null) {
      processElement = eventListenerReference;
    } else if (event!=null) {
      processElement = event;
    } else if (observableElement!=null) {
      processElement = observableElement;
    } else {
      throw new PvmException("no current process element, event or action");
    }
   
    exceptionHandler = processElement.createExceptionHandler();

    if (exceptionClass!=null) {
      exceptionHandler.setExceptionClassName(exceptionClass.getName());
    }

    return this;
  }
 
  public ProcessFactory exceptionHandlerEnd() {
    exceptionHandler = null;
    return this;
  }
 
  public ProcessFactory transactional() {
    if (exceptionHandler==null) {
      throw new PvmException("transactional is a property of an exception handler");
    }
    exceptionHandler.setTransactional(true);
    return this;
  }
 
  /** adds an action to the current event.  The current event was either
   * created by {@link #event(String)} or by a {@link #transition()}.
   * Subsequent invocations of {@link #exceptionHandler(Class)} will
   * be associated to this event listener. */
  public ProcessFactory listener(Descriptor descriptor) {
    if (exceptionHandler!=null) {
      exceptionHandler.createActivityReference(descriptor);
    } else {
      getEvent().createListenerReference(descriptor);
    }
    return this;
  }

  /** adds an action to the current event.  The current event was either
   * created by {@link #event(String)} or by a {@link #transition()}.
   * Subsequent invocations of {@link #exceptionHandler(Class)} will
   * be associated to this event listener. */
  public ProcessFactory listener(Activity activity) {
    if (exceptionHandler!=null) {
      exceptionHandler.createActivityReference(activity);
    } else {
      eventListenerReference = getEvent().createListenerReference(activity);
    }
    return this;
  }

  /** adds an action to the current event.  The current event was either
   * created by {@link #event(String)} or by a {@link #transition()}.
   * Subsequent invocations of {@link #exceptionHandler(Class)} will
   * be associated to this event listener. */
  public ProcessFactory listener(String expression) {
    if (exceptionHandler!=null) {
      exceptionHandler.createActivityReference(expression);
    } else {
      eventListenerReference = getEvent().createListenerReference(expression);
    }
    return this;
  }
 
  /** disables propagated events.  This means that this action will only be executed
   * if the event is fired on the actual process element of the event.  The current
   * action will not be executed if an event is fired on one of the children of the
   * process element to which this event relates. */
  public ProcessFactory propagationDisabled() {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }
    if (eventListenerReference==null) {
      throw new PvmException("no current event action");
    }
    eventListenerReference.setPropagationEnabled(false);
    return this;
  }

  private EventImpl getEvent() {
    if ( (event==null)
         && (observableElement instanceof TransitionImpl)
       ) {
      event = ((TransitionImpl)observableElement).createEvent();
      return event;
    }
    if (event==null) {
      throw new PvmException("no current event");
    }
    return event;
  }

  /** adds a string-valued configuration to the current process element */
  public ProcessFactory property(String name, String stringValue) {
    StringDescriptor stringDescriptor = new StringDescriptor();
    stringDescriptor.setName(name);
    stringDescriptor.setValue(stringValue);
    property(stringDescriptor);
    return this;
  }

  /** adds a configuration to the current process element */
  public ProcessFactory property(Descriptor descriptor) {
    if (exceptionHandler!=null) {
      throw new PvmException("exceptionHandler needs to be closed with exceptionHandlerEnd");
    }
    if (observableElement==null) {
      throw new PvmException("no current process element");
    }
    if (event!=null) {
      event.addProperty(descriptor);
    } else {
      observableElement.addProperty(descriptor);
    }
    return this;
  }

  public class DestinationReference {
    TransitionImpl transition;
    String destinationName;
    public DestinationReference(TransitionImpl transition, String destinationName) {
      this.transition = transition;
      this.destinationName = destinationName;
    }
    public void resolve() {
      NodeImpl destination = (NodeImpl) processDefinition.findNode(destinationName);
      if (destination==null) {
        throw new PvmException("couldn't find destination node "+destinationName+" for "+transition);
      }
      destination.addIncomingTransition(transition);
      transition.setDestination(destination);
    }
  }

  /** extract the process definition from the factory.  This should be
   * the last method in the chain of subsequent invoked methods on this
   * factory object. */
  public ProcessDefinition done() {
    resolveDestinations();
    return processDefinition;
  }
 
  private void resolveDestinations() {
    if (destinationReferences!=null) {
      for (DestinationReference destinationReference : destinationReferences) {
        destinationReference.resolve();
      }
    }
  }
}
TOP

Related Classes of org.jbpm.pvm.ProcessFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.