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

org.jbpm.JbpmConfiguration Maven / Gradle / Ivy

The newest version!
/*
 * 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.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

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.ValueInfo;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.instantiation.ProcessClassLoaderFactory;
import org.jbpm.job.executor.JobExecutor;
import org.jbpm.persistence.db.DbPersistenceServiceFactory;
import org.jbpm.svc.ServiceFactory;
import org.jbpm.svc.Services;
import org.jbpm.util.ClassLoaderUtil;

/**
 * configuration of one jBPM instance.
 * 

* During process execution, jBPM might need to use some services. A JbpmConfiguration contains * the knowledge on how to create those services. *

*

* 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. *

*

* A JbpmConfiguration can be obtained in following ways: *

*
    *
  • from a resource (by default jbpm.cfg.xml is used): * *
     * JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
     * 
    * * or * *
     * String myXmlResource = "...";
     * JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance(myXmlResource);
     * 
    * *
  • *
  • from an XML string: * *
     * JbpmConfiguration jbpmConfiguration = JbpmConfiguration.parseXmlString(
     *   "<jbpm-configuration>" +
     *   ...
     *   "</jbpm-configuration>");
     * 
    * *
  • *
  • 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. * *
     * ObjectFactory of = new <i>MyCustomObjectFactory</i>();
     * JbpmConfiguration.Configs.setDefaultObjectFactory(of);
     * JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
     * 
    * *
  • *
*

* JbpmConfigurations can be configured using a spring-like XML notation (in relax ng compact * notation): *

* *
 * 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 {}
 * 
* *

*/ public class JbpmConfiguration implements Serializable { private static final long serialVersionUID = 1L; private static final String DEFAULT_RESOURCE = "jbpm.cfg.xml"; static final String OBJECT_NAME = "jbpm.configuration"; private static ObjectFactory defaultObjectFactory; private static final Map instances = new HashMap<>(); private static final ThreadLocal> threadLocalConfigurationStack = new ThreadLocal<>(); private final ObjectFactory objectFactory; private final String resourceName; private transient final ThreadLocal> threadLocalContextStack = new ThreadLocal<>(); private JobExecutor jobExecutor; private volatile boolean isClosed; public JbpmConfiguration(ObjectFactory objectFactory) { this(objectFactory, null); } private JbpmConfiguration(ObjectFactory objectFactory, String resourceName) { if (objectFactory == null) throw new IllegalArgumentException("object factory is null"); this.objectFactory = objectFactory; this.resourceName = resourceName; } ObjectFactory getObjectFactory() { return objectFactory; } String getResourceName() { return resourceName; } public static void setDefaultObjectFactory(ObjectFactory objectFactory) { defaultObjectFactory = objectFactory; } public static JbpmConfiguration getInstance() { return getInstance(null); } public static JbpmConfiguration getInstance(String resource) { if (resource == null) { resource = DEFAULT_RESOURCE; } JbpmConfiguration instance; synchronized (instances) { // look for configuration in cache instance = (JbpmConfiguration) instances.get(resource); if (instance == null) { // configuration does not exist or was evicted, construct it if (defaultObjectFactory != null) { log.info("configuring from default object factory"); instance = new JbpmConfiguration(defaultObjectFactory); } else { log.info("configuring from resource: " + resource); InputStream jbpmCfgXmlStream = ClassLoaderUtil.getStream(resource, false); /* * if a custom resource is specified, but not found in the classpath, log a warning; * otherwise, users who want to load custom stuff will not receive any feedback when * their resource cannot be found */ if (jbpmCfgXmlStream == null && !DEFAULT_RESOURCE.equals(resource)) { log.warn("configuration resource not found: " + resource); } ObjectFactory objectFactory = parseObjectFactory(jbpmCfgXmlStream); instance = createJbpmConfiguration(objectFactory, resource); } // put configuration in cache instances.put(resource, instance); } } return instance; } public static boolean hasInstance(String resource) { return instances.containsKey(resource != null ? resource : DEFAULT_RESOURCE); } protected static ObjectFactory parseObjectFactory(InputStream inputStream) { ObjectFactoryParser objectFactoryParser = new ObjectFactoryParser(); ObjectFactoryImpl objectFactory = new ObjectFactoryImpl(); objectFactoryParser.parseElementsFromResource("org/jbpm/default.jbpm.cfg.xml", objectFactory); if (inputStream != null) { objectFactoryParser.parseElementsStream(inputStream, objectFactory); } return objectFactory; } private static ObjectFactory loadDefaultObjectFactory() { log.info("loading default configuration"); return ObjectFactoryParser.parseResource("org/jbpm/default.jbpm.cfg.xml"); } public static JbpmConfiguration parseXmlString(String xml) { ObjectFactory objectFactory; if (xml != null) { log.info("configuring from xml string"); InputStream inputStream = new ByteArrayInputStream(xml.getBytes()); objectFactory = parseObjectFactory(inputStream); } else { objectFactory = loadDefaultObjectFactory(); } return createJbpmConfiguration(objectFactory); } protected static JbpmConfiguration createJbpmConfiguration(ObjectFactory objectFactory) { return createJbpmConfiguration(objectFactory, null); } private static JbpmConfiguration createJbpmConfiguration(ObjectFactory objectFactory, String resourceName) { JbpmConfiguration jbpmConfiguration = new JbpmConfiguration(objectFactory, resourceName); // make the configuration available to other objects if (objectFactory instanceof ObjectFactoryImpl) { ObjectFactoryImpl objectFactoryImpl = (ObjectFactoryImpl) objectFactory; objectFactoryImpl.addObjectInfo(new ValueInfo("jbpmConfiguration", jbpmConfiguration)); objectFactoryImpl.addObjectInfo(new ValueInfo(OBJECT_NAME, jbpmConfiguration)); } return jbpmConfiguration; } public static JbpmConfiguration parseInputStream(InputStream inputStream) { ObjectFactory objectFactory; if (inputStream != null) { log.info("configuring from " + inputStream); objectFactory = parseObjectFactory(inputStream); } else { objectFactory = loadDefaultObjectFactory(); } return createJbpmConfiguration(objectFactory); } public static JbpmConfiguration parseResource(String resource) { ObjectFactory objectFactory; if (resource != null) { log.info("configuring from resource: " + resource); InputStream inputStream = ClassLoaderUtil.getStream(resource, false); if (inputStream == null) { throw new IllegalArgumentException("resource not found: " + resource); } objectFactory = parseObjectFactory(inputStream); try { inputStream.close(); } catch (IOException e) { log.warn("failed to close resource: " + resource, e); } } else { objectFactory = loadDefaultObjectFactory(); } return createJbpmConfiguration(objectFactory, resource); } public JbpmContext createJbpmContext() { return createJbpmContext(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME); } public JbpmContext createJbpmContext(String name) { ensureOpen(); JbpmContext jbpmContext = (JbpmContext) objectFactory.createObject(name); pushJbpmContext(jbpmContext); return jbpmContext; } public ServiceFactory getServiceFactory(String serviceName) { return getServiceFactory(serviceName, JbpmContext.DEFAULT_JBPM_CONTEXT_NAME); } public ServiceFactory getServiceFactory(String serviceName, String jbpmContextName) { JbpmContext jbpmContext = createJbpmContext(jbpmContextName); try { return jbpmContext.getServices().getServiceFactory(serviceName); } finally { jbpmContext.close(); } } private DbPersistenceServiceFactory getPersistenceServiceFactory(String jbpmContextName) { return (DbPersistenceServiceFactory) getServiceFactory(Services.SERVICENAME_PERSISTENCE, jbpmContextName); } public static ClassLoader getProcessClassLoader(ProcessDefinition processDefinition) { ProcessClassLoaderFactory factory = (ProcessClassLoaderFactory) Configs.getObject("process.class.loader.factory"); return factory.getProcessClassLoader(processDefinition); } /** * access to configuration information through the current {@link JbpmContext} */ public static class Configs { private Configs() { // hide default constructor to prevent instantiation } public static ObjectFactory getObjectFactory() { JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext(); return jbpmContext != null ? jbpmContext.getObjectFactory() : getInstance().getObjectFactory(); } /** * @deprecated call {@link JbpmConfiguration#setDefaultObjectFactory(ObjectFactory)} instead */ public static void setDefaultObjectFactory(ObjectFactory objectFactory) { JbpmConfiguration.setDefaultObjectFactory(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 cleanSchema() { cleanSchema(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME); } public void cleanSchema(String jbpmContextName) { getPersistenceServiceFactory(jbpmContextName).cleanSchema(); } public void createSchema() { createSchema(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME); } public void createSchema(String jbpmContextName) { getPersistenceServiceFactory(jbpmContextName).createSchema(); } public void dropSchema() { dropSchema(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME); } public void dropSchema(String jbpmContextName) { getPersistenceServiceFactory(jbpmContextName).dropSchema(); } private void ensureOpen() { if (isClosed) throw new JbpmException(this + " is closed"); } public boolean isClosed() { return isClosed; } public void close() { close(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME); } public void close(String jbpmContextName) { // prevent configuration from being closed more than once if (isClosed) return; synchronized (this) { // stop job executor if (jobExecutor != null) { try { jobExecutor.stopAndJoin(); } catch (InterruptedException e) { // reassert interruption and continue Thread.currentThread().interrupt(); } jobExecutor = null; } // close remaining contexts List contextStack = threadLocalContextStack.get(); if (contextStack != null && !contextStack.isEmpty()) { log.warn("closing " + contextStack.size() + " open contexts;" + " make sure to close JbpmContext after use"); // copy to array because JbpmContext.close() pops the context off the stack JbpmContext[] jbpmContexts = (JbpmContext[]) contextStack.toArray(new JbpmContext[contextStack.size()]); for (int i = 0; i < jbpmContexts.length; i++) { jbpmContexts[i].close(); } } // close service factories JbpmContext jbpmContext = createJbpmContext(jbpmContextName); try { Map serviceFactories = jbpmContext.getServices().getServiceFactories(); if (serviceFactories != null) { for (Iterator i = serviceFactories.values().iterator(); i.hasNext();) { Object sf = i.next(); if (sf instanceof ServiceFactory) { ServiceFactory serviceFactory = (ServiceFactory) sf; serviceFactory.close(); } } } } finally { jbpmContext.close(); } // release thread-local context stack threadLocalContextStack.set(null); } // closing service factories requires open configuration isClosed = true; // remove from configuration cache if (resourceName != null) { synchronized (instances) { instances.remove(resourceName); } } } static JbpmConfiguration getCurrentJbpmConfiguration() { List stack = (List) threadLocalConfigurationStack.get(); return (stack == null || stack.isEmpty()) ? null : (JbpmConfiguration) stack.get(stack.size() - 1); } static void clearInstances() { synchronized (instances) { instances.clear(); } } public JbpmContext getCurrentJbpmContext() { ensureOpen(); List stack = (List) threadLocalContextStack.get(); return (stack == null || stack.isEmpty()) ? null : (JbpmContext) stack.get(stack.size() - 1); } void pushJbpmContext(JbpmContext jbpmContext) { // first push the configuration List configStack = threadLocalConfigurationStack.get(); if (configStack == null) { configStack = new ArrayList<>(); threadLocalConfigurationStack.set(configStack); } configStack.add(this); // then push the context List contextStack = threadLocalContextStack.get(); if (contextStack == null) { contextStack = new ArrayList<>(); threadLocalContextStack.set(contextStack); } contextStack.add(jbpmContext); } private static void remove(ThreadLocal threadLocal) { threadLocal.remove(); } void popJbpmContext(JbpmContext jbpmContext) { boolean threadSafetyFlag = false; boolean creationOrderFlag = false; // first pop the context List contextStack = (List) threadLocalContextStack.get(); int contextIndex; if (contextStack == null || (contextIndex = contextStack.lastIndexOf(jbpmContext)) == -1) { threadSafetyFlag = true; } else { if (contextIndex != contextStack.size() - 1) { creationOrderFlag = true; } // prevent context from remaining in the stack, no matter what contextStack.remove(contextIndex); // if context stack gets empty, remove thread-local variable if (contextStack.isEmpty()) remove(threadLocalContextStack); } // then pop the configuration List configStack = (List) threadLocalConfigurationStack.get(); int configIndex; if (configStack == null || (configIndex = configStack.lastIndexOf(this)) == -1) { threadSafetyFlag = true; } else { if (configIndex != configStack.size() - 1) { creationOrderFlag = true; } // prevent configuration from remaining in the stack, no matter what configStack.remove(configIndex); // if configuration stack gets empty, remove thread-local variable if (configStack.isEmpty()) remove(threadLocalConfigurationStack); } if (threadSafetyFlag) { log.warn(jbpmContext + " was not closed in the thread that created it;" + " JbpmContext is not safe for access from multiple threads!"); } else if (creationOrderFlag) { log.warn(jbpmContext + " was not closed in a block-structured manner;" + " check try-finally clauses around JbpmContext blocks"); } } public void startJobExecutor() { getJobExecutor().start(); } public JobExecutor getJobExecutor() { ensureOpen(); synchronized (this) { if (jobExecutor == null) { jobExecutor = (JobExecutor) objectFactory.createObject("jbpm.job.executor"); } } return jobExecutor; } public String toString() { return "JbpmConfiguration" + (resourceName != null ? '(' + resourceName + ')' : '@' + Integer.toHexString(hashCode())); } private static final Log log = LogFactory.getLog(JbpmConfiguration.class); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy