org.activiti.engine.ProcessEngines.java Source code

Java tutorial

Introduction

Here is the source code for org.activiti.engine.ProcessEngines.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.engine;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.activiti.engine.impl.ProcessEngineInfoImpl;
import org.activiti.engine.impl.util.IoUtil;
import org.activiti.engine.impl.util.ReflectUtil;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Helper for initializing and closing process engines in server environments. <br>
 * All created {@link ProcessEngine}s will be registered with this class. <br>
 * The activiti-webapp-init webapp will call the {@link #init()} method when the webapp is deployed and it will call the {@link #destroy()} method when the webapp is destroyed, using a
 * context-listener (<code>org.activiti.impl.servlet.listener.ProcessEnginesServletContextListener</code>). That way, all applications can just use the {@link #getProcessEngines()} to obtain
 * pre-initialized and cached process engines. <br>
 * <br>
 * Please note that there is <b>no lazy initialization</b> of process engines, so make sure the context-listener is configured or {@link ProcessEngine}s are already created so they were registered on
 * this class.<br>
 * <br>
 * The {@link #init()} method will try to build one {@link ProcessEngine} for each flowable.cfg.xml file found on the classpath. If you have more then one, make sure you specify different
 * process.engine.name values.
 *
 * @author Tom Baeyens
 * @author Joram Barrez
 */
public abstract class ProcessEngines {

    private static final Logger LOGGER = LoggerFactory.getLogger(ProcessEngines.class);

    public static final String NAME_DEFAULT = "default";

    protected static boolean isInitialized;
    protected static Map<String, ProcessEngine> processEngines = new HashMap<>();
    protected static Map<String, ProcessEngineInfo> processEngineInfosByName = new HashMap<>();
    protected static Map<String, ProcessEngineInfo> processEngineInfosByResourceUrl = new HashMap<>();
    protected static List<ProcessEngineInfo> processEngineInfos = new ArrayList<>();

    /**
     * Initializes all process engines that can be found on the classpath for resources <code>flowable.cfg.xml</code> (plain Activiti style configuration) and for resources
     * <code>activiti-context.xml</code> (Spring style configuration).
     */
    public static synchronized void init() {
        if (!isInitialized()) {
            if (processEngines == null) {
                // Create new map to store process-engines if current map is null
                processEngines = new HashMap<>();
            }
            ClassLoader classLoader = ReflectUtil.getClassLoader();
            Enumeration<URL> resources = null;
            try {
                resources = classLoader.getResources("flowable.cfg.xml");
            } catch (IOException e) {
                throw new ActivitiIllegalArgumentException(
                        "problem retrieving flowable.cfg.xml resources on the classpath: "
                                + System.getProperty("java.class.path"),
                        e);
            }

            // Remove duplicated configuration URL's using set. Some classloaders may return identical URL's twice, causing duplicate startups
            Set<URL> configUrls = new HashSet<>();
            while (resources.hasMoreElements()) {
                configUrls.add(resources.nextElement());
            }
            for (Iterator<URL> iterator = configUrls.iterator(); iterator.hasNext();) {
                URL resource = iterator.next();
                LOGGER.info("Initializing process engine using configuration '{}'", resource.toString());
                initProcessEngineFromResource(resource);
            }

            try {
                resources = classLoader.getResources("activiti-context.xml");
            } catch (IOException e) {
                throw new ActivitiIllegalArgumentException(
                        "problem retrieving activiti-context.xml resources on the classpath: "
                                + System.getProperty("java.class.path"),
                        e);
            }
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                LOGGER.info("Initializing process engine using Spring configuration '{}'", resource.toString());
                initProcessEngineFromSpringResource(resource);
            }

            setInitialized(true);
        } else {
            LOGGER.info("Process engines already initialized");
        }
    }

    protected static void initProcessEngineFromSpringResource(URL resource) {
        try {
            Class<?> springConfigurationHelperClass = ReflectUtil
                    .loadClass("org.activiti.spring.SpringConfigurationHelper");
            Method method = springConfigurationHelperClass.getDeclaredMethod("buildProcessEngine",
                    new Class<?>[] { URL.class });
            ProcessEngine processEngine = (ProcessEngine) method.invoke(null, new Object[] { resource });

            String processEngineName = processEngine.getName();
            ProcessEngineInfo processEngineInfo = new ProcessEngineInfoImpl(processEngineName, resource.toString(),
                    null);
            processEngineInfosByName.put(processEngineName, processEngineInfo);
            processEngineInfosByResourceUrl.put(resource.toString(), processEngineInfo);

        } catch (Exception e) {
            throw new ActivitiException("couldn't initialize process engine from spring configuration resource "
                    + resource.toString() + ": " + e.getMessage(), e);
        }
    }

    /**
     * Registers the given process engine. No {@link ProcessEngineInfo} will be available for this process engine. An engine that is registered will be closed when the {@link ProcessEngines#destroy()}
     * is called.
     */
    public static void registerProcessEngine(ProcessEngine processEngine) {
        processEngines.put(processEngine.getName(), processEngine);
    }

    /**
     * Unregisters the given process engine.
     */
    public static void unregister(ProcessEngine processEngine) {
        processEngines.remove(processEngine.getName());
    }

    private static ProcessEngineInfo initProcessEngineFromResource(URL resourceUrl) {
        ProcessEngineInfo processEngineInfo = processEngineInfosByResourceUrl.get(resourceUrl.toString());
        // if there is an existing process engine info
        if (processEngineInfo != null) {
            // remove that process engine from the member fields
            processEngineInfos.remove(processEngineInfo);
            if (processEngineInfo.getException() == null) {
                String processEngineName = processEngineInfo.getName();
                processEngines.remove(processEngineName);
                processEngineInfosByName.remove(processEngineName);
            }
            processEngineInfosByResourceUrl.remove(processEngineInfo.getResourceUrl());
        }

        String resourceUrlString = resourceUrl.toString();
        try {
            LOGGER.info("initializing process engine for resource {}", resourceUrl);
            ProcessEngine processEngine = buildProcessEngine(resourceUrl);
            String processEngineName = processEngine.getName();
            LOGGER.info("initialised process engine {}", processEngineName);
            processEngineInfo = new ProcessEngineInfoImpl(processEngineName, resourceUrlString, null);
            processEngines.put(processEngineName, processEngine);
            processEngineInfosByName.put(processEngineName, processEngineInfo);
        } catch (Throwable e) {
            LOGGER.error("Exception while initializing process engine: {}", e.getMessage(), e);
            processEngineInfo = new ProcessEngineInfoImpl(null, resourceUrlString, ExceptionUtils.getStackTrace(e));
        }
        processEngineInfosByResourceUrl.put(resourceUrlString, processEngineInfo);
        processEngineInfos.add(processEngineInfo);
        return processEngineInfo;
    }

    private static ProcessEngine buildProcessEngine(URL resource) {
        InputStream inputStream = null;
        try {
            inputStream = resource.openStream();
            ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration
                    .createProcessEngineConfigurationFromInputStream(inputStream);
            return processEngineConfiguration.buildProcessEngine();

        } catch (IOException e) {
            throw new ActivitiIllegalArgumentException("couldn't open resource stream: " + e.getMessage(), e);
        } finally {
            IoUtil.closeSilently(inputStream);
        }
    }

    /**
     * Get initialization results.
     */
    public static List<ProcessEngineInfo> getProcessEngineInfos() {
        return processEngineInfos;
    }

    /**
     * Get initialization results. Only info will we available for process engines which were added in the {@link ProcessEngines#init()}. No {@link ProcessEngineInfo} is available for engines which
     * were registered programmatically.
     */
    public static ProcessEngineInfo getProcessEngineInfo(String processEngineName) {
        return processEngineInfosByName.get(processEngineName);
    }

    public static ProcessEngine getDefaultProcessEngine() {
        return getProcessEngine(NAME_DEFAULT);
    }

    /**
     * obtain a process engine by name.
     *
     * @param processEngineName is the name of the process engine or null for the default process engine.
     */
    public static ProcessEngine getProcessEngine(String processEngineName) {
        if (!isInitialized()) {
            init();
        }
        return processEngines.get(processEngineName);
    }

    /**
     * retries to initialize a process engine that previously failed.
     */
    public static ProcessEngineInfo retry(String resourceUrl) {
        LOGGER.debug("retying initializing of resource {}", resourceUrl);
        try {
            return initProcessEngineFromResource(new URL(resourceUrl));
        } catch (MalformedURLException e) {
            throw new ActivitiIllegalArgumentException("invalid url: " + resourceUrl, e);
        }
    }

    /**
     * provides access to process engine to application clients in a managed server environment.
     */
    public static Map<String, ProcessEngine> getProcessEngines() {
        return processEngines;
    }

    /**
     * closes all process engines. This method should be called when the server shuts down.
     */
    public static synchronized void destroy() {
        if (isInitialized()) {
            Map<String, ProcessEngine> engines = new HashMap<>(processEngines);
            processEngines = new HashMap<>();

            for (String processEngineName : engines.keySet()) {
                ProcessEngine processEngine = engines.get(processEngineName);
                try {
                    processEngine.close();
                } catch (Exception e) {
                    LOGGER.error("exception while closing {}",
                            (processEngineName == null ? "the default process engine"
                                    : "process engine " + processEngineName),
                            e);
                }
            }

            processEngineInfosByName.clear();
            processEngineInfosByResourceUrl.clear();
            processEngineInfos.clear();

            setInitialized(false);
        }
    }

    public static boolean isInitialized() {
        return isInitialized;
    }

    public static void setInitialized(boolean isInitialized) {
        ProcessEngines.isInitialized = isInitialized;
    }
}