All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ow2.bonita.pvm.model.ProcessFactory Maven / Gradle / Ivy

/*
 * 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.ow2.bonita.pvm.model;

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

import org.ow2.bonita.pvm.ProcessDefinition;
import org.ow2.bonita.pvm.activity.Activity;
import org.ow2.bonita.pvm.client.ClientProcessDefinition;
import org.ow2.bonita.pvm.internal.model.CompositeElementImpl;
import org.ow2.bonita.pvm.internal.model.EventImpl;
import org.ow2.bonita.pvm.internal.model.EventListenerReference;
import org.ow2.bonita.pvm.internal.model.ExceptionHandlerImpl;
import org.ow2.bonita.pvm.internal.model.NodeImpl;
import org.ow2.bonita.pvm.internal.model.ObservableElementImpl;
import org.ow2.bonita.pvm.internal.model.PVMProcessDefinitionImpl;
import org.ow2.bonita.pvm.internal.model.ProcessElementImpl;
import org.ow2.bonita.pvm.internal.model.TimerDefinitionImpl;
import org.ow2.bonita.pvm.internal.model.TransitionImpl;
import org.ow2.bonita.pvm.internal.model.VariableDefinitionImpl;
import org.ow2.bonita.pvm.internal.wire.Descriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.ObjectDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.ProvidedObjectDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.StringDescriptor;
import org.ow2.bonita.pvm.listener.EventListener;
import org.ow2.bonita.util.BonitaRuntimeException;
import org.ow2.bonita.util.ExceptionManager;

/**
 * factory for process definitions.
 * 
 * 

* Use this factory as a fluent * interface 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. *

* *

* 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 : *

* *
 * ProcessDefinition processDefinition = ProcessFactory.build().node().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();
 * 
* *
* *

* If more control is needed over the creation of the process definition * objects, then consider using the concrete implementation classes from package * {@link org.ow2.bonita.pvm.internal.model} directly. The implementation code * of this class might be a good guide to get you on your way. *

* * @author Tom Baeyens */ public class ProcessFactory { // static factory methods /////////////////////////////////////////////////// protected PVMProcessDefinitionImpl processDefinition; protected NodeImpl node; protected TransitionImpl transition; protected List destinationReferences; protected ObservableElementImpl observableElement; protected EventImpl event; protected EventListenerReference eventListenerReference; protected ExceptionHandlerImpl exceptionHandler; protected CompositeElementImpl compositeElement; protected CompositeElementImpl scope; protected Stack 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, PVMProcessDefinitionImpl processDefinition) { if (processDefinition != null) { this.processDefinition = processDefinition; } else { this.processDefinition = instantiateProcessDefinition(); } this.processDefinition.setName(processName); this.observableElement = this.processDefinition; this.compositeElement = this.processDefinition; this.scope = 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, PVMProcessDefinitionImpl processDefinition) { return new ProcessFactory(processName, processDefinition); } /** to be overwritten by specific process language factories */ protected PVMProcessDefinitionImpl instantiateProcessDefinition() { return new PVMProcessDefinitionImpl(); } /** marks the last created node as the initial node in the process. */ public ProcessFactory initial() { if (node == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_1"); throw new BonitaRuntimeException(message); } if (processDefinition.getInitial() != null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_2"); throw new BonitaRuntimeException(message); } processDefinition.setInitial(node); return this; } /** * applies on a node and makes it create a local activity instance scope. This * is automatically implied when {@link #variable(String) adding a variable} or * {@link #timer() adding a timer} */ public ProcessFactory scope() { if (node == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_3"); throw new BonitaRuntimeException(message); } node.setLocalScope(true); scope = node; return this; } /** declares a local variable. {@link #scope()} is automatically implied. */ public ProcessFactory variable(String key) { if (node != null) { scope(); } VariableDefinitionImpl variableDefinition = compositeElement .createVariableDefinition(); variableDefinition.setKey(key); return this; } /** declares a local variable. {@link #scope()} is automatically implied. */ public ProcessFactory variable(Descriptor sourceDescriptor) { if (node != null && scope == null) { scope(); } VariableDefinitionImpl variableDefinition = scope .createVariableDefinition(); variableDefinition.setKey(sourceDescriptor.getName()); variableDefinition.setSourceDescriptor(sourceDescriptor); return this; } /** declares a local variable. {@link #scope()} is automatically implied. */ public ProcessFactory variable(String key, String initialValue) { return variable(new StringDescriptor(key, initialValue)); } /** * declares a timer on the current node or process. {@link #scope()} is * automatically implied. */ public ProcessFactory timer(String dueDateDescription, String signalName) { return timer(dueDateDescription, null, signalName, null); } /** * declares a timer on the current node or process. {@link #scope()} is * automatically implied. */ public ProcessFactory timer(String dueDateDescription, String signalName, String repeat) { return timer(dueDateDescription, null, signalName, repeat); } /** * declares a timer on the current node or process. {@link #scope()} is * automatically implied. */ public ProcessFactory timer(Date dueDate, String signalName) { return timer(null, dueDate, signalName, null); } protected ProcessFactory timer(String dueDateDescription, Date dueDate, String signalName, String repeat) { if (node != null && scope == null) { scope(); } TimerDefinitionImpl timerDefinition = scope.createTimerDefinition(); if (dueDate != null) { timerDefinition.setDueDate(dueDate); } else { timerDefinition.setDueDateDescription(dueDateDescription); } timerDefinition.setSignalName(signalName); timerDefinition.setRepeat(repeat); 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); scope = null; 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) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_4"); throw new BonitaRuntimeException(message); } if (node == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_5"); throw new BonitaRuntimeException(message); } node.setBehaviour(activity); return this; } /** * sets the behaviour on the current node. A current node is required. */ public ProcessFactory behaviour(Descriptor descriptor) { if (exceptionHandler != null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_6"); throw new BonitaRuntimeException(message); } if (node == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_7"); throw new BonitaRuntimeException(message); } node.setBehaviour(descriptor); return this; } /** * sets the behaviour on the current node. A current node is required. */ public ProcessFactory behaviour(Class activityClass) { return behaviour(new ObjectDescriptor(activityClass)); } /** * sets the behaviour on the current node. A current node is required. */ public ProcessFactory behaviour(String expression) { if (exceptionHandler != null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_8"); throw new BonitaRuntimeException(message); } if (node == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_9"); throw new BonitaRuntimeException(message); } node.setBehaviour(expression); return this; } /** * sets the asyncExecute property on the current node. A current node is * required. */ public ProcessFactory asyncExecute() { if (exceptionHandler != null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_10"); throw new BonitaRuntimeException(message); } if (node == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_11"); throw new BonitaRuntimeException(message); } node.setExecutionAsync(true); return this; } /** * sets the asyncLeave property on the current node. A current node is * required. */ public ProcessFactory asyncLeave() { if (exceptionHandler != null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_12"); throw new BonitaRuntimeException(message); } if (node == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_13"); throw new BonitaRuntimeException(message); } node.setLeaveAsync(true); return this; } /** * sets the asyncSignal property on the current node. A current node is * required. */ public ProcessFactory asyncSignal() { if (exceptionHandler != null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_14"); throw new BonitaRuntimeException(message); } if (node == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_15"); throw new BonitaRuntimeException(message); } node.setSignalAsync(true); return this; } /** * sets the property needsPrevious on the current node. A current node is * required. */ public ProcessFactory needsPrevious() { if (exceptionHandler != null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_16"); throw new BonitaRuntimeException(message); } if (node == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_17"); throw new BonitaRuntimeException(message); } 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) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_18"); throw new BonitaRuntimeException(message); } if (compositeElementStack == null) { compositeElementStack = new Stack(); } 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) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_19"); throw new BonitaRuntimeException(message); } if (compositeElementStack == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_20"); throw new BonitaRuntimeException(message); } 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) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_21"); throw new BonitaRuntimeException(message); } 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) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_22"); throw new BonitaRuntimeException(message); } if (transition == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_23"); throw new BonitaRuntimeException(message); } 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) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_24"); throw new BonitaRuntimeException(message); } if (transition == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_25"); throw new BonitaRuntimeException(message); } if (destinationReferences == null) { destinationReferences = new ArrayList(); } 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) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_26"); throw new BonitaRuntimeException(message); } if (transition == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_27"); throw new BonitaRuntimeException(message); } Descriptor conditionDescriptor = new ProvidedObjectDescriptor(condition); transition.setWaitConditionDescriptor(conditionDescriptor); return this; } /** * sets the guard condition on the current transition. This method requires a * current transition. */ public ProcessFactory guardCondition(Condition condition) { if (exceptionHandler != null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_28"); throw new BonitaRuntimeException(message); } if (transition == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_29"); throw new BonitaRuntimeException(message); } Descriptor conditionDescriptor = new ProvidedObjectDescriptor(condition); transition.setConditionDescriptor(conditionDescriptor); 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) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_30"); throw new BonitaRuntimeException(message); } if (observableElement == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_31"); throw new BonitaRuntimeException(message); } if (observableElement instanceof Transition) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_32"); throw new BonitaRuntimeException(message); } 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 exceptionClass) { if (exceptionHandler != null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_33"); throw new BonitaRuntimeException(message); } ProcessElementImpl processElement = null; if (eventListenerReference != null) { processElement = eventListenerReference; } else if (event != null) { processElement = event; } else if (observableElement != null) { processElement = observableElement; } else { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_34"); throw new BonitaRuntimeException(message); } 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) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_35"); throw new BonitaRuntimeException(message); } 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.createEventListenerReference(descriptor); } else { getEvent().createEventListenerReference(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(EventListener eventListener) { if (exceptionHandler != null) { exceptionHandler.createEventListenerReference(eventListener); } else { eventListenerReference = getEvent().createEventListenerReference( eventListener); } 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().createEventListenerReference( 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) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_36"); throw new BonitaRuntimeException(message); } if (eventListenerReference == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_37"); throw new BonitaRuntimeException(message); } eventListenerReference.setPropagationEnabled(false); return this; } private EventImpl getEvent() { if ((event == null) && (observableElement instanceof TransitionImpl)) { event = ((TransitionImpl) observableElement).createEvent(); return event; } if (event == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_38"); throw new BonitaRuntimeException(message); } 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) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_39"); throw new BonitaRuntimeException(message); } if (observableElement == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_40"); throw new BonitaRuntimeException(message); } 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) { String message = ExceptionManager.getInstance().getFullMessage( "pb_PF_41", destinationName, transition); throw new BonitaRuntimeException(message); } 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 ClientProcessDefinition done() { resolveDestinations(); if (processDefinition.getInitial() == null) { String message = ExceptionManager.getInstance().getFullMessage("bp_PF_42"); throw new BonitaRuntimeException(message); } return processDefinition; } /** * sets the {@link ProcessDefinition#getVersion() version} of the process * definition explicitely */ public ProcessFactory version(int version) { processDefinition.setVersion(version); return this; } /** * sets the {@link ProcessDefinition#getKey() key} of the process definition * explicitely */ public ProcessFactory key(String key) { processDefinition.setKey(key); return this; } private void resolveDestinations() { if (destinationReferences != null) { for (DestinationReference destinationReference : destinationReferences) { destinationReference.resolve(); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy