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

org.ow2.petals.activitibpmn.ActivitiSE Maven / Gradle / Ivy

There is a newer version: 1.2.0
Show newest version
/**
 * Copyright (c) 2014-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;

import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DEFAULT_ENGINE_ENABLE_BPMN_VALIDATION;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DEFAULT_ENGINE_ENABLE_JOB_EXECUTOR;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.ENGINE_ENABLE_BPMN_VALIDATION;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.ENGINE_ENABLE_JOB_EXECUTOR;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.ENGINE_IDENTITY_SERVICE_CFG_FILE;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.ENGINE_IDENTITY_SERVICE_CLASS_NAME;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.Activiti.PETALS_SENDER_COMP_NAME;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.DATABASE_SCHEMA_UPDATE;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.DATABASE_TYPE;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.DEFAULT_DATABASE_SCHEMA_UPDATE;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.DEFAULT_JDBC_MAX_ACTIVE_CONNECTIONS;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.DEFAULT_JDBC_MAX_CHECKOUT_TIME;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.DEFAULT_JDBC_MAX_IDLE_CONNECTIONS;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.DEFAULT_JDBC_MAX_WAIT_TIME;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.DEFAULT_JDBC_URL_DATABASE_FILENAME;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.JDBC_DRIVER;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.JDBC_MAX_ACTIVE_CONNECTIONS;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.JDBC_MAX_CHECKOUT_TIME;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.JDBC_MAX_IDLE_CONNECTIONS;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.JDBC_MAX_WAIT_TIME;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.JDBC_PASSWORD;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.JDBC_URL;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.DBServer.JDBC_USERNAME;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.IntegrationOperation.ITG_OP_ACTIVATEPROCESSINSTANCES;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.IntegrationOperation.ITG_OP_GETPROCESSINSTANCES;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.IntegrationOperation.ITG_OP_GETTASKS;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.IntegrationOperation.ITG_OP_SUSPENDPROCESSINSTANCES;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.IntegrationOperation.ITG_PROCESSINSTANCES_PORT_TYPE_NAME;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.IntegrationOperation.ITG_TASK_PORT_TYPE_NAME;

import java.io.File;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jbi.JBIException;
import javax.xml.namespace.QName;

import org.activiti.engine.ActivitiException;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.impl.asyncexecutor.AsyncExecutor;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.parse.BpmnParseHandler;
import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.transport.ConduitInitiatorManager;
import org.ow2.easywsdl.wsdl.api.Endpoint;
import org.ow2.easywsdl.wsdl.api.WSDLException;
import org.ow2.petals.activitibpmn.event.AbstractEventListener;
import org.ow2.petals.activitibpmn.event.ProcessInstanceCanceledEventListener;
import org.ow2.petals.activitibpmn.event.ProcessInstanceCompletedEventListener;
import org.ow2.petals.activitibpmn.event.ProcessInstanceStartedEventListener;
import org.ow2.petals.activitibpmn.event.ServiceTaskStartedEventListener;
import org.ow2.petals.activitibpmn.event.UserTaskCompletedEventListener;
import org.ow2.petals.activitibpmn.event.UserTaskStartedEventListener;
import org.ow2.petals.activitibpmn.identity.IdentityService;
import org.ow2.petals.activitibpmn.identity.exception.IdentityServiceInitException;
import org.ow2.petals.activitibpmn.identity.file.FileConfigurator;
import org.ow2.petals.activitibpmn.incoming.ActivitiService;
import org.ow2.petals.activitibpmn.incoming.integration.ActivateProcessInstancesOperation;
import org.ow2.petals.activitibpmn.incoming.integration.GetProcessInstancesOperation;
import org.ow2.petals.activitibpmn.incoming.integration.GetTasksOperation;
import org.ow2.petals.activitibpmn.incoming.integration.SuspendProcessInstancesOperation;
import org.ow2.petals.activitibpmn.incoming.integration.exception.OperationInitializationException;
import org.ow2.petals.activitibpmn.outgoing.PetalsSender;
import org.ow2.petals.activitibpmn.outgoing.cxf.transport.PetalsCxfTransportFactory;
import org.ow2.petals.component.framework.listener.AbstractListener;
import org.ow2.petals.component.framework.se.AbstractServiceEngine;
import org.ow2.petals.component.framework.se.ServiceEngineServiceUnitManager;
import org.ow2.petals.component.framework.util.ServiceEndpointOperationKey;
import org.ow2.petals.component.framework.util.WSDLUtilImpl;

import com.ebmwebsourcing.easycommons.uuid.SimpleUUIDGenerator;

/**
 * The component class of the Activiti BPMN Service Engine.
 * 
 * @author Bertrand Escudie - Linagora
 */
public class ActivitiSE extends AbstractServiceEngine {

    /**
     * The Activiti BPMN Engine.
     */
    private ProcessEngine activitiEngine;

    /**
     * A map used to get the Activiti Operation associated with (end-point Name + Operation)
     */
    private final Map activitiServices = new ConcurrentHashMap();

    /**
     * The Activiti Async Executor service
     */
    private AsyncExecutor activitiAsyncExecutor = null;

    /**
     * Activation flag of the Activiti job executor
     */
    private boolean enableActivitiJobExecutor = DEFAULT_ENGINE_ENABLE_JOB_EXECUTOR;

    /**
     * Event listener fired when a process is started
     */
    private AbstractEventListener processInstanceStartedEventListener;

    /**
     * Event listener fired when a process is completed
     */
    private AbstractEventListener processInstanceCompletedEventListener;

    /**
     * Event listener fired when a process is canceled
     */
    private AbstractEventListener processInstanceCanceledEventListener;

    /**
     * Event listener fired when a service task is started
     */
    private AbstractEventListener serviceTaskStartedEventListener;

    /**
     * Event listener fired when a user task is started
     */
    private AbstractEventListener userTaskStartedEventListener;

    /**
     * Event listener fired when a user task is completed
     */
    private AbstractEventListener userTaskCompletedEventListener;

    /**
     * An UUID generator.
     */
    private final SimpleUUIDGenerator simpleUUIDGenerator = new SimpleUUIDGenerator();

    /**
     * @return the Activiti Engine
     */
    public ProcessEngine getProcessEngine() {
        return this.activitiEngine;
    }

    /**
     * @param eptAndOperation
     *            the end-point Name and operation Name
     * @param activitiservice
     *            the Activiti service
     * @return the map with the inserted elements
     */
    public void registerActivitiService(final ServiceEndpointOperationKey eptAndOperation,
            final ActivitiService activitiservice) {
        this.activitiServices.put(eptAndOperation, activitiservice);
    }

    /**
     * @param eptName
     *            the end-point name
     */
    public void removeActivitiService(final String eptName) {

        final Iterator> itEptOperationToActivitiOperation = this.activitiServices
                .entrySet().iterator();
        while (itEptOperationToActivitiOperation.hasNext()) {
            final Entry entry = itEptOperationToActivitiOperation.next();
            if (entry.getKey().getEndpointName().equals(eptName)) {
                itEptOperationToActivitiOperation.remove();
            }
        }
    }

    /**
     * @param logLevel
     */
    public void logEptOperationToActivitiOperation(final Logger logger, final Level logLevel) {
        if (logger.isLoggable(logLevel)) {
            for (final Map.Entry entry : this.activitiServices.entrySet()) {
                final ServiceEndpointOperationKey key = entry.getKey();
                logger.log(logLevel, "*** Endpoint Operation ");
                logger.log(logLevel, key.toString());
                logger.log(logLevel, "------------------------------------------------------ ");
                entry.getValue().log(logger, logLevel);
                logger.log(logLevel, "******************* ");
            }
        }
    }

    /**
     * @param eptAndOperation
     *            the end-point Name and operation Name
     * @return the Activiti Service associated with this end-point name and operation Name
     */
    public ActivitiService getActivitiServices(final ServiceEndpointOperationKey eptAndOperation) {
        return this.activitiServices.get(eptAndOperation);
    }

    @Override
    public void doInit() throws JBIException {
        this.getLogger().fine("Start ActivitiSE.doInit()");

        try {
            // JDBC Driver
            final String jdbcDriver = ActivitiParameterReader.getJdbcDriver(
                    this.getComponentExtensions().get(JDBC_DRIVER), this.getLogger());

            // JDBC URL
            final String jdbcUrlConfigured = this.getComponentExtensions().get(JDBC_URL);
            final String jdbcUrl;
            if (jdbcUrlConfigured == null || jdbcUrlConfigured.trim().isEmpty()) {
                // JDBC URL not set or empty ---> Default value:
                // $PETALS_HOME/data/repository/components//h2-activiti.db
                this.getLogger().info("No JDBC URL configured for database. Default value used.");
                final File databaseFile = new File(this.getContext().getWorkspaceRoot(),
                        DEFAULT_JDBC_URL_DATABASE_FILENAME);
                try {
                    jdbcUrl = String.format("jdbc:h2:%s", databaseFile.toURI().toURL().toExternalForm());
                } catch (final MalformedURLException e) {
                    // This exception should not occur. It's a bug
                    throw new JBIException("The defaul JDBC URL is invalid !!", e);
                }
            } else {
                jdbcUrl = jdbcUrlConfigured;
            }

            final String jdbcUsername = this.getComponentExtensions().get(JDBC_USERNAME);
            final String jdbcPassword = this.getComponentExtensions().get(JDBC_PASSWORD);

            final String jdbcMaxActiveConnectionsConfigured = this.getComponentExtensions().get(
                    JDBC_MAX_ACTIVE_CONNECTIONS);
            int jdbcMaxActiveConnections;
            if (jdbcMaxActiveConnectionsConfigured == null || jdbcMaxActiveConnectionsConfigured.trim().isEmpty()) {
                this.getLogger().info("No JDBC Max Active Connections configured for database. Default value used.");
                jdbcMaxActiveConnections = DEFAULT_JDBC_MAX_ACTIVE_CONNECTIONS;
            } else {
                try {
                    jdbcMaxActiveConnections = Integer.parseInt(jdbcMaxActiveConnectionsConfigured);
                } catch (final NumberFormatException e) {
                    this.getLogger().warning(
                            "Invalid value for the number of JDBC Max Active Connections. Default value used.");
                    jdbcMaxActiveConnections = DEFAULT_JDBC_MAX_ACTIVE_CONNECTIONS;
                }
            }

            final String jdbcMaxIdleConnectionsConfigured = this.getComponentExtensions()
                    .get(JDBC_MAX_IDLE_CONNECTIONS);
            int jdbcMaxIdleConnections;
            if (jdbcMaxIdleConnectionsConfigured == null || jdbcMaxIdleConnectionsConfigured.trim().isEmpty()) {
                this.getLogger().info("No JDBC Max Idle Connections configured for database. Default value used.");
                jdbcMaxIdleConnections = DEFAULT_JDBC_MAX_IDLE_CONNECTIONS;
            } else {
                try {
                    jdbcMaxIdleConnections = Integer.parseInt(jdbcMaxIdleConnectionsConfigured);
                } catch (final NumberFormatException e) {
                    this.getLogger().warning(
                            "Invalid value for the number of JDBC Max Idle Connections. Default value used.");
                    jdbcMaxIdleConnections = DEFAULT_JDBC_MAX_IDLE_CONNECTIONS;
                }
            }

            final String jdbcMaxCheckoutTimeConfigured = this.getComponentExtensions().get(JDBC_MAX_CHECKOUT_TIME);
            int jdbcMaxCheckoutTime;
            if (jdbcMaxCheckoutTimeConfigured == null || jdbcMaxCheckoutTimeConfigured.trim().isEmpty()) {
                this.getLogger().info("No JDBC Max Checkout Time configured for database. Default value used.");
                jdbcMaxCheckoutTime = DEFAULT_JDBC_MAX_CHECKOUT_TIME;
            } else {
                try {
                    jdbcMaxCheckoutTime = Integer.parseInt(jdbcMaxCheckoutTimeConfigured);
                } catch (final NumberFormatException e) {
                    this.getLogger().warning(
                            "Invalid value for the number of JDBC Max Checkout Time. Default value used.");
                    jdbcMaxCheckoutTime = DEFAULT_JDBC_MAX_CHECKOUT_TIME;
                }
            }

            final String jdbcMaxWaitTimeConfigured = this.getComponentExtensions().get(JDBC_MAX_WAIT_TIME);
            int jdbcMaxWaitTime;
            if (jdbcMaxWaitTimeConfigured == null || jdbcMaxWaitTimeConfigured.trim().isEmpty()) {
                this.getLogger().info("No JDBC Max Wait Time configured for database. Default value used.");
                jdbcMaxWaitTime = DEFAULT_JDBC_MAX_WAIT_TIME;
            } else {
                try {
                    jdbcMaxWaitTime = Integer.parseInt(jdbcMaxWaitTimeConfigured);
                } catch (final NumberFormatException e) {
                    this.getLogger().warning("Invalid value for the number of JDBC Max Wait Time. Default value used.");
                    jdbcMaxWaitTime = DEFAULT_JDBC_MAX_WAIT_TIME;
                }
            }

            /* DATABASE_TYPE Possible values: {h2, mysql, oracle, postgres, mssql, db2}. */
            final String databaseType = this.getComponentExtensions().get(DATABASE_TYPE);

            /* DATABASE_SCHEMA_UPDATE Possible values: {false, true, create-drop } */
            /*
             * TODO Test the Database Schema Version What about databaseSchemaUpdate values "true" and "create-drop"
             */
            final String databaseSchemaUpdateConfigured = this.getComponentExtensions().get(DATABASE_SCHEMA_UPDATE);
            final String databaseSchemaUpdate;
            if (databaseSchemaUpdateConfigured == null || databaseSchemaUpdateConfigured.trim().isEmpty()) {
                this.getLogger().info("No schema update processing configured for database. Default value used.");
                databaseSchemaUpdate = DEFAULT_DATABASE_SCHEMA_UPDATE;
            } else if (databaseSchemaUpdateConfigured.trim().equals("false")
                    || databaseSchemaUpdateConfigured.trim().equals("true")
                    || databaseSchemaUpdateConfigured.trim().equals("create-drop")) {
                databaseSchemaUpdate = databaseSchemaUpdateConfigured.trim();
            } else {
                this.getLogger().info(
                        "Invalid value '" + databaseSchemaUpdateConfigured
                                + "' configured for the schema update processing. Default value used.");
                databaseSchemaUpdate = DEFAULT_DATABASE_SCHEMA_UPDATE;
            }

            /* TODO Test Activiti database connection configuration */
            /* TODO Set the non set value with default value */

            this.getLogger().config("DB configuration:");
            this.getLogger().config("   - " + JDBC_DRIVER + " = " + jdbcDriver);
            this.getLogger().config("   - " + JDBC_URL + " = " + jdbcUrl);
            this.getLogger().config("   - " + JDBC_USERNAME + " = " + jdbcUsername);
            this.getLogger().config("   - " + JDBC_PASSWORD + " = " + jdbcPassword);
            this.getLogger().config("   - " + JDBC_MAX_ACTIVE_CONNECTIONS + " = " + jdbcMaxActiveConnections);
            this.getLogger().config("   - " + JDBC_MAX_IDLE_CONNECTIONS + " = " + jdbcMaxIdleConnections);
            this.getLogger().config("   - " + JDBC_MAX_CHECKOUT_TIME + " = " + jdbcMaxCheckoutTime);
            this.getLogger().config("   - " + JDBC_MAX_WAIT_TIME + " = " + jdbcMaxWaitTime);
            this.getLogger().config("   - " + DATABASE_TYPE + " = " + databaseType);
            this.getLogger().config("   - " + DATABASE_SCHEMA_UPDATE + " = " + databaseSchemaUpdate);

            // Caution:
            // - only the value "false", ignoring case and spaces will disable the job executor,
            // - only the value "true", ignoring case and spaces will enable the job executor,
            // - otherwise, the default value is used.
            final String enableActivitiJobExecutorConfigured = this.getComponentExtensions().get(
                    ENGINE_ENABLE_JOB_EXECUTOR);
            if (enableActivitiJobExecutorConfigured == null || enableActivitiJobExecutorConfigured.trim().isEmpty()) {
                this.getLogger().info(
                        "The activation of the Activiti job executor is not configured. Default value used.");
                this.enableActivitiJobExecutor = DEFAULT_ENGINE_ENABLE_JOB_EXECUTOR;
            } else {
                this.enableActivitiJobExecutor = enableActivitiJobExecutorConfigured.trim().equalsIgnoreCase("false") ? false
                        : (enableActivitiJobExecutorConfigured.trim().equalsIgnoreCase("true") ? true
                                : DEFAULT_ENGINE_ENABLE_JOB_EXECUTOR);
            }

            // Caution:
            // - only the value "false", ignoring case and spaces will disable the BPMN validation,
            // - only the value "true", ignoring case and spaces will enable the BPMN validation,
            // - otherwise, the default value is used.
            final String enableActivitiBpmnValidationConfigured = this.getComponentExtensions().get(
                    ENGINE_ENABLE_BPMN_VALIDATION);
            final boolean enableActivitiBpmnValidation;
            if (enableActivitiBpmnValidationConfigured == null
                    || enableActivitiBpmnValidationConfigured.trim().isEmpty()) {
                this.getLogger()
                        .info("The activation of the BPMN validation during process deployments is not configured. Default value used.");
                enableActivitiBpmnValidation = DEFAULT_ENGINE_ENABLE_BPMN_VALIDATION;
            } else {
                enableActivitiBpmnValidation = enableActivitiBpmnValidationConfigured.trim().equalsIgnoreCase(
                        "false") ? false
                        : (enableActivitiBpmnValidationConfigured.trim().equalsIgnoreCase("true") ? true
                                : DEFAULT_ENGINE_ENABLE_BPMN_VALIDATION);
            }

            ((ActivitiSuManager) getServiceUnitManager()).setEnableActivitiBpmnValidation(enableActivitiBpmnValidation);

            final Class identityServiceClass = ActivitiParameterReader.getEngineIdentityServiceClassName(this
                    .getComponentExtensions().get(ENGINE_IDENTITY_SERVICE_CLASS_NAME), this.getLogger());

            final File identityServiceCfgFile = ActivitiParameterReader.getEngineIdentityServiceConfigurationFile(this
                    .getComponentExtensions().get(ENGINE_IDENTITY_SERVICE_CFG_FILE), this.getLogger());

            this.getLogger().config("Activiti engine configuration:");
            this.getLogger().config("   - " + ENGINE_ENABLE_JOB_EXECUTOR + " = " + this.enableActivitiJobExecutor);
            this.getLogger().config("   - " + ENGINE_ENABLE_BPMN_VALIDATION + " = " + enableActivitiBpmnValidation);
            this.getLogger().config(
                    "   - " + ENGINE_IDENTITY_SERVICE_CLASS_NAME + " = " + identityServiceClass.getName());
            this.getLogger().config(
                    "   - " + ENGINE_IDENTITY_SERVICE_CFG_FILE + " = "
                            + (identityServiceCfgFile == null ? "" : identityServiceCfgFile.getAbsolutePath()));

            /* Create an Activiti ProcessEngine with database configuration */
            final ProcessEngineConfiguration pec = ProcessEngineConfiguration
                    .createStandaloneProcessEngineConfiguration();
            pec.setJdbcDriver(jdbcDriver);
            pec.setJdbcUrl(jdbcUrl);
            pec.setJdbcUsername(jdbcUsername).setJdbcPassword(jdbcPassword);
            pec.setJdbcMaxActiveConnections(jdbcMaxActiveConnections);
            pec.setJdbcMaxIdleConnections(jdbcMaxIdleConnections);
            pec.setJdbcMaxCheckoutTime(jdbcMaxCheckoutTime);
            pec.setJdbcMaxWaitTime(jdbcMaxWaitTime);
            if (databaseType != null && !databaseType.trim().isEmpty()) {
                pec.setDatabaseType(databaseType);
            }
            pec.setDatabaseSchemaUpdate(databaseSchemaUpdate);
            pec.setJobExecutorActivate(false);

            // We register the Petals transport into Apache CXF
            this.registerCxfPetalsTransport();

            // As recommended by Activiti team, we prefer the Async Job Executor
            pec.setJobExecutorActivate(false);
            // The Async job is enabled ...
            pec.setAsyncExecutorEnabled(this.enableActivitiJobExecutor);
            // ... but must be started when starting the SE
            pec.setAsyncExecutorActivate(false);

            // Override the default configuration of the identity service.
            this.registerIdentityService(pec, identityServiceClass, identityServiceCfgFile);

            // Add post BPMN parse handlers
            this.addPostBpmnParseHandlers(pec);

            this.activitiEngine = pec.buildProcessEngine();
            this.activitiAsyncExecutor = this.enableActivitiJobExecutor ? pec.getAsyncExecutor() : null;

            // Caution: Configuration beans are initialized when building the process engine
            if (pec instanceof ProcessEngineConfigurationImpl) {
                // We add to the BPMN engine the bean in charge of sending Petals message exchange
                final AbstractListener petalsSender = new PetalsSender(this);
                ((ProcessEngineConfigurationImpl) pec).getBeans().put(PETALS_SENDER_COMP_NAME, petalsSender);
            } else {
                this.getLogger()
                        .warning(
                                "The implementation of the process engine configuration is not the expected one ! No Petals services can be invoked !");
            }

            this.registersIntegrationOperations();

        } catch (final ActivitiException e) {
            throw new JBIException("An error occurred while creating the Activiti BPMN Engine.", e);
        } finally {
            this.getLogger().fine("End ActivitiSE.doInit()");
        }
    }

    /**
     * Registers integration operations
     */
    private void registersIntegrationOperations() {

        // Register integration operation
        final List integrationEndpoints = WSDLUtilImpl.getEndpointList(this.getNativeWsdl().getDescription());
        if (integrationEndpoints.size() > 0) {
            try {
                for (final Endpoint endpoint : integrationEndpoints) {
                    final String integrationEndpointName = endpoint.getName();
                    final QName integrationServiceName = endpoint.getService().getQName();
                    final QName integrationInterfaceName = endpoint.getService().getInterface().getQName();
                    try {
                        if (ITG_PROCESSINSTANCES_PORT_TYPE_NAME.equals(integrationInterfaceName.getLocalPart())) {
                            this.activitiServices.put(
                                    new ServiceEndpointOperationKey(integrationServiceName, integrationEndpointName,
                                            ITG_OP_GETPROCESSINSTANCES),
                                    new GetProcessInstancesOperation(this.activitiEngine.getRuntimeService(),
                                            this.activitiEngine.getHistoryService(), this.activitiEngine
                                                    .getRepositoryService(), this.getLogger()));
                            this.activitiServices.put(new ServiceEndpointOperationKey(integrationServiceName, integrationEndpointName,
                                    ITG_OP_SUSPENDPROCESSINSTANCES),
                                    new SuspendProcessInstancesOperation(this.activitiEngine.getRuntimeService(), this.getLogger()));
                            this.activitiServices.put(
                                    new ServiceEndpointOperationKey(integrationServiceName, integrationEndpointName,
                                            ITG_OP_ACTIVATEPROCESSINSTANCES),
                                    new ActivateProcessInstancesOperation(this.activitiEngine.getRuntimeService(), this
                                            .getLogger()));
                        } else if (ITG_TASK_PORT_TYPE_NAME.equals(integrationInterfaceName.getLocalPart())) {
                            this.activitiServices.put(new ServiceEndpointOperationKey(integrationServiceName, integrationEndpointName,
                                    ITG_OP_GETTASKS), new GetTasksOperation(
                                    this.activitiEngine.getTaskService(), this.activitiEngine.getRepositoryService(),
                                    this.getLogger()));
                        } else {
                            this.getLogger().log(Level.WARNING,
                                    "Unexpected/Uknown integration operations: " + integrationInterfaceName);
                        }
                    } catch (final OperationInitializationException e) {
                        this.getLogger().log(Level.WARNING,
                                "Error registering the integration operation '" + integrationInterfaceName + "'.", e);
                    }
                }
            } catch (final WSDLException e) {
                this.getLogger().log(Level.WARNING, "Integration operations are not completly initialized", e);
            }
        } else {
            this.getLogger().warning("No endpoint exists to execute integration operations");
        }
    }

    /**
     * Initialize the identity service
     * 
     * @param pec
     *            The Activiti process engine configuration. Not null.
     * @param identityServiceClass
     *            The identity service class to use. Not null. Must implement {@link IdentityService}.
     * @param identityServiceCfgFile
     *            The identity service configuration file. If null, the default configuration of the
     *            identity service will be used.
     */
    private final void registerIdentityService(final ProcessEngineConfiguration pec,
            final Class identityServiceClass, final File identityServiceCfgFile) throws JBIException {

        assert pec != null : "pec can not be null";
        assert identityServiceClass != null : "identityServiceClass can not be null";
        assert IdentityService.class.isAssignableFrom(identityServiceClass) : "The identity service class does not implement IdentityService";

        Object identityServiceObj;
        try {
            identityServiceObj = identityServiceClass.newInstance();
            assert identityServiceObj instanceof IdentityService;
            final IdentityService identityService = (IdentityService) identityServiceObj;

            identityService.init(identityServiceCfgFile);

            if (pec instanceof ProcessEngineConfigurationImpl) {
                ((ProcessEngineConfigurationImpl) pec).addConfigurator(new FileConfigurator(identityService));
            } else {
                this.getLogger()
                        .warning(
                                "The implementation of the process engine configuration is not the expected one ! Identity service not overriden !");
            }
        } catch (final InstantiationException | IllegalAccessException | IdentityServiceInitException e) {
            throw new JBIException("An error occurred while instantiating the identity service.", e);
        }
    }

    /**
     * Add BPMN parse handlers executed after the default ones
     * 
     * @param pec
     *            The Activiti process engine configuration. Not null.
     */
    private final void addPostBpmnParseHandlers(final ProcessEngineConfiguration pec) throws JBIException {

        assert pec != null : "pec can not be null";

        if (pec instanceof ProcessEngineConfigurationImpl) {
            final List postBpmnParseHandlers = new ArrayList();
            postBpmnParseHandlers.add(new ServiceTaskForceAsyncParseHandler());
            ((ProcessEngineConfigurationImpl) pec).setPostBpmnParseHandlers(postBpmnParseHandlers);
        } else {
            this.getLogger()
                    .warning(
                            "The implementation of the process engine configuration is not the expected one ! Identity service not overriden !");
        }
    }

    @Override
    public void doStart() throws JBIException {
        this.getLogger().fine("Start ActivitiSE.doStart()");

        // Create & Register event listeners
        final RuntimeService runtimeService = this.activitiEngine.getRuntimeService();
        this.processInstanceStartedEventListener = new ProcessInstanceStartedEventListener(this.getLogger());
        runtimeService.addEventListener(this.processInstanceStartedEventListener,
                this.processInstanceStartedEventListener.getListenEventType());

        this.processInstanceCompletedEventListener = new ProcessInstanceCompletedEventListener(this.getLogger());
        runtimeService.addEventListener(this.processInstanceCompletedEventListener,
                this.processInstanceCompletedEventListener.getListenEventType());

        this.processInstanceCanceledEventListener = new ProcessInstanceCanceledEventListener(this.getLogger());
        runtimeService.addEventListener(this.processInstanceCanceledEventListener,
                this.processInstanceCanceledEventListener.getListenEventType());

        this.serviceTaskStartedEventListener = new ServiceTaskStartedEventListener(this.getLogger());
        runtimeService.addEventListener(this.serviceTaskStartedEventListener,
                this.serviceTaskStartedEventListener.getListenEventType());

        this.userTaskStartedEventListener = new UserTaskStartedEventListener(this.simpleUUIDGenerator, this.getLogger());
        runtimeService.addEventListener(this.userTaskStartedEventListener,
                this.userTaskStartedEventListener.getListenEventType());

        this.userTaskCompletedEventListener = new UserTaskCompletedEventListener(this.getLogger());
        runtimeService.addEventListener(this.userTaskCompletedEventListener,
                this.userTaskCompletedEventListener.getListenEventType());

        try {
            // Startup Activiti engine against running states of the SE:
            // - Activiti Engine must be started when the SE is in state 'STOPPED' to be able to deploy process
            // definitions
            // - In state 'STOPPED', the SE will not process incoming requests, so no process instance creation and no
            // user task completion will occur
            // - To avoid the executions of activities trigerred by timer or other events, the Activiti job executor
            // must be stopped when the SE is in state 'STOPPED'
            if (this.enableActivitiJobExecutor) {
                if (this.activitiAsyncExecutor != null) {
                    if (this.activitiAsyncExecutor.isActive()) {
                        this.getLogger().warning("Activiti Job Executor already started !!");
                    } else {
                        this.activitiAsyncExecutor.start();
                    }
                } else {
                    this.getLogger().warning("No Activiti Job Executor exists !!");
                }
            } else {
                this.getLogger().info("Activiti Job Executor not started because it is not activated.");
            }

            // TODO: Add JMX operation to start/stop the Activiti job executor when the component is started
            // TODO: Add JMX operation to disable/enable the Activiti job executor when the component is running

        } catch (final ActivitiException e) {
            throw new JBIException("An error occurred while starting the Activiti BPMN Engine.", e);
        } finally {
            this.getLogger().fine("End ActivitiSE.doStart()");
        }
    }

    @Override
    public void doStop() throws JBIException {
        this.getLogger().fine("Start ActivitiSE.doStop()");

        try {
            // Stop the Activiti Job Executor */
            if (this.enableActivitiJobExecutor) {
                if (this.activitiAsyncExecutor != null) {
                    if (this.activitiAsyncExecutor.isActive()) {
                        this.activitiAsyncExecutor.shutdown();
                    } else {
                        this.getLogger().warning("Activiti Job Executor not started !!");
                    }
                } else {
                    this.getLogger().warning("No Activiti Job Executor exists !!");
                }
            } else {
                this.getLogger().info("Activiti Job Executor not stopped because it is not activated.");
            }

        } catch (final ActivitiException e) {
            throw new JBIException("An error occurred while stopping the Activiti BPMN Engine.", e);
        } finally {
            this.getLogger().fine("End ActivitiSE.doStop()");
        }

        // Deregister event listeners
        final RuntimeService runtimeService = this.activitiEngine.getRuntimeService();
        runtimeService.removeEventListener(this.processInstanceStartedEventListener);
        runtimeService.removeEventListener(this.processInstanceCompletedEventListener);
        runtimeService.removeEventListener(this.processInstanceCanceledEventListener);
        runtimeService.removeEventListener(this.serviceTaskStartedEventListener);
        runtimeService.removeEventListener(this.userTaskStartedEventListener);
        runtimeService.removeEventListener(this.userTaskCompletedEventListener);

    }

    @Override
    public void doShutdown() throws JBIException {
        this.getLogger().fine("Start ActivitiSE.doShutdown()");

        try {
            if (this.activitiEngine != null) {
                this.activitiEngine.close();
            }

        } catch (final ActivitiException e) {
            throw new JBIException("An error occurred while shutdowning the Activiti BPMN Engine.", e);
        } finally {
            this.getLogger().fine("End ActivitiSE.doShutdown()");
        }
    }

    @Override
    protected ServiceEngineServiceUnitManager createServiceUnitManager() {
        return new ActivitiSuManager(this, this.simpleUUIDGenerator);
    }

    private void registerCxfPetalsTransport() {
        final Bus bus = BusFactory.getThreadDefaultBus();
        final PetalsCxfTransportFactory cxfPetalsTransport = new PetalsCxfTransportFactory();
        final ConduitInitiatorManager extension = bus.getExtension(ConduitInitiatorManager.class);
        extension.registerConduitInitiator(PetalsCxfTransportFactory.TRANSPORT_ID, cxfPetalsTransport);
        // TODO: Set a timeout at CXF client level (it should be the same than the tiemout at NMR level)
        // TODO: Add unit tests about timeout
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy