org.activiti.camel.CamelBehavior.java Source code

Java tutorial

Introduction

Here is the source code for org.activiti.camel.CamelBehavior.java

Source

/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.activiti.camel;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

import org.activiti.engine.ActivitiException;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
import org.activiti.engine.impl.bpmn.behavior.BpmnActivityBehavior;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.pvm.PvmProcessDefinition;
import org.activiti.engine.impl.pvm.delegate.ActivityBehavior;
import org.activiti.engine.impl.pvm.delegate.ActivityExecution;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.apache.camel.CamelContext;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
import org.apache.camel.impl.DefaultExchange;
import org.apache.commons.lang3.StringUtils;

/**
* This abstract class takes the place of the now-deprecated CamelBehaviour class (which can still be used for legacy compatibility)
* and significantly improves on its flexibility. Additional implementations can be created that change the way in which Activiti
* interacts with Camel per your specific needs.
* 
* Three out-of-the-box implementations of CamelBehavior are provided:
*   (1) CamelBehaviorDefaultImpl: Works just like CamelBehaviour does; copies variables into and out of Camel as or from properties.
*   (2) CamelBehaviorBodyAsMapImpl: Works by copying variables into and out of Camel using a Map<String,Object> object in the body.
*   (3) CamelBehaviorCamelBodyImpl: Works by copying a single variable value into Camel as a String body and copying the Camel
*      body into that same Activiti variable. The variable in Activiti must be named "camelBody".
* 
* The chosen implementation should be set within your ProcessEngineConfiguration. To specify the implementation using Spring, include
* the following line in your configuration file as part of the properties for "org.activiti.spring.SpringProcessEngineConfiguration":
* 
*   <property name="camelBehaviorClass" value="org.activiti.camel.impl.CamelBehaviorCamelBodyImpl"/>
* 
* Note also that the manner in which variables are copied to Activiti from Camel has changed. It will always copy Camel
* properties to the Activiti variable set; they can safely be ignored, of course, if not required. It will conditionally
* copy the Camel body to the "camelBody" variable if it is of type java.lang.String, OR it will copy the Camel body to
* individual variables within Activiti if it is of type Map<String,Object>.
* 
* @author Ryan Johnston (@rjfsu), Tijs Rademakers, Saeid Mirzaei
* @version 5.12
*/
public abstract class CamelBehavior extends AbstractBpmnActivityBehavior implements ActivityBehavior {

    private static final long serialVersionUID = 1L;
    protected Expression camelContext;
    protected CamelContext camelContextObj;
    protected SpringProcessEngineConfiguration springConfiguration;

    protected abstract void setPropertTargetVariable(ActivitiEndpoint endpoint);

    public enum TargetType {
        BODY_AS_MAP, BODY, PROPERTIES
    }

    protected TargetType toTargetType = null;

    protected void updateTargetVariables(ActivitiEndpoint endpoint) {
        toTargetType = null;
        if (endpoint.isCopyVariablesToBodyAsMap())
            toTargetType = TargetType.BODY_AS_MAP;
        else if (endpoint.isCopyCamelBodyToBody())
            toTargetType = TargetType.BODY;
        else if (endpoint.isCopyVariablesToProperties())
            toTargetType = TargetType.PROPERTIES;

        if (toTargetType == null)
            setPropertTargetVariable(endpoint);
    }

    protected void copyVariables(Map<String, Object> variables, Exchange exchange, ActivitiEndpoint endpoint) {
        switch (toTargetType) {
        case BODY_AS_MAP:
            copyVariablesToBodyAsMap(variables, exchange);
            break;

        case BODY:
            copyVariablesToBody(variables, exchange);
            break;

        case PROPERTIES:
            copyVariablesToProperties(variables, exchange);
        }
    }

    public void execute(ActivityExecution execution) throws Exception {
        setAppropriateCamelContext(execution);

        final ActivitiEndpoint endpoint = createEndpoint(execution);
        final Exchange exchange = createExchange(execution, endpoint);

        if (isASync(execution)) {

            FutureTask<Void> future = new FutureTask<Void>(new Callable<Void>() {
                public Void call() {
                    try {
                        endpoint.process(exchange);
                    } catch (Exception e) {
                        throw new RuntimeException("Unable to process camel endpint asynchronously.");
                    }
                    return null;
                }
            });
            ExecutorService executor = Executors.newSingleThreadExecutor();
            executor.submit(future);
            handleCamelException(exchange);

        } else {
            endpoint.process(exchange);
            handleCamelException(exchange);
            execution.setVariables(ExchangeUtils.prepareVariables(exchange, endpoint));
        }

        leave(execution);
    }

    protected ActivitiEndpoint createEndpoint(ActivityExecution execution) {
        String uri = "activiti://" + getProcessDefinitionKey(execution) + ":" + execution.getActivity().getId();
        return getEndpoint(uri);
    }

    protected ActivitiEndpoint getEndpoint(String key) {
        for (Endpoint e : camelContextObj.getEndpoints()) {
            if (e.getEndpointKey().equals(key) && (e instanceof ActivitiEndpoint)) {
                return (ActivitiEndpoint) e;
            }
        }
        throw new RuntimeException("Activiti endpoint not defined for " + key);
    }

    protected Exchange createExchange(ActivityExecution activityExecution, ActivitiEndpoint endpoint) {
        Exchange ex = new DefaultExchange(camelContextObj);
        ex.setProperty(ActivitiProducer.PROCESS_ID_PROPERTY, activityExecution.getProcessInstanceId());
        Map<String, Object> variables = activityExecution.getVariables();
        updateTargetVariables(endpoint);
        copyVariables(variables, ex, endpoint);
        return ex;
    }

    protected void handleCamelException(Exchange exchange) {
        Exception camelException = exchange.getException();
        boolean notHandledByCamel = exchange.isFailed() && camelException != null;
        if (notHandledByCamel) {
            throw new ActivitiException("Unhandled exception on camel route", camelException);
        }
    }

    protected void copyVariablesToProperties(Map<String, Object> variables, Exchange exchange) {
        for (Map.Entry<String, Object> var : variables.entrySet()) {
            exchange.setProperty(var.getKey(), var.getValue());
        }
    }

    protected void copyVariablesToBodyAsMap(Map<String, Object> variables, Exchange exchange) {
        exchange.getIn().setBody(new HashMap<String, Object>(variables));
    }

    protected void copyVariablesToBody(Map<String, Object> variables, Exchange exchange) {
        Object camelBody = variables.get(ExchangeUtils.CAMELBODY);
        if (camelBody != null) {
            exchange.getIn().setBody(camelBody);
        }
    }

    protected String getProcessDefinitionKey(ActivityExecution execution) {
        PvmProcessDefinition processDefinition = execution.getActivity().getProcessDefinition();
        return processDefinition.getKey();
    }

    protected boolean isASync(ActivityExecution execution) {
        return execution.getActivity().isAsync();
    }

    protected void setAppropriateCamelContext(ActivityExecution execution) {
        //Check to see if the springConfiguration has been set. If not, set it.
        if (springConfiguration == null) {
            //Get the ProcessEngineConfiguration object.
            ProcessEngineConfiguration engineConfiguration = Context.getProcessEngineConfiguration();

            //Convert it to a SpringProcessEngineConfiguration. If this doesn't work, throw a RuntimeException.
            // (ActivitiException extends RuntimeException.)
            try {
                springConfiguration = (SpringProcessEngineConfiguration) engineConfiguration;
            } catch (Exception e) {
                throw new ActivitiException(
                        "Expecting a SpringProcessEngineConfiguration for the Activiti Camel module.", e);
            }
        }

        //Get the appropriate String representation of the CamelContext object from ActivityExecution (if available).
        String camelContextValue = getStringFromField(camelContext, execution);

        //If the String representation of the CamelContext object from ActivityExecution is empty, use the default.
        if (StringUtils.isEmpty(camelContextValue) && camelContextObj != null) {
            //No processing required. No custom CamelContext & the default is already set.
        } else {
            if (StringUtils.isEmpty(camelContextValue) && camelContextObj == null) {
                camelContextValue = springConfiguration.getDefaultCamelContext();
            }

            //Get the CamelContext object and set the super's member variable.
            Object ctx = springConfiguration.getApplicationContext().getBean(camelContextValue);
            if (ctx == null || ctx instanceof CamelContext == false) {
                throw new ActivitiException("Could not find CamelContext named " + camelContextValue + ".");
            }
            camelContextObj = (CamelContext) ctx;
        }
    }

    protected String getStringFromField(Expression expression, DelegateExecution execution) {
        if (expression != null) {
            Object value = expression.getValue(execution);
            if (value != null) {
                return value.toString();
            }
        }
        return null;
    }

    public void setCamelContext(Expression camelContext) {
        this.camelContext = camelContext;
    }
}