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

org.ow2.bonita.parsing.xpdl.binding.ActivityBinding Maven / Gradle / Ivy

The newest version!
/**
 * 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.
 * 
 * Modified by Matthieu Chaffotte - BonitaSoft S.A.
 **/
package org.ow2.bonita.parsing.xpdl.binding;

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

import org.ow2.bonita.definition.VariablePerformerAssign;
import org.ow2.bonita.facade.def.element.HookDefinition;
import org.ow2.bonita.facade.def.element.HookDefinition.Event;
import org.ow2.bonita.facade.def.majorElement.ActivityDefinition.JoinType;
import org.ow2.bonita.facade.def.majorElement.ActivityDefinition.SplitType;
import org.ow2.bonita.util.ExceptionManager;
import org.ow2.bonita.util.GroovyExpression;
import org.ow2.bonita.util.ProcessBuilder;
import org.ow2.bonita.util.xml.Parse;
import org.ow2.bonita.util.xml.Parser;
import org.ow2.bonita.util.xml.XmlUtil;
import org.w3c.dom.Element;

/**
 * @author Marc Blachon, Guillaume Porcher, Charles Souillard, Miguel Valdes, Pierre Vigneras
 */
public class ActivityBinding extends MajorElementBinding {

  private static final Logger LOG = Logger.getLogger(ActivityBinding.class.getName());

  public ActivityBinding() {
    super("Activity");
  }

  @Override
  @SuppressWarnings("unchecked")
  public Object parse(final Element activityElement, final Parse parse, final Parser parser) {
    if (ActivityBinding.LOG.isLoggable(Level.FINE)) {
      ActivityBinding.LOG.fine("parsing element = " + activityElement);
    }
    final String id = getId(activityElement);

    final ProcessBuilder processBuilder = parse.findObject(ProcessBuilder.class);
    final Collection activityDatafields = parse.findObject(Collection.class);

    final String limit = getChildTextContent(activityElement, "Limit");
    if (limit != null) {
      parse.addProblem("'Limit' element not yet supported on activity definition." + "Please remove it from activity: "
          + id);
      if ("".equals(limit.trim())) {
        parse.addProblem("Limit definition incorrect: no value specified in element 'Limit' for activity: " + id);
      }
    }

    Element subFlowElement = null;
    boolean noImplementation = false;
    final Element implementationElement = XmlUtil.element(activityElement, "Implementation");
    if (implementationElement != null) {
      noImplementation = XmlUtil.element(implementationElement, "No") != null;
      subFlowElement = XmlUtil.element(implementationElement, "SubFlow");
    }

    String startMode = "Automatic";
    final Element startModeElement = XmlUtil.element(activityElement, "StartMode");
    if (startModeElement != null) {
      final Element startModeFirstChildElement = XmlUtil.element(startModeElement);
      startMode = startModeFirstChildElement.getLocalName();
    }
    String finishMode = null;
    final Element finishModeElement = XmlUtil.element(activityElement, "FinishMode");
    if (finishModeElement != null) {
      final Element finishModeFirstChildElement = XmlUtil.element(finishModeElement);
      finishMode = finishModeFirstChildElement.getLocalName();
    } else {
      if ("Automatic".equals(startMode)) {
        finishMode = "Automatic";
      } else {
        finishMode = "Manual";
      }
    }
    final String performer = getChildTextContent(activityElement, "Performer");
    checkStartAndFinishMode(startMode, finishMode, performer, id, noImplementation, parse);
    final String priority = getChildTextContent(activityElement, "Priority");
    if (priority != null) {
      parse.addProblem("'Priority' element not yet supported on activity definition. Please remove it from activity: "
          + id);
    }

    final boolean route = XmlUtil.element(activityElement, "Route") != null;

    if (route) {
      processBuilder.addDecisionNode(id);
    } else if (subFlowElement != null) {
      final Element actualparametersElement = XmlUtil.element(subFlowElement, "ActualParameters");
      if (actualparametersElement != null) {
        parse.addWarning("Subflow ActualParameters not yet supported.");
      }

      final String subFlowId = XmlUtil.attribute(subFlowElement, "Id");
      final String execution = XmlUtil.attribute(subFlowElement, "Execution");
      if (execution != null && "Asyncr".equals(execution)) {
        parse.addProblem("Asyncr SubFlow is not supported");
      }
      processBuilder.addSubProcess(id, subFlowId);
    } else if ("Manual".equals(startMode)) {
      processBuilder.addHumanTask(id, performer);
    } else {
      processBuilder.addSystemTask(id);
    }

    final String description = getChildTextContent(activityElement, "Description");
    processBuilder.addDescription(description);
    processBuilder.addLabel(XmlUtil.attribute(activityElement, "Name"));

    boolean isAsync = false;
    final Element asyncElement = getExtendedAttribute(activityElement, "Async");
    if (asyncElement != null) {
      isAsync = Boolean.valueOf(XmlUtil.attribute(asyncElement, "Value"));
    }
    if (isAsync) {
      processBuilder.asynchronous();
    }

    parseDeadlines(activityElement, parse, processBuilder);

    SplitType splitType = null;
    JoinType joinType = JoinType.AND;
    final Element transitionRestrictionsElement = XmlUtil.element(activityElement, "TransitionRestrictions");
    if (transitionRestrictionsElement != null) {
      final List transitionRestrictionElements = XmlUtil.elements(transitionRestrictionsElement,
          "TransitionRestriction");
      if (transitionRestrictionElements != null) {
        for (final Element transitionrestrictionElement : transitionRestrictionElements) {
          final Element joinElement = XmlUtil.element(transitionrestrictionElement, "Join");
          if (joinElement != null) {
            joinType = this.getEnumValue(JoinType.class, XmlUtil.attribute(joinElement, "Type"), null);
            if (joinType == null) {
              parse.addProblem("Mandatory type attribute is not specified on join element");
            }
          }
          final Element splitElement = XmlUtil.element(transitionrestrictionElement, "Split");
          if (splitElement != null) {
            splitType = this.getEnumValue(SplitType.class, XmlUtil.attribute(splitElement, "Type"), null);
            if (splitType == null) {
              parse.addProblem("Mandatory type attribute is not specified on Split element!");
            }
            processBuilder.addSplitType(splitType);
          }
          break;
        }
      }
    }
    processBuilder.addJoinType(joinType);
    // parse hooks
    final boolean isAutomaticActivity = "Automatic".equals(startMode);
    parseHooks(activityElement, parse, isAutomaticActivity, processBuilder);
    // parse performer asign
    parsePerformerAssign(activityElement, parse, processBuilder);
    // parse datafields
    parseDataFields(activityElement, parse, processBuilder, activityDatafields, parser);
    // parse multi instantiation
    parseMultiInstantiationDefinition(activityElement, parse, route, processBuilder);
    return null;
  }

  /**
   * Parse multi instantiation info from the activity element.
   * 
   * @param activityElement
   * @param parse
   * @return
   */
  private void parseMultiInstantiationDefinition(final Element activityElement, final Parse parse,
      final boolean isRoute, final ProcessBuilder processuilder) {
    final Element activityInstantiatorElement = getExtendedAttribute(activityElement, "MultiInstantiation");
    if (activityInstantiatorElement != null) {
      final String className = getChildTextContent(activityInstantiatorElement, "MultiInstantiator");
      if (className == null) {
        parse.addProblem("MultiInstantiation needs to specify a MultiInstantiator class name "
            + "(in a nested MultiInstantiator element)");
      }
      final String variableId = getChildTextContent(activityInstantiatorElement, "Variable");
      if (variableId == null) {
        parse.addProblem("MultiInstantiation needs to specify a variable id " + "(in a nested Variable element)");
      }
      if (className != null && variableId != null) {
        if (isRoute) {
          parse.addProblem("Multi Instantiation cannot be defined on a Route activity");
        } else {
          processuilder.addMultiInstanciation(variableId, className);
          // parameters always null ?
        }
      }
    }
  }

  protected void parsePerformerAssign(final Element activityElement, final Parse parse,
      final ProcessBuilder processBuilder) {
    final Element performerAssignElement = getExtendedAttribute(activityElement, "PerformerAssign");
    if (performerAssignElement != null) {
      final String value = XmlUtil.attribute(performerAssignElement, "Value");
      String className = null;
      Map parameters = null;
      if ("Callback".equals(value)) {
        final Element callbackElement = XmlUtil.element(performerAssignElement, "Callback");
        className = callbackElement.getTextContent();
      } else if ("Custom".equals(value)) {
        final Element callbackElement = XmlUtil.element(performerAssignElement, "Custom");
        className = callbackElement.getTextContent();
      } else if ("Variable".equals(value)) {
        final Element propertyElement = XmlUtil.element(performerAssignElement, "Variable");
        parameters = new HashMap();
        parameters.put("variableId", new Object[] { propertyElement.getTextContent() });
        className = VariablePerformerAssign.class.getName();
      } else {
        parse.addProblem("Unsupported value on extendedAttribute PerformerAssign: " + value);
      }
      processBuilder.addFilter(className);
      if (parameters != null) {
        for (final Entry parameter : parameters.entrySet()) {
          final String key = parameter.getKey();
          if (GroovyExpression.isGroovyExpression(key)) {
            processBuilder.addOutputParameter(key, (String) parameter.getValue()[0]);
          } else {
            processBuilder.addInputParameter(key, parameter.getValue());
          }
        }
      }
    }
  }

  private void checkStartAndFinishMode(final String startMode, final String finishMode, final String performer,
      final String activityId, final boolean isNoImpl, final Parse parse) {
    if ("Manual".equals(finishMode) && "Automatic".equals(startMode) || "Automatic".equals(finishMode)
        && "Manual".equals(startMode)) {
      parse.addProblem("StartMode and FinishMode have different values: this feature is not yet supported.");
    }
    final boolean hasManualMode = "Manual".equals(startMode) || "Manual".equals(finishMode);
    if (hasManualMode) {
      if (performer == null) {
        parse
            .addProblem("StartMode or FinishMode is Manual and no performer is specified on activity processDefinitionUUID = "
                + activityId + "! Please specify one.");
      }
      if (!isNoImpl) {
        parse.addProblem("StartMode or FinishMode is Manual and activity implementation is not No:"
            + "this feature is not yet supported.");
      }
    }
  }

  protected void parseDataFields(final Element activityElement, final Parse parse, final ProcessBuilder processBuilder,
      final Collection activityDatafields, final Parser parser) {
    final Set propertyElements = getExtendedAttributes(activityElement, "property");
    if (propertyElements != null && !propertyElements.isEmpty()) {
      if (activityDatafields == null) {
        parse.addProblem("No activity dataFields is defined within the process.");
      } else {
        for (final Element propertyElement : propertyElements) {
          final String dataFieldId = XmlUtil.attribute(propertyElement, "Value");
          boolean found = false;
          for (final Element datafieldElement : activityDatafields) {
            final String id = getId(datafieldElement);
            if (id.equals(dataFieldId)) {
              parser.parseElement(datafieldElement, parse, "majorElements");
              found = true;
              break;
            }
          }
          if (!found) {
            parse.addProblem("Looking for a datafield with id: " + dataFieldId
                + " in enclosing process but unable to find it.");
          }
          final String propagatedValue = getChildTextContent(propertyElement, "Propagated");
          if (propagatedValue != null
              && !("no".equalsIgnoreCase(propagatedValue) || "false".equalsIgnoreCase(propagatedValue))) {
            parse.addProblem("Propagated value not supported: " + propagatedValue + ". Use instance variables instead."
                + "(Only 'no' or 'false' are supported for backward compatibility.)");
          }
        }
      }
    }
  }

  protected void parseHooks(final Element activityElement, final Parse parse, final boolean isAutomatic,
      final ProcessBuilder processBuilder) {
    final Set hookElements = getExtendedAttributes(activityElement, "hook");
    if (hookElements != null) {
      for (final Element hookElement : hookElements) {
        final String className = XmlUtil.attribute(hookElement, "Value");
        final String hookEventName = getChildTextContent(hookElement, "HookEventName");
        final Map parameters = getHookParameters(hookElement, parse, className);
        if (hookEventName == null) {
          parse.addProblem("hook ExtendedAttribute needs an element child called HookEventName");
          return;
        }
        HookDefinition.Event event = null;
        boolean throwingException = false;

        final String rollBackFlag = getChildTextContent(hookElement, "Rollback");
        // We can't use Boolean's class methods since in their case,
        // true == "true" and false == !true (anything different to "true")
        // In our case, we really want either "true" or "false". Any other string
        // is simply an invalid flag.
        throwingException = false;
        if ("true".equals(rollBackFlag)) {
          throwingException = true;
        }
        try {
          event = getEventFromString(hookEventName);
        } catch (final IllegalArgumentException iae) {
          parse.addProblem("Unsupported HookEventName: " + hookEventName + " for "
              + (isAutomatic ? "automatic" : "manual") + " activity.");
        }
        if (event != null) {
          processBuilder.addConnector(event, className, throwingException);
          if (parameters != null) {
            for (final Entry parameter : parameters.entrySet()) {
              final String key = parameter.getKey();
              if (GroovyExpression.isGroovyExpression(key)) {
                processBuilder.addOutputParameter(key, (String) parameter.getValue()[0]);
              } else {
                processBuilder.addInputParameter(key, parameter.getValue());
              }
            }
          }
        }
      }
    }
  }

  private HookDefinition.Event getEventFromString(final String hookEventName) {
    if ("task:onReady".equals(hookEventName)) {
      return Event.taskOnReady;
    } else if ("task:onStart".equals(hookEventName)) {
      return Event.taskOnStart;
    } else if ("task:onFinish".equals(hookEventName)) {
      return Event.taskOnFinish;
    } else if ("task:onSuspend".equals(hookEventName)) {
      return Event.taskOnSuspend;
    } else if ("task:onResume".equals(hookEventName)) {
      return Event.taskOnResume;
    } else if ("task:onCancel".equals(hookEventName)) {
      return Event.taskOnCancel;
    } else if ("automatic:onEnter".equals(hookEventName)) {
      return Event.automaticOnEnter;
    } else {
      final String message = ExceptionManager.getInstance().getFullMessage("buc_M_11",
          HookDefinition.Event.class.getName(), hookEventName);
      throw new IllegalArgumentException(message);
    }
  }

  private Map getHookParameters(final Element hookElement, final Parse parse, final String className) {
    final Map hookParameters = new HashMap();
    final Element hookParametersElement = XmlUtil.element(hookElement, "Parameters");
    if (hookParametersElement != null) {
      final List parameters = XmlUtil.elements(hookParametersElement);
      for (final Element param : parameters) {
        final String paramType = param.getLocalName();
        final String variable = param.getAttribute("Var");
        String method;
        if ("InParameter".equals(paramType)) {
          if (param.hasAttribute("Setter")) {
            method = param.getAttribute("Setter");
            if (method.length() != 0) {
              hookParameters.put(method, new Object[] { variable });
            } else {
              parse.addProblem("The Setter attribute should not be empty!");
            }
          } else {
            parse.addProblem("InParameter need the Setter attribute");
          }
        } else if ("OutParameter".equals(paramType)) {
          if (param.hasAttribute("Getter")) {
            method = param.getAttribute("Getter");
            if (method.length() != 0) {
              hookParameters.put(method, new Object[] { variable });
            } else {
              parse.addProblem("The Getter attribute should not be empty!");
            }
          } else {
            parse.addProblem("OutParameter need the Getter attribute");
          }
        } else if ("Properties".equals(paramType)) {
          if (variable.startsWith("file:")) {
            hookParameters.put("file:", new Object[] { variable.substring(5) });
          } else if (variable.startsWith("bar:")) {
            hookParameters.put("bar:", new Object[] { variable.substring(4) });
          } else if (variable.startsWith("resource:")) {
            hookParameters.put("resource:", new Object[] { variable.substring(9) });
          } else {
            parse.addProblem("The value of Var attribute can only be either "
                + "file: or bar: or" + "resource:");
          }
        } // ignores all other tags
      }
    }
    return hookParameters;
  }

  protected void parseDeadlines(final Element activityElement, final Parse parse, final ProcessBuilder processBuilder) {
    final List deadlineElements = XmlUtil.elements(activityElement, "Deadline");

    if (deadlineElements != null) {
      if (ActivityBinding.LOG.isLoggable(Level.WARNING)) {
        ActivityBinding.LOG.warning("Bonita use of deadlines is specific: refer to bonita User Guide for more details");
      }
      for (final Element deadlineElement : deadlineElements) {
        // schema validation ensures that deadlineCondtion and ExceptionName
        // are specified once and only once
        final String deadlineCondition = getChildTextContent(deadlineElement, "DeadlineCondition");
        if (deadlineCondition == null || "".equals(deadlineCondition)) {
          parse.addProblem("DeadlineCondition element is not specified on deadline element");
        }
        final String exceptionName = getChildTextContent(deadlineElement, "ExceptionName");
        if (exceptionName == null) {
          parse.addProblem("ExceptionName element is not specified on deadline element");
        }
        processBuilder.addDeadline(deadlineCondition, exceptionName);
      }
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy