org.jbpm.JbpmConfiguration.java Source code

Java tutorial

Introduction

Here is the source code for org.jbpm.JbpmConfiguration.java

Source

/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jbpm;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jbpm.configuration.ObjectFactory;
import org.jbpm.configuration.ObjectFactoryImpl;
import org.jbpm.configuration.ObjectFactoryParser;
import org.jbpm.configuration.ObjectInfo;
import org.jbpm.configuration.ValueInfo;
import org.jbpm.job.executor.JobExecutor;
import org.jbpm.persistence.db.DbPersistenceServiceFactory;
import org.jbpm.persistence.db.StaleObjectLogConfigurer;
import org.jbpm.svc.ServiceFactory;
import org.jbpm.svc.Services;
import org.jbpm.util.ClassLoaderUtil;

/**
 * configuration of one jBPM instance.
 * 
 * <p>During process execution, jBPM might need to use some services.  
 * A JbpmConfiguration contains the knowledge on how to create those services.
 * </p>
 * 
 * <p>A JbpmConfiguration is a thread safe object and serves as a factory for 
 * {@link org.jbpm.JbpmContext}s, which means one JbpmConfiguration 
 * can be used to create {@link org.jbpm.JbpmContext}s for all threads. 
 * The single JbpmConfiguration can be maintained in a static member or 
 * in the JNDI tree if that is available.
 * </p>
 * 
 * <p>A JbpmConfiguration can be obtained in following ways:
 * <ul>
 *   <li>from a resource (by default <code>jbpm.cfg.xml</code> is used):
 * <pre> JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
 * </pre>
 * or
 * <pre> String myXmlResource = "...";
 * JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance(myXmlResource);</pre> 
 *   </li>
 *   <li>from an XML string:
 * <pre> JbpmConfiguration jbpmConfiguration = JbpmConfiguration.parseXmlString(
 *   "<jbpm-configuration>" +
 *   ...
 *   "</jbpm-configuration>"
 * );
 * </pre>
 *   </li>
 *   <li>By specifying a custom implementation of an object factory.  This can be 
 *   used to specify a JbpmConfiguration in other bean-style notations such as 
 *   used by JBoss Microcontainer or Spring.
 * <pre> ObjectFactory of = new <i>MyCustomObjectFactory</i>();
 * JbpmConfiguration.Configs.setDefaultObjectFactory(of);
 * JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
 * </pre>
 *   </li>
 * </ul>
 * </p>
 * 
 * <p>JbpmConfigurations can be configured using a spring-like XML notation
 * (in relax ng compact notation):
 * </p>
 * 
 * <pre>
 * datatypes xs = "http://www.w3.org/2001/XMLSchema-datatypes"
 * 
 * start = element beans { element object* }
 * 
 * object = {
 *   jbpm-context |
 *   bean |
 *   ref |
 *   map |
 *   list |
 *   string |
 *   int |
 *   long |
 *   float |
 *   double |
 *   char |
 *   bool |
 *   true |
 *   false |
 *   null
 * }
 * 
 * jbpm-context = element jbpm-context {
 *   ( attribute name {xsd:string},
 *     service*,
 *     save-operations? 
 *   )
 * }
 * 
 * service = element service {
 *   ( attribute name {xsd:string},
 *     ( attribute factory {xsd:string} ) |
 *     ( factory )
 *   )
 * }
 * 
 * factory = element factory {
 *   ( bean |
 *     ref
 *   )
 * }
 * 
 * save-operations = element save-operations {
 *   ( save-operation* )
 * }
 * 
 * save-operation = element save-operation {
 *   ( ( attribute class {xsd:string} ) |
 *     ( bean |
 *       ref
 *     ) 
 *   )
 * }
 * 
 * bean = element bean {
 *   ( attribute ref-name {xsd:string} ) |
 *   ( attribute name {xsd:string}?,
 *     attribute class {xsd:string}?,
 *     attribute singleton { "true" | "false" }?,
 *     constructor*,
 *     field*,
 *     property*
 *   )
 * }
 * 
 * ref = element ref {
 *   ( attribute bean (xsd:string) )
 * }
 * 
 * constructor = element constructor {
 *   attribute class {xsd:string}?,
 *   ( attribute factory {xsd:string}, 
 *     attribute method {xsd:string}
 *   )?,
 *   parameter*
 * }
 * 
 * parameter = element parameter {
 *   attribute class {xsd:string},
 *   object
 * }
 * 
 * field = element field {
 *   attribute name {xsd:string},
 *   object
 * }
 * 
 * property = element property {
 *   ( attribute name {xsd:string} |
 *     attribute setter {xsd:string}
 *   ),
 *   object
 * }
 * 
 * map = element map {
 *   entry*
 * }
 * 
 * entry = element entry { 
 *   key, 
 *   value 
 * }
 * 
 * key = element key {
 *   object
 * }
 * 
 * value = element value {
 *   object
 * }
 * 
 * list = element list {
 *   object*
 * }
 * 
 * string = element string {xsd:string}
 * int    = element integer {xsd:integer}
 * long   = element long {xsd:long}
 * float  = element float {xsd:string}
 * double = element string {xsd:double}
 * char   = element char {xsd:character}
 * bool   = element bool { "true" | "false" }
 * true   = element true {}
 * false  = element false {}
 * null   = element null {}
 * </pre>
 * </p>
 * 
 * <p>
 * Other configuration properties
 * <table>
 *   <tr>
 *     <td>jbpm.files.dir</td><td></td>
 *   </tr>
 *   <tr>
 *     <td>jbpm.types</td><td></td>
 *   </tr>
 * </table>
 * </p>
 */
public class JbpmConfiguration implements Serializable {

    private static final long serialVersionUID = 1L;

    static ObjectFactory defaultObjectFactory = null;
    static Map instances = new HashMap();

    /**
     * resets static members for test isolation.
     */
    static void reset() {
        defaultObjectFactory = null;
        instances = new HashMap();
    }

    ObjectFactory objectFactory = null;
    static ThreadLocal jbpmConfigurationsStacks = new ThreadLocal();
    ThreadLocal jbpmContextStacks = new ThreadLocal();
    JobExecutor jobExecutor = null;

    public JbpmConfiguration(ObjectFactory objectFactory) {
        this.objectFactory = objectFactory;
    }

    public static JbpmConfiguration getInstance() {
        return getInstance(null);
    }

    public static JbpmConfiguration getInstance(String resource) {

        JbpmConfiguration instance = null;

        synchronized (instances) {
            if (resource == null) {
                resource = "jbpm.cfg.xml";
            }

            instance = (JbpmConfiguration) instances.get(resource);
            if (instance == null) {

                if (defaultObjectFactory != null) {
                    log.debug("creating jbpm configuration from given default object factory '"
                            + defaultObjectFactory + "'");
                    instance = new JbpmConfiguration(defaultObjectFactory);

                } else {

                    try {
                        log.info("using jbpm configuration resource '" + resource + "'");
                        InputStream jbpmCfgXmlStream = ClassLoaderUtil.getStream(resource);

                        // if a resource SHOULD BE used, but is not found in the classpath
                        // throw exception (otherwise, the user wants to load own stuff
                        // but is confused, if it is not found and not loaded, without
                        // any notice)
                        if (jbpmCfgXmlStream == null)
                            throw new JbpmException(
                                    "jbpm configuration resource '" + resource + "' is not available");

                        ObjectFactory objectFactory = parseObjectFactory(jbpmCfgXmlStream);
                        instance = createJbpmConfiguration(objectFactory);

                    } catch (RuntimeException e) {
                        throw new JbpmException(
                                "couldn't parse jbpm configuration from resource '" + resource + "'", e);
                    }
                }

                instances.put(resource, instance);
            }
        }

        return instance;
    }

    public static boolean hasInstance(String resource) {
        boolean hasInstance = false;
        if (resource == null) {
            resource = "jbpm.cfg.xml";
        }
        if ((instances != null) && (instances.containsKey(resource))) {
            hasInstance = true;
        }
        return hasInstance;
    }

    protected static ObjectFactory parseObjectFactory(InputStream inputStream) {
        log.debug("loading defaults in jbpm configuration");
        ObjectFactoryParser objectFactoryParser = new ObjectFactoryParser();
        ObjectFactoryImpl objectFactoryImpl = new ObjectFactoryImpl();
        objectFactoryParser.parseElementsFromResource("org/jbpm/default.jbpm.cfg.xml", objectFactoryImpl);

        if (inputStream != null) {
            log.debug("loading specific configuration...");
            objectFactoryParser.parseElementsStream(inputStream, objectFactoryImpl);
        }

        return objectFactoryImpl;
    }

    /**
     * create an ObjectFacotory from an XML string.
     */
    public static JbpmConfiguration parseXmlString(String xml) {
        log.debug("creating jbpm configuration from xml string");
        InputStream inputStream = null;
        if (xml != null) {
            inputStream = new ByteArrayInputStream(xml.getBytes());
        }
        ObjectFactory objectFactory = parseObjectFactory(inputStream);
        return createJbpmConfiguration(objectFactory);
    }

    protected static JbpmConfiguration createJbpmConfiguration(ObjectFactory objectFactory) {
        JbpmConfiguration jbpmConfiguration = new JbpmConfiguration(objectFactory);

        // now we make the bean jbpm.configuration always availble 
        if (objectFactory instanceof ObjectFactoryImpl) {
            ObjectFactoryImpl objectFactoryImpl = (ObjectFactoryImpl) objectFactory;
            ObjectInfo jbpmConfigurationInfo = new ValueInfo("jbpmConfiguration", jbpmConfiguration);
            objectFactoryImpl.addObjectInfo(jbpmConfigurationInfo);

            if (mustStaleObjectExceptionsBeHidden(objectFactory)) {
                StaleObjectLogConfigurer.hideStaleObjectExceptions();
            }
        }

        return jbpmConfiguration;
    }

    private static boolean mustStaleObjectExceptionsBeHidden(ObjectFactory objectFactory) {
        if (!objectFactory.hasObject("jbpm.hide.stale.object.exceptions")) {
            return true;
        }
        Object o = (Boolean) objectFactory.createObject("jbpm.hide.stale.object.exceptions");
        if ((o instanceof Boolean) && (((Boolean) o).booleanValue() == false)) {
            return false;
        }
        return true;
    }

    public static JbpmConfiguration parseInputStream(InputStream inputStream) {
        ObjectFactory objectFactory = parseObjectFactory(inputStream);
        log.debug("creating jbpm configuration from input stream");
        return createJbpmConfiguration(objectFactory);
    }

    public static JbpmConfiguration parseResource(String resource) {
        InputStream inputStream = null;
        log.debug("creating jbpm configuration from resource '" + resource + "'");
        if (resource != null) {
            inputStream = ClassLoaderUtil.getStream(resource);
        }
        ObjectFactory objectFactory = parseObjectFactory(inputStream);
        return createJbpmConfiguration(objectFactory);
    }

    public JbpmContext createJbpmContext() {
        return createJbpmContext(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME);
    }

    public JbpmContext createJbpmContext(String name) {
        JbpmContext jbpmContext = (JbpmContext) objectFactory.createObject(name);
        jbpmContext.jbpmConfiguration = this;
        jbpmContextCreated(jbpmContext);
        return jbpmContext;
    }

    public ServiceFactory getServiceFactory(String serviceName) {
        return getServiceFactory(serviceName, JbpmContext.DEFAULT_JBPM_CONTEXT_NAME);
    }

    public ServiceFactory getServiceFactory(String serviceName, String jbpmContextName) {
        ServiceFactory serviceFactory = null;
        JbpmContext jbpmContext = createJbpmContext(jbpmContextName);
        try {
            serviceFactory = jbpmContext.getServices().getServiceFactory(serviceName);
        } finally {
            jbpmContext.close();
        }
        return serviceFactory;
    }

    /**
     * gives the jbpm domain model access to configuration information via the current JbpmContext.
     */
    public abstract static class Configs {
        public static ObjectFactory getObjectFactory() {
            ObjectFactory objectFactory = null;
            JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
            if (jbpmContext != null) {
                objectFactory = jbpmContext.objectFactory;
            } else {
                objectFactory = getInstance().objectFactory;
            }
            return objectFactory;
        }

        public static void setDefaultObjectFactory(ObjectFactory objectFactory) {
            defaultObjectFactory = objectFactory;
        }

        public static boolean hasObject(String name) {
            ObjectFactory objectFactory = getObjectFactory();
            return objectFactory.hasObject(name);
        }

        public static synchronized Object getObject(String name) {
            ObjectFactory objectFactory = getObjectFactory();
            return objectFactory.createObject(name);
        }

        public static String getString(String name) {
            return (String) getObject(name);
        }

        public static long getLong(String name) {
            return ((Long) getObject(name)).longValue();
        }

        public static int getInt(String name) {
            return ((Integer) getObject(name)).intValue();
        }

        public static boolean getBoolean(String name) {
            return ((Boolean) getObject(name)).booleanValue();
        }
    }

    public void createSchema() {
        createSchema(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME);
    }

    public void createSchema(String jbpmContextName) {
        JbpmContext jbpmContext = createJbpmContext(jbpmContextName);
        try {
            Services services = jbpmContext.getServices();
            DbPersistenceServiceFactory persistenceServiceFactory = (DbPersistenceServiceFactory) services
                    .getServiceFactory(Services.SERVICENAME_PERSISTENCE);
            persistenceServiceFactory.createSchema();
        } finally {
            jbpmContext.close();
        }
    }

    public void dropSchema() {
        dropSchema(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME);
    }

    public void dropSchema(String jbpmContextName) {
        JbpmContext jbpmContext = createJbpmContext(jbpmContextName);
        try {
            Services services = jbpmContext.getServices();
            DbPersistenceServiceFactory persistenceServiceFactory = (DbPersistenceServiceFactory) services
                    .getServiceFactory(Services.SERVICENAME_PERSISTENCE);
            persistenceServiceFactory.dropSchema();
        } finally {
            jbpmContext.close();
        }
    }

    public void close() {
        close(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME);
    }

    public void close(String jbpmContextName) {
        JbpmContext jbpmContext = createJbpmContext(jbpmContextName);
        try {

            synchronized (instances) {
                Iterator iter = instances.values().iterator();
                while (iter.hasNext()) {
                    if (this == iter.next()) {
                        iter.remove();
                        break;
                    }
                }
            }

            if (jobExecutor != null) {
                jobExecutor.stop();
            }

            Map serviceFactories = jbpmContext.getServices().getServiceFactories();
            if (serviceFactories != null) {
                Iterator iter = serviceFactories.values().iterator();
                while (iter.hasNext()) {
                    ServiceFactory serviceFactory = (ServiceFactory) iter.next();
                    serviceFactory.close();
                }
            }
        } finally {
            jbpmContext.close();
        }
    }

    static JbpmConfiguration getCurrentJbpmConfiguration() {
        JbpmConfiguration currentJbpmConfiguration = null;
        Stack stack = getJbpmConfigurationStack();
        if (!stack.isEmpty()) {
            currentJbpmConfiguration = (JbpmConfiguration) stack.peek();
        }
        return currentJbpmConfiguration;
    }

    static synchronized Stack getJbpmConfigurationStack() {
        Stack stack = (Stack) jbpmConfigurationsStacks.get();
        if (stack == null) {
            stack = new Stack();
            jbpmConfigurationsStacks.set(stack);
        }
        return stack;
    }

    synchronized void pushJbpmConfiguration() {
        getJbpmConfigurationStack().push(this);
    }

    synchronized void popJbpmConfiguration() {
        getJbpmConfigurationStack().remove(this);
    }

    public JbpmContext getCurrentJbpmContext() {
        JbpmContext currentJbpmContext = null;
        Stack stack = getJbpmContextStack();
        if (!stack.isEmpty()) {
            currentJbpmContext = (JbpmContext) stack.peek();
        }
        return currentJbpmContext;
    }

    Stack getJbpmContextStack() {
        Stack stack = (Stack) jbpmContextStacks.get();
        if (stack == null) {
            stack = new Stack();
            jbpmContextStacks.set(stack);
        }
        return stack;
    }

    void pushJbpmContext(JbpmContext jbpmContext) {
        getJbpmContextStack().push(jbpmContext);
    }

    void popJbpmContext(JbpmContext jbpmContext) {
        Stack stack = getJbpmContextStack();
        if (stack.isEmpty()) {
            throw new JbpmException(
                    "closed JbpmContext more then once... check your try-finally's around JbpmContexts blocks");
        }
        JbpmContext popped = (JbpmContext) stack.pop();
        if (jbpmContext != popped) {
            throw new JbpmException(
                    "closed JbpmContext in different order then they were created... check your try-finally's around JbpmContexts blocks");
        }
    }

    void jbpmContextCreated(JbpmContext jbpmContext) {
        pushJbpmConfiguration();
        pushJbpmContext(jbpmContext);
    }

    void jbpmContextClosed(JbpmContext jbpmContext) {
        popJbpmConfiguration();
        popJbpmContext(jbpmContext);
    }

    public void startJobExecutor() {
        getJobExecutor().start();
    }

    public synchronized JobExecutor getJobExecutor() {
        if (jobExecutor == null) {
            try {
                jobExecutor = (JobExecutor) this.objectFactory.createObject("jbpm.job.executor");
            } catch (ClassCastException e) {
                throw new JbpmException("jbpm configuration object under key 'jbpm.job.executor' is not a "
                        + JobExecutor.class.getName(), e);
            }
        }
        return jobExecutor;
    }

    private static Log log = LogFactory.getLog(JbpmConfiguration.class);
}