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

org.ow2.bonita.deployment.PVMProcessBuilder Maven / Gradle / Ivy

/**
 * Copyright (C) 2006  Bull S. A. S.
 * Bull, Rue Jean Jaures, B.P.68, 78340, Les Clayes-sous-Bois
 * This library 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
 * version 2.1 of the License.
 * This library 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
 * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA  02110-1301, USA.
 **/
package org.ow2.bonita.deployment;

import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.ow2.bonita.definition.InternalProcess;
import org.ow2.bonita.definition.activity.AbstractActivity;
import org.ow2.bonita.definition.activity.AutomaticActivity;
import org.ow2.bonita.definition.activity.ConditionDefinition;
import org.ow2.bonita.definition.activity.EndingNodeBehaviour;
import org.ow2.bonita.definition.activity.InitialNodeBehaviour;
import org.ow2.bonita.definition.activity.SubFlow;
import org.ow2.bonita.definition.activity.Task;
import org.ow2.bonita.facade.def.element.DeadlineDefinition;
import org.ow2.bonita.facade.def.element.MultiInstantiationDefinition;
import org.ow2.bonita.facade.def.majorElement.ActivityDefinition;
import org.ow2.bonita.facade.def.majorElement.DataFieldDefinition;
import org.ow2.bonita.facade.def.majorElement.ProcessDefinition;
import org.ow2.bonita.facade.def.majorElement.TransitionDefinition;
import org.ow2.bonita.facade.def.majorElement.ActivityDefinition.JoinType;
import org.ow2.bonita.facade.def.majorElement.impl.ActivityDefinitionImpl;
import org.ow2.bonita.pvm.activity.ExternalActivity;
import org.ow2.bonita.pvm.internal.model.CompositeElementImpl;
import org.ow2.bonita.pvm.internal.model.NodeImpl;
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.ArgDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.BooleanHelper;
import org.ow2.bonita.pvm.internal.wire.descriptor.CharacterDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.DoubleDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.FloatDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.IntegerDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.LongDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.NullDescriptor;
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.ShortDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.StringDescriptor;
import org.ow2.bonita.pvm.model.Condition;
import org.ow2.bonita.pvm.model.Node;
import org.ow2.bonita.pvm.model.Transition;
import org.ow2.bonita.util.DateUtil;
import org.ow2.bonita.util.EnvTool;

/**
 *
 * @author Marc Blachon, Guillaume Porcher, Charles Souillard, Miguel Valdes, Pierre Vigneras
 */
public final class PVMProcessBuilder {

  private static final Logger LOG = Logger.getLogger(PVMProcessBuilder.class.getName());
  public static final String BONITA_INIT = "BonitaInit";
  public static final String BONITA_END = "BonitaEnd";

  private PVMProcessBuilder() { }

  public static InternalProcess createProcess(final ProcessDefinition processDef) {   
    final InternalProcess process = new InternalProcess(processDef.getUUID());
    createNodes(processDef, process);
    initializeProcessVariables(processDef, process);

    IterationDetection.findIterations(process);
    return process;
  }

  protected static void createNodes(final ProcessDefinition processDef, final InternalProcess process) {
    final Set activities = getAllActivities(processDef);

    //map of created activities : key = activity.id and value = corresponding node
    final Map createdNodes = new HashMap();
    for (final ActivityDefinition activity : activities) {
      final NodeImpl node = createNode(activity, processDef, process);
      createdNodes.put(activity.getName(), node);
    }
    createTransitions(processDef, createdNodes);


    // INITIAL / ENDING node creation
    String initialEndCreationError = null;
    //create initial split
    NodeImpl initialNode = null;
    if (createdNodes.containsKey(BONITA_INIT)) {
      initialNode = createdNodes.get(BONITA_INIT);
    } else {
      initialNode = process.createNode(BONITA_INIT);
      final Set initialActivities = getInitialActivities(processDef, activities);
      if (!initialActivities.isEmpty()) {
        //normal case, this process has at least one activity
        //create a node for each initialActivity and create a transition from initialNode to initialActivities
        for (final ActivityDefinition initialActivity : initialActivities) {
          final NodeImpl initialActivityNode = createdNodes.get(initialActivity.getName());
          initialNode.createOutgoingTransition(initialActivityNode, "initialSplit_"
              + initialNode.getName() + "_to_" + initialActivityNode.getName());
        }
      } else {
        if (!createdNodes.isEmpty()) {
          //there is a problem, there are activities, but none of them are initial activities
          //maybe a cycle ?
          initialEndCreationError = "no initial";
        }
      }
    }
    ActivityDefinitionImpl initialActivity = new ActivityDefinitionImpl(processDef.getUUID(), BONITA_INIT, null, null);
    final ExternalActivity initialRoute = new InitialNodeBehaviour(initialActivity);
    initialNode.setBehaviour(initialRoute);
    initialNode.setPreviousNeeded(true);
    process.setInitial(initialNode);

    //create ending join
    NodeImpl endingNode = null;
    if (createdNodes.containsKey(BONITA_END)) {
      endingNode = createdNodes.get(BONITA_END);
    } else {
      endingNode = process.createNode(BONITA_END);

      final Set endingActivities = getEndingActivities(processDef, activities);
      if (!endingActivities.isEmpty()) {
        //normal case, this process has at least one activity
        //create node for each endingActivity and create a transition from ending activities to endingNode
        for (final ActivityDefinition endingActivity : endingActivities) {
          final NodeImpl endingActivityNode = createdNodes.get(endingActivity.getName());
          endingActivityNode.createOutgoingTransition(endingNode, "endingJoin_"
              + endingActivityNode.getName() + "_to_" + endingNode.getName());
        }
      } else {
        if (!createdNodes.isEmpty()) {
          //there is a problem, there are activities, but none of them are ending activities
          //maybe a cycle ?
          if (initialEndCreationError != null) {
            initialEndCreationError += " and no ending";
          } else {
            initialEndCreationError = "no ending";
          }
          throw new DeploymentRuntimeException("Error in process definition: " + initialEndCreationError
              + " node found. There is maybe a cycle in activities definition");
        }
        //empty process (no activity)
        initialNode.createOutgoingTransition(endingNode, "initialSplit_" + initialNode.getName() + "_to_" + endingNode.getName());
      }
    }
    if (initialEndCreationError != null) {
      throw new DeploymentRuntimeException("Error in process definition: " + initialEndCreationError
          + " node found. There is maybe a cycle in activities definition");
    }

    ActivityDefinitionImpl endActivity = new ActivityDefinitionImpl(processDef.getUUID(), BONITA_END, null, null);
    endActivity.setJoinType(JoinType.XOR);
    final ExternalActivity endingRoute = new EndingNodeBehaviour(endActivity);
    endingNode.setPreviousNeeded(true);
    endingNode.setBehaviour(endingRoute);


    // Check path from init to end
    final Set activityNames = new HashSet();
    activityNames.add(BONITA_END);
    for (final ActivityDefinition act : activities) {
      activityNames.add(act.getName());
    }
    activityNames.remove(BONITA_INIT);
    if (!checkReachable(activityNames, initialNode)) {
      throw new DeploymentRuntimeException("There is no path from " + BONITA_INIT + " to " + BONITA_END);
    }
    if (!activityNames.isEmpty()) {
      throw new DeploymentRuntimeException("These activities " + activityNames + " cannot be executed."
          + " There is no path from " + BONITA_INIT + " to them.");
    }
  }

  private static boolean checkReachable(final Set nodeNames, final Node currentNode) {
    boolean endReached = currentNode.getName().equals(BONITA_END);
    final List trs = currentNode.getOutgoingTransitions();
    if (trs != null) {
      for (final Transition t : trs) {
        if (nodeNames.remove(t.getDestination().getName())) {
          endReached = checkReachable(nodeNames, t.getDestination()) || endReached;
        }
      }
    }
    return endReached;
  }

  protected static void createTransitions(final ProcessDefinition processDef,
      final Map createdNodes) {
    final Set transitions = processDef.getTransitions();
    if (transitions != null) {
      for (final TransitionDefinition transition : transitions) {
        final String toActivityId = transition.getTo();
        final String fromActivityId = transition.getFrom();
        String condition = transition.getCondition();
        if (!activityWithIdExist(processDef, toActivityId)) {
          throw new DeploymentRuntimeException("Transition " + transition.getName() + " is using activity "
              + toActivityId + " in to attribute. There is no activity with this uuid in the defined workflowprocess.");
        }
        if (!activityWithIdExist(processDef, fromActivityId)) {
          throw new DeploymentRuntimeException("Transition " + transition.getName() + " is using activity "
              + fromActivityId + " in from attribute. There is no activity with this uuid in the defined workflowprocess.");
        }
        final NodeImpl fromNode = createdNodes.get(fromActivityId);
        final NodeImpl toNode = createdNodes.get(toActivityId);

        final String transitionName = fromActivityId + "_" + toActivityId;
        final TransitionImpl pvmTransition = fromNode.createOutgoingTransition(toNode, transitionName);
        if (LOG.isLoggable(Level.FINE)) {
          LOG.fine("**** created transition = " + transitionName);
        }

        // check/set if condition on the transition
        if (condition != null) {
          final Condition guardCondition = new ConditionDefinition(condition);
          pvmTransition.setConditionDescriptor(new ProvidedObjectDescriptor(guardCondition));
        }
      }
    }
  }

  protected static NodeImpl createNode(final ActivityDefinition activity, final ProcessDefinition processDef,
      final InternalProcess process) {
    final NodeImpl node = process.createNode(activity.getName());
    if (activity.isAsynchronous()) {
      node.setExecutionAsync(true);
    }
    AbstractActivity abstractActivity = null;

    //createVariableDefinitionsFromDataFields(node, activity.getDataFields());

    if (activity.isAutomatic() && activity.isSubflow()) {
      final String subProcessId = activity.getSubflowProcessName();

      final InternalProcess externalSubProcess = EnvTool.getRepository().findLatestProcessByName(subProcessId);
      if (externalSubProcess == null) {
        throw new DeploymentRuntimeException("Unable to find process \"" + subProcessId + "\" used in subFlow. "
            + "If the subFlow process is in an external package, "
            + "please check that this package has already been deployed");
      }
      abstractActivity = new SubFlow(activity);
    } else if (activity.isAutomatic()) {
      abstractActivity = new AutomaticActivity(activity);
    } else if (!activity.isAutomatic()) {
      abstractActivity = new Task(activity);
    }

    createTimerDefinitions(activity, node);
    
    final MultiInstantiationDefinition multiInstantiationDefinition = activity.getMultiInstantiationDefinition();
    if (multiInstantiationDefinition != null) {
      final String variableId = multiInstantiationDefinition.getVariableName();
      boolean variableExists = false;
      if (activity.getDataFields() != null) {
        for (final DataFieldDefinition var : activity.getDataFields()) {
          if (var.getName().equals(variableId)) {
            variableExists = true;
            break;
          }
        }
      }
      if (!variableExists) {
        throw new DeploymentRuntimeException("MultiInstantiation variable " + variableId
            + " must be a local variable of activity " + activity.getName());
      }

    }

    node.setBehaviour(abstractActivity);
    node.setPreviousNeeded(true);
    return node;
  }

  protected static ProcessDefinition getProcess(final Set processes, final String processId) {
    if (processes == null) {
      return null;
    }
    for (final ProcessDefinition process : processes) {
      if (process.getName().equals(processId)) {
        return process;
      }
    }
    return null;
  }

  protected static Set getAllActivities(final ProcessDefinition processDef) {
    final Set activities = new HashSet();

    final Set processActivities = processDef.getActivities();
    if (processActivities != null) {
      activities.addAll(processActivities);
    }

    return activities;
  }

  protected static Set getInitialActivities(final ProcessDefinition processDef,
      final Set activitiesToAnalyse) {
    final Set initialActivities = new HashSet();
    for (final ActivityDefinition activity : activitiesToAnalyse) {
      if (getIncomingTransitionNb(processDef, activity.getName()) == 0) {
        initialActivities.add(activity);
      }
    }
    return initialActivities;
  }

  protected static Set getEndingActivities(final ProcessDefinition processDef,
      final Set activitiesToAnalyse) {
    final Set endingActivities = new HashSet();
    for (final ActivityDefinition activity : activitiesToAnalyse) {
      if (getOutgoingTransitionNb(processDef, activity.getName()) == 0) {
        endingActivities.add(activity);
      }
    }
    return endingActivities;
  }

  protected static int getIncomingTransitionNb(final ProcessDefinition processDef, final String activityId) {
    int incomingTransitionNb = 0;
    if (processDef.getTransitions() != null) {
      for (final TransitionDefinition transition : processDef.getTransitions()) {
        if (transition.getTo().equals(activityId)) {
          incomingTransitionNb++;
        }
      }
    }
    return incomingTransitionNb;
  }

  protected static int getOutgoingTransitionNb(final ProcessDefinition processDef, final String activityId) {
    int outgoingTransitionNb = 0;
    if (processDef.getTransitions() != null) {
      for (final TransitionDefinition transition : processDef.getTransitions()) {
        if (transition.getFrom().equals(activityId)) {
          outgoingTransitionNb++;
        }
      }
    }
    return outgoingTransitionNb;
  }

  protected static void createVariableDefinitionsFromDataFields(final CompositeElementImpl element,
      final Collection dataFields) {
    if (dataFields != null && !dataFields.isEmpty()) {
      for (final DataFieldDefinition dataField : dataFields) {
        final VariableDefinitionImpl variable = element.createVariableDefinition();
        variable.setKey(dataField.getName());        
        variable.setSourceDescriptor(getInitialValueDescriptor(dataField.getInitialValue(), dataField.getDataTypeClassName()));
      }
    }
  }

  protected static void initializeProcessVariables(final ProcessDefinition processDef, final InternalProcess process) {
    Set processDatafields = processDef.getDataFields();
    final Map dataFields = new HashMap();
    if (processDatafields != null) {
      for (final DataFieldDefinition datafield : processDatafields) {
        dataFields.put(datafield.getName(), datafield);
      }
    }
    //createVariableDefinitionsFromDataFields(process, dataFields.values());

  }

  /**
   * create a descriptor for the initial value of a BasicType INTEGER element
   */
  private static Descriptor getIntegerInitialValueDescriptor(final Integer value) {
    Integer i = null;
    if (value != null) {
      i = Integer.valueOf(value);
    }
    final IntegerDescriptor descriptor = new IntegerDescriptor();
    descriptor.setValue(i);
    return descriptor;
  }

  /**
   * create a descriptor for the initial value of a BasicType FLOAT element
   */
  private static Descriptor getFloatInitialValueDescriptor(final Float value) {
    Float flt = null;
    if (value != null) {
        flt = Float.valueOf(value);
    }
    final FloatDescriptor descriptor = new FloatDescriptor();
    descriptor.setValue(flt);
    return descriptor;
  }
  
  private static Descriptor getShortInitialValueDescriptor(final Short value) {
    Short flt = null;
    if (value != null) {
        flt = Short.valueOf(value);
    }
    final ShortDescriptor descriptor = new ShortDescriptor();
    descriptor.setValue(flt);
    return descriptor;
  }
  
  private static Descriptor getCharInitialValueDescriptor(final Character value) {
    Character flt = null;
    if (value != null) {
        flt = Character.valueOf(value);
    }
    final CharacterDescriptor descriptor = new CharacterDescriptor();
    descriptor.setValue(flt);
    return descriptor;
  }
  
  private static Descriptor getLongInitialValueDescriptor(final Long value) {
    Long flt = null;
    if (value != null) {
        flt = Long.valueOf(value);
    }
    final LongDescriptor descriptor = new LongDescriptor();
    descriptor.setValue(flt);
    return descriptor;
  }
  
  private static Descriptor getDoubleInitialValueDescriptor(final Double value) {
    Double flt = null;
    if (value != null) {
        flt = Double.valueOf(value);
    }
    final DoubleDescriptor descriptor = new DoubleDescriptor();
    descriptor.setValue(flt);
    return descriptor;
  }

  /**
   * create a descriptor for the initial value of a BasicType DATETIME element
   */
  private static Descriptor getDateTimeInitialValueDescriptor(final Date value) {
    if (value == null) {
      return new NullDescriptor();
    }
    // creation of the descriptor
    final ObjectDescriptor descriptor = new ObjectDescriptor();
    descriptor.setClassName(DateUtil.class.getName());
    descriptor.setMethodName("parseDate");
    final ArgDescriptor argDescriptor = new ArgDescriptor();
    argDescriptor.setTypeName(String.class.getName());
    argDescriptor.setDescriptor(new StringDescriptor(DateUtil.format(value)));
    descriptor.addArgDescriptor(argDescriptor);
    return descriptor;
  }

  /**
   * create a descriptor for the initial value of a BasicType PERFORMER element
   */
  /*
  private static Descriptor getPerformerInitialValueDescriptor(final String value,
      final Set participantIds, final String dataFieldName) {
    Descriptor descriptor;
    if (value == null || value.length() == 0) {
      descriptor = new NullDescriptor();
    } else {
      if (participantIds != null && participantIds.contains(value)) {
        descriptor = new StringDescriptor(value);
      } else {
        throw new DeploymentRuntimeException("Invalid value: Participant " + value
            + " does not exist in the naming context of datafield " + dataFieldName);
      }
    }
    return descriptor;
  }*/

  /**
   * create a descriptor for the initialValue of a dataField
   * @param processDef: the process definition; used for a PERFORMER dataField
   * @param dataField: the dataField definition
   * @return a descriptor of the initial value
   */
  public static Descriptor getInitialValueDescriptor(final Object initialValue, final String typeClassName) {
    if (Boolean.class.getName().equals(typeClassName)) {
      return BooleanHelper.createDescriptor((Boolean) initialValue);
    } else if (String.class.getName().equals(typeClassName)) {
      return new StringDescriptor((String) initialValue);
    } else if (Integer.class.getName().equals(typeClassName)) {
      return getIntegerInitialValueDescriptor((Integer) initialValue);
    } else if (Float.class.getName().equals(typeClassName)) {
      return getFloatInitialValueDescriptor((Float) initialValue);
    } else if (Date.class.getName().equals(typeClassName)) {
      return getDateTimeInitialValueDescriptor((Date) initialValue);
    } else if (Character.class.getName().equals(typeClassName)) {
      return getCharInitialValueDescriptor((Character) initialValue);
    } else if (Short.class.getName().equals(typeClassName)) {
      return getShortInitialValueDescriptor((Short) initialValue);
    } else if (Long.class.getName().equals(typeClassName)) {
      return getLongInitialValueDescriptor((Long) initialValue);
    } else if (Double.class.getName().equals(typeClassName)) {
      return getDoubleInitialValueDescriptor((Double) initialValue);
    } else {
      throw new DeploymentRuntimeException("Unknow basic type: " + typeClassName);
    }
  }

  protected static boolean activityWithIdExist(final ProcessDefinition process, final String activityId) {
    final Set activities = process.getActivities();
    if (activities != null) {
      for (final ActivityDefinition activity : activities) {
        if (activity.getName().equals(activityId)) {
          return true;
        }
      }
    }
    //scope of from and to is the surrounding scope (see spec p40)
    return false;
  }

  protected static Set createTimerDefinitions(final ActivityDefinition activity, final NodeImpl node) {
    if (activity.getDeadlines() != null) {
      final Set deadlineHooks = new HashSet();
      for (final DeadlineDefinition deadline : activity.getDeadlines()) {
        deadlineHooks.add(deadline.getClassName());
        createTimerDefinition(node, deadline);
      }
      return deadlineHooks;
    }
    return null;
  }

  protected static void createTimerDefinition(final NodeImpl node, final DeadlineDefinition deadline) {
    final TimerDefinitionImpl timerDefinition = node.createTimerDefinition();
    timerDefinition.setSignalName(deadline.getClassName());

    final String condition = deadline.getCondition();
    try {
      final long duration = Long.parseLong(condition);
      timerDefinition.setDueDateDescription(duration + " millis");
    } catch (final NumberFormatException e1) {
      try {
        timerDefinition.setDueDate(DateUtil.parseDate(condition));
      } catch (final IllegalArgumentException e2) {
        throw new DeploymentRuntimeException("deadline condition '" + deadline.getCondition()
            + "' is neither a Long nor a formatted date", e2);
      }
    }
  }

}






© 2015 - 2025 Weber Informatics LLC | Privacy Policy