org.ow2.petals.activitibpmn.incoming.operation.ActivitiOperation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of petals-se-activiti Show documentation
Show all versions of petals-se-activiti Show documentation
BPMN Service Engine based on Activiti
/**
* Copyright (c) 2015-2016 Linagora
*
* This program/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, either version 2.1 of the License, or (at your
* option) any later version.
*
* This program/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/library; If not, see http://www.gnu.org/licenses/
* for the GNU Lesser General Public License version 2.1.
*/
package org.ow2.petals.activitibpmn.incoming.operation;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jbi.messaging.Fault;
import javax.jbi.messaging.MessagingException;
import javax.xml.bind.DatatypeConverter;
import javax.xml.namespace.QName;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import org.activiti.bpmn.model.FormProperty;
import org.activiti.bpmn.model.FormValue;
import org.ow2.petals.activitibpmn.incoming.ActivitiService;
import org.ow2.petals.activitibpmn.incoming.operation.annotated.AnnotatedOperation;
import org.ow2.petals.activitibpmn.incoming.operation.exception.NoUserIdValueException;
import org.ow2.petals.activitibpmn.incoming.operation.exception.OperationProcessingException;
import org.ow2.petals.activitibpmn.incoming.operation.exception.OperationProcessingFault;
import org.ow2.petals.activitibpmn.utils.XslUtils;
import org.ow2.petals.component.framework.api.message.Exchange;
import org.w3c.dom.Document;
import com.ebmwebsourcing.easycommons.xml.XMLPrettyPrinter;
public abstract class ActivitiOperation implements ActivitiService {
/**
* Namespace of special parameters for the output XSLT style-sheet
*/
protected static final String SCHEMA_OUTPUT_XSLT_SPECIAL_PARAMS = "http://petals.ow2.org/se/bpmn/output-params/1.0/special";
/**
* Local part of the special parameter name about the process instance identifier for the output XSLT style-sheet
*/
protected static final String SCHEMA_OUTPUT_XSLT_PARAM_PROCESS_INSTANCE_ID = "processInstanceId";
/**
* Local part of the special parameter name about the user identifier for the output XSLT style-sheet
*/
protected static final String SCHEMA_OUTPUT_XSLT_PARAM_USER_ID = "userId";
/**
* Namespace of process instance parameters for the output XSLT style-sheet
*/
protected static final String SCHEMA_OUTPUT_XSLT_PROCESS_INSTANCE_PARAMS = "http://petals.ow2.org/se/bpmn/output-params/1.0/process-instance";
/**
* Namespace of task parameters for the output XSLT style-sheet
*/
protected static final String SCHEMA_OUTPUT_XSLT_TASK_PARAMS = "http://petals.ow2.org/se/bpmn/output-params/1.0/task";
/**
* Namespace of fault parameters for the fault XSLT style-sheet
*/
public static final String SCHEMA_OUTPUT_XSLT_FAULT_PARAMS = "http://petals.ow2.org/se/bpmn/faults/1.0";
/**
* The WSDL operation name associated to this {@link ActivitiOperation}
*/
protected final QName wsdlOperation;
/**
* The process definition identifier
*/
protected final String processDefinitionId;
/**
* The identifier of the deployed process definition, different from the process definition identifier.
*/
protected String deployedProcessDefinitionId = null;
/**
* The task identifier on which the action must be realize on the BPMN process side
*/
protected final String actionId;
/**
* The compiled XPath expression of the process instance identifier placeholder
*/
protected final XPathExpression proccesInstanceIdXPathExpr;
/**
* The compiled XPath expression of the user identifier placeholder
*/
protected final XPathExpression userIdXPathExpr;
/**
* The definition of variables of the operation
*/
protected final Map variables;
/**
* Types of variables
*/
protected final Map variableTypes;
/**
* The output XSLT style-sheet compiled
*/
private final Templates outputTemplate;
/**
* The XSLT style-sheet compiled associated to WSDL faults. The key is the class simple name of the exception
* associated to the fault.
*/
private final Map faultTemplates;
protected final Logger logger;
/**
* @param annotatedOperation
* Annotations of the operation to create
* @param processDefinitionId
* The process definition identifier to associate to the operation to create
* @param logger
*/
protected ActivitiOperation(final AnnotatedOperation annotatedOperation, final Logger logger) {
this.wsdlOperation = annotatedOperation.getWsdlOperation();
this.processDefinitionId = annotatedOperation.getProcessDefinitionId();
this.actionId = annotatedOperation.getActionId();
this.proccesInstanceIdXPathExpr = annotatedOperation.getProcessInstanceIdHolder();
this.userIdXPathExpr = annotatedOperation.getUserIdHolder();
this.variables = annotatedOperation.getVariables();
this.variableTypes = annotatedOperation.getVariableTypes();
this.outputTemplate = annotatedOperation.getOutputTemplate();
this.faultTemplates = annotatedOperation.getFaultTemplates();
this.logger = logger;
}
/**
* @param deployedProcessDefinitionId
* The identifier of the deployed process definition, different from the process definition identifier.
*/
public void setDeployedProcessDefinitionId(final String deployedProcessDefinitionId) {
this.deployedProcessDefinitionId = deployedProcessDefinitionId;
}
/**
* @return The action to realize on the BPMN process side (ie, the name of the BPMN action)
*/
public abstract String getAction();
@Override
public final void execute(final Exchange exchange) {
try {
final Document incomingPayload = exchange.getInMessageContentAsDocument();
if (this.logger.isLoggable(Level.FINE)) {
this.logger.fine("*** incomingPayload = " + XMLPrettyPrinter.prettyPrint(incomingPayload));
}
if (this.logger.isLoggable(Level.FINE)) {
this.logger.fine("Activiti processDefId = " + processDefinitionId);
this.logger.fine("Activiti Action = " + this.getClass().getSimpleName());
this.logger.fine("Activiti ActionType (TaskId) = " + this.getAction());
}
incomingPayload.getDocumentElement().normalize();
try {
// Get the userId
final String userId;
try {
userId = this.userIdXPathExpr.evaluate(incomingPayload);
if (userId == null || userId.trim().isEmpty()) {
throw new NoUserIdValueException(this.wsdlOperation);
}
if (this.logger.isLoggable(Level.FINE)) {
this.logger.fine("User identifier value: " + userId);
}
} catch (final XPathExpressionException e) {
throw new OperationProcessingException(this.wsdlOperation, e);
}
// Get the bpmn variables
final Map variableValues = new HashMap();
for (final Entry variable : this.variables.entrySet()) {
final String variableName = variable.getKey();
try {
final String variableValueAsStr = variable.getValue().evaluate(incomingPayload);
if (variableValueAsStr == null || variableValueAsStr.trim().isEmpty()) {
if (this.variableTypes.get(variableName).isRequired()) {
throw new MessagingException("The task: " + this.getClass().getSimpleName()
+ " of process: " + this.processDefinitionId + " required the variable: "
+ variableName);
} else {
if (logger.isLoggable(Level.FINE)) {
logger.fine("variable: " + variableName + "=> no value");
}
}
} else {
if (logger.isLoggable(Level.FINE)) {
logger.fine("variable: " + variableName + "=> value: " + variableValueAsStr);
}
// Get the type of the bpmn variable
final FormProperty variableProperties = this.variableTypes.get(variableName);
final String varType = variableProperties.getType();
// Put the value in Map of activiti variable in the right format
if (varType.equals("string")) {
variableValues.put(variableName, variableValueAsStr);
} else if (varType.equals("long")) {
try {
variableValues.put(variableName, Long.valueOf(variableValueAsStr));
} catch (final NumberFormatException e) {
throw new MessagingException("The value of the variable '" + variableName
+ "' must be a long ! Current value is: " + variableValueAsStr);
}
} else if (varType.equals("enum")) {
boolean validValue = false;
for (final FormValue enumValue : variableProperties.getFormValues()) {
if (variableValueAsStr.equals(enumValue.getId())) {
validValue = true;
}
}
if (!validValue) {
throw new MessagingException("The value of the variable '" + variableName
+ " does not belong to the enum of Activiti variable ! Current value is: "
+ variableValueAsStr);
} else {
variableValues.put(variableName, variableValueAsStr);
}
} else if (varType.equals("date")) {
try {
variableValues.put(variableName, DatatypeConverter
.parseDateTime(variableValueAsStr).getTime());
} catch (final IllegalArgumentException e) {
throw new MessagingException("The value of the variable '" + variableName
+ "' must be a valid date ! Current value is: " + variableValueAsStr);
}
} else if (varType.equals("boolean")) {
if (variableValueAsStr.equalsIgnoreCase("true")
|| variableValueAsStr.equalsIgnoreCase("false")) {
variableValues.put(variableName, (Boolean) Boolean.valueOf(variableValueAsStr));
} else {
throw new MessagingException("The value of the variable '" + variableName
+ "' must be a boolean value \"true\" or \"false\" ! Current value is: "
+ variableValueAsStr);
}
}
}
} catch (final XPathExpressionException e) {
throw new OperationProcessingException(this.wsdlOperation, e);
}
}
// Extract process flow data
final Map xslParameters = new HashMap();
this.doExecute(incomingPayload, userId, variableValues, xslParameters, exchange);
try {
exchange.setOutMessageContent(XslUtils.createXmlPayload(this.outputTemplate, xslParameters,
this.logger));
} catch (final TransformerException e) {
throw new OperationProcessingException(this.wsdlOperation, e);
}
} catch (final OperationProcessingFault e) {
// A fault occurs during the operation processing
final Templates faultTemplate = this.faultTemplates.get(e.getClass().getSimpleName());
if (faultTemplate == null) {
// No fault mapped --> Processing as an error
throw new OperationProcessingException(this.wsdlOperation, e);
}
final Fault fault = exchange.createFault();
try {
fault.setContent(XslUtils.createXmlPayload(faultTemplate, e.getXslParameters(), this.logger));
exchange.setFault(fault);
} catch (final TransformerException e1) {
throw new OperationProcessingException(this.wsdlOperation, e);
}
}
} catch (final OperationProcessingException e) {
this.logger.log(Level.SEVERE, "Exchange " + exchange.getExchangeId() + " encountered a problem.", e);
// Technical error, it would be set as a Fault by the CDK
exchange.setError(e);
} catch (final MessagingException e) {
this.logger.log(Level.SEVERE, "Exchange " + exchange.getExchangeId() + " encountered a problem.", e);
// Technical error, it would be set as a Fault by the CDK
exchange.setError(e);
}
}
/**
*
* @param domSource
* The incoming XML payload
* @param userId
* The user identifier
* @param processVars
* @param outputNamedValues
* The output named values to generate response
* @param exchange
* The exchange
* @throws OperationProcessingException
* An error occurs when processing the operation
*/
protected abstract void doExecute(final Document incomingPayload, final String userId,
final Map processVars, final Map outputNamedValues, final Exchange exchange)
throws OperationProcessingException;
@Override
public void log(final Logger logger, final Level logLevel) {
if (logger.isLoggable(logLevel)) {
logger.log(logLevel, "operation '" + this.getClass().getSimpleName() + "':");
logger.log(logLevel, " - processDefinitionId = " + this.processDefinitionId);
logger.log(logLevel, " - processInstanceId => compiled XPath expression");
logger.log(logLevel, " - action = " + this.getClass().getSimpleName());
for (final String variableName : this.variables.keySet()) {
logger.log(logLevel, " - variable => name: " + variableName + " => compiled XPath expression");
}
logger.log(logLevel, " - Activiti variable types");
for (final Entry entry : this.variableTypes.entrySet()) {
final String key = entry.getKey();
final FormProperty value = entry.getValue();
logger.log(logLevel, " - bpmn variable : " + key + " - Name = " + value.getName() + " - Type = "
+ value.getType());
if (value.getType().equals("enum")) {
for (final FormValue enumValue : value.getFormValues()) {
logger.log(logLevel, " |------ enum value Id = " + enumValue.getId() + " - Value = "
+ enumValue.getName());
}
} else if (value.getType().equals("date")) {
logger.log(logLevel, " |------ Date pattern = " + value.getDatePattern());
}
}
}
}
public String getProcessDefinitionId() {
return this.processDefinitionId;
}
/**
* @return The WSDL operation associated to this {@link ActivitiOperation}
*/
public QName getWsdlOperation() {
return this.wsdlOperation;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy