/**
*
*/
package org.jboss.soa.esb.services.jbpm;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.crypto.SealedObject;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.tree.DefaultElement;
import org.jboss.internal.soa.esb.assertion.AssertArgument;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.message.format.MessageFactory;
import org.jboss.soa.esb.message.mapping.ObjectMapper;
import org.jboss.soa.esb.message.mapping.ObjectMappingException;
import org.jboss.soa.esb.services.security.SecurityService;
import org.jbpm.context.exe.ContextInstance;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.Token;
import org.mvel2.MVEL;
/**
* Mapping glue between jBPM and ESB. Handles setting up the replyTo of the
* JBpmCallback Service, and is a wrapper around org.jboss.soa.esb.message.mapping.ObjectMapper
* to handle jBPM specifics around mapping variables from jBPM to ESB and back.
*
* @author kstam
* @author <a href="mailto:dbevenius@jboss.com">Daniel Bevenius</a>
*/
public class JBpmObjectMapper {
public JBpmObjectMapper() {
super();
}
private Logger log = Logger.getLogger(getClass());
private ObjectMapper objectMapper = new ObjectMapper();
/**
* Creates an ESB Message, giving the mapping specified in the jbpmToEsbVars Element.
* This element is defined in the jBPM process-definition.xml as child element
* of either the EsbNotifier ActionHandler, or the EsbActionHandler.
*
* @param jbpmToEsbVars - the configuration of the mapping.
* @param isGlobalProcessScope - Variables will be looked up using either the token scope or the process-definition (global) scope
* See the jBPM documentation for more details.
* @param executionContext - the jBPM ExecutionContext.
* @return the newly created ESB message.
*
* @throws Exception
*/
public Message mapFromJBpmToEsbMessage (final DefaultElement bpmToEsbVars, final Boolean isGlobalProcessScope, final ExecutionContext executionContext)
{
Message message = MessageFactory.getInstance().getMessage();
boolean gblPrcScope = (null == isGlobalProcessScope) ? false : isGlobalProcessScope;
// Map SecurityContext and AuthRequest if they exist.
mapSecurityInfoFromJbpmToEsb(gblPrcScope, executionContext, message);
if (bpmToEsbVars==null) {
return message;
}
List mappings = bpmToEsbVars.elements(Constants.MAPPING_TAG);
if (mappings.size() < 1) {
setAllOnEsbMessage(gblPrcScope, executionContext, message);
} else {
for (Object mappingElement : mappings) {
Element element = (Element) mappingElement;
try {
Mapping mapping = Mapping.parseMappingElement(element);
setOnEsbMessage(mapping, gblPrcScope, executionContext, message);
} catch (ConfigurationException ce) {
log.error(ce.getMessage(), ce);
} catch (ObjectMappingException ome) {
log.error(ome.getMessage(), ome);
}
}
}
return message;
}
/**
* Sets all the Objects in the jBPM VariableMap in the body of the ESB Message using the
* the jBPM object names as the Esb Message body keys.
*
* @param gblPrcScope
* @param executionContext
* @param message
*/
private void setAllOnEsbMessage(final boolean gblPrcScope, final ExecutionContext executionContext, Message message)
{
Token token = executionContext.getToken();
ContextInstance ctxInstance = token.getProcessInstance().getContextInstance();
log.debug("The user has not mapped anything (jbpmToEsbVars is null) " +
"so add all the variables using their jBPM name");
Map map = (gblPrcScope) ? ctxInstance.getVariables() : ctxInstance.getVariables(token);
if (null != map) {
for (Iterator iter = map.entrySet().iterator(); iter.hasNext();){
Map.Entry jBPMVariable = (Map.Entry) iter.next();
message.getBody().add(jBPMVariable.getKey().toString(), jBPMVariable.getValue());
}
}
}
/**
* Sets a jBPM object onto the ESB Message. The mapping is defined in the Mapping element.
*
* @param mapping - Mapping object, used to extract the object from jBPM and used to set the object on the ESB Message
* @param gblPrcScope - Global setting for the jBPM scope
* @param message - ESB Message
* @param executionContext - jBPM ExcutionContext
*
* @throws ObjectMappingException
*/
private void setOnEsbMessage(final Mapping mapping, final boolean gblPrcScope, final ExecutionContext executionContext, Message message)
throws ObjectMappingException, ConfigurationException
{
if (mapping.getBpm()==null || "".equals(mapping.getBpm())) {
throw new ConfigurationException("The 'bpm' attribute is a required attribute");
}
if (mapping.getEsb()==null || "".equals(mapping.getEsb())) {
mapping.setEsb(mapping.getBpm());
}
Token token = executionContext.getToken();
ContextInstance ctxInstance = token.getProcessInstance().getContextInstance();
//Each mapping can override the global setting
boolean isPrcScope = (null == mapping.getIsProcessScope()) ? gblPrcScope : mapping.getIsProcessScope();
//By default assume the object is part of the jBPM variableMap
Object object = getObjectFromJBpmVariableMap(isPrcScope, mapping.getBpm(), ctxInstance, token);
//if that fails then try to get it from the ExecutionContext
if (object==null) {
object = MVEL.getProperty(mapping.getBpm(), executionContext);
}
if (null != object) {
ObjectMapper mapper = new ObjectMapper();
mapper.setObjectOnMessage(message, mapping.getEsb(), object);
} else {
log.warn("The object " + mapping.getBpm() + " is null and cannot not be set on the message");
}
}
/**
* Obtains an Object from the jBPM variableMap.
*
* @param isPrcScope - if true, within process-instance scope, if false, within token scope, or up the token hierarchy.
* @param expression - MVEL expression String.
* @param ctxInstance - jBPM ContextInstance where the jBPM variableMap lives.
* @param token - the current jBPM token, needed if isPrcScope is false.
* @return
*/
private Object getObjectFromJBpmVariableMap(final boolean isPrcScope, final String expression, final ContextInstance ctxInstance, final Token token)
{
Object object = null;
String objectName = expression;
String remainingExpression = null;
int dotPosition=expression.indexOf(".");
if (dotPosition > 0) {
objectName = expression.substring(0, dotPosition);
remainingExpression = expression.substring(dotPosition+1);
}
if (isPrcScope) {
object = ctxInstance.getVariable(objectName);
} else {
object = ctxInstance.getVariable(objectName, token);
}
if (object !=null && remainingExpression!=null) {
log.debug("Using MVEL to obtain the object from " + object + " using expression: " + remainingExpression);
object = MVEL.getProperty(remainingExpression, object);
}
return object;
}
/**
*
* @param message
* @param esbToBpmXml
* @return
*/
public HashMap<String,Object> mapFromEsbMessageToJBpmMap(Message message, final String esbToBpmXml)
throws ConfigurationException
{
return mapFromEsbMessageToJBpmMap(message, getMappingList(esbToBpmXml));
}
/**
* This
* @param message
* @param token
* @throws Exception
*/
public HashMap<String,Object> mapFromEsbMessageToJBpmMap (Message message, final List<Mapping> mappingList)
{
HashMap<String,Object> map = new HashMap<String, Object>();
if (null==mappingList || mappingList.size()<1) {
return null;
}
for (Mapping mapping: mappingList) {
if (mapping.getBpm()==null || "".equals(mapping.getBpm())) {
mapping.setBpm(mapping.getEsb());
}
Object value = null;
try {
value = getObjectFromMessage(message, mapping);
} catch (ConfigurationException ce) {
log.error(ce.getMessage(), ce);
}
// only put it in the map if it's not null
if (null!=value)
map.put(mapping.getBpm(), value);
}
map.putAll(mapSecurityContextFromEsbMessageToJBpmMap(message));
map.putAll(mapAuthRequestFromEsbMessageToJBpmMap(message));
return map;
}
/**
*
* @param message
* @param esbToBpmXml
* @return
*/
public HashMap<Mapping,Object> mapFromEsbMessageToJBpmMapping(Message message, final String esbToBpmXml)
throws ConfigurationException
{
return mapFromEsbMessageToJBpmMapping(message, getMappingList(esbToBpmXml));
}
/**
* This
* @param message
* @param token
* @throws Exception
*/
public HashMap<Mapping,Object> mapFromEsbMessageToJBpmMapping(Message message, final List<Mapping> mappingList)
{
HashMap<Mapping,Object> map = new HashMap<Mapping, Object>();
if (null==mappingList || mappingList.size()<1) {
return null;
}
for (Mapping mapping: mappingList) {
if (mapping.getBpm()==null || "".equals(mapping.getBpm())) {
mapping.setBpm(mapping.getEsb());
}
Object value = null;
try {
value = getObjectFromMessage(message, mapping);
} catch (ConfigurationException ce) {
log.error(ce.getMessage(), ce);
}
// only put it in the map if it's not null
if (null!=value)
map.put(mapping, value);
}
return map;
}
private List<Mapping> getMappingList(final String esbToBpmXml)
throws ConfigurationException
{
final List<Mapping> mappingList = new ArrayList<Mapping>();
if (esbToBpmXml!=null) {
try {
Document document = DocumentHelper.parseText(esbToBpmXml);
Element element = document.getRootElement();
Iterator iterator=element.elementIterator();
while(iterator.hasNext()) {
Element mappingElement = (Element) iterator.next();
Mapping mapping = Mapping.parseMappingElement(mappingElement);
mappingList.add(mapping);
}
} catch (DocumentException de) {
throw new ConfigurationException(de.getMessage(), de);
}
}
return mappingList ;
}
private Object getObjectFromMessage(Message message, Mapping mapping)
throws ConfigurationException
{
Object value = null;
if (mapping.getEsb()==null || "".equals(mapping.getEsb())) {
throw new ConfigurationException("The 'esb' attribute is a required attribute");
}
try {
value = objectMapper.getObjectFromMessage(message, mapping.getEsb());
} catch (ObjectMappingException ome) {
log.info(mapping.getEsb() + " not found");
}
if (value == null) {
value = mapping.getDefaultValue();
}
log.debug("value=" + value);
return value;
}
public HashMap<String, Object> mapSecurityContextFromEsbMessageToJBpmMap(final Message message)
{
final HashMap<String, Object> map = new HashMap<String, Object>();
final SealedObject sealedObject = (SealedObject) message.getContext().getContext(SecurityService.CONTEXT);
if (sealedObject !=null)
{
map.put(Constants.SECURITY_CONTEXT, sealedObject);
}
return map;
}
public HashMap<String, ?> mapAuthRequestFromEsbMessageToJBpmMap(final Message message)
{
final HashMap<String, Object> map = new HashMap<String, Object>();
final byte[] encryptedAuthRequest = (byte[]) message.getContext().getContext(SecurityService.AUTH_REQUEST);
if (encryptedAuthRequest != null)
{
map.put(Constants.AUTH_REQUEST, encryptedAuthRequest);
}
return map;
}
/**
* Will map the SecurityContext and AuthenticationRequest from a jBPM
* variable to ESB Message context.
*
* @param gblPrcScope True if the process scope is global.
* @param executionContext The jBPM execution context.
* @param esbMessage The distination ESB message
* @return
*/
public Message mapSecurityInfoFromJbpmToEsb(final boolean gblPrcScope, final ExecutionContext executionContext, final Message esbMessage)
{
AssertArgument.isNotNull(executionContext, "executionContext");
AssertArgument.isNotNull(esbMessage, "esbMessage");
final Token token = executionContext.getToken();
final ContextInstance ctxInstance = token.getProcessInstance().getContextInstance();
final Map jbpmMap = (gblPrcScope) ? ctxInstance.getVariables() : ctxInstance.getVariables(token);
if (jbpmMap == null)
{
return esbMessage;
}
final Object sealedObject = jbpmMap.get(Constants.SECURITY_CONTEXT);
if (sealedObject != null)
{
esbMessage.getContext().setContext(SecurityService.CONTEXT, sealedObject);
}
final Object encrypedAutRequest = jbpmMap.get(Constants.AUTH_REQUEST);
if (encrypedAutRequest != null)
{
esbMessage.getContext().setContext(SecurityService.AUTH_REQUEST, encrypedAutRequest);
}
return esbMessage;
}
}