/* * 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.pvm.internal.env; import java.io.Serializable; import java.util.Stack; import org.jbpm.api.JbpmException; import org.jbpm.api.cmd.Environment; /** * maintains contextual information for a thread in a set of * {@link Context}s. * *

Introduction

* *

Objects have different lifecycles and different context's (aka scopes). An * environment provides the structure to easily manage objects with different * contexts. *

* *

Examples of contexts are: *

* *
* *

An environment is typically installed like this *

* *
static EnvironmentFactory environmentFactory = new DefaultEnvironmentFactory();
 * 
 * ...
 * 
 * EnvironmentImpl environment = environmentFactory.openEnvironment();
 * try {
 * 
 *   ... everything available in this block ... 
 * 
 * } finally {
 *   environment.close();
 * }
 * 
* *

Purpose

* *

The first purpose of the environment is to separate the application from the * environment. Standard Java and Enterprise Java are quite different and an environment * abstraction like this allows for the development of applications that can run in * both Standard and Enterprise environments. Also test environments are easier to * tweak this way. *

* *

A second purpose of the environment is to enable specific to global searching * of resources. E.g. you could search for an 'adminEmailAddress' in the contexts * 'execution', 'transaction' and 'process-engine' in the given order. * That way, a global adminEmailAddress can be specified in the process-engine context * and it can be refined in more specific contexts. *

* *

Search order

* *

To find an object in the environment, a searchOrder can be specified. A * search order is an sequence that specifies the order in which the contexts should * be searched. *

* *

The default search order is the inverse sequence of how the contexts are * added to the environment. This is because in general, we can assume that the * more recent a context was added, the more specific it is. *

* *

Transaction, username and classloader

* *

Three objects are used so frequently in an environment that they get * special treatment: *

* * * *

For these special properties, setters are also available. That is to support * programmatic injection into the environment. Alternatively, they can be configured * in one of the contexts. *

* * * @see EnvironmentFactory * @author Tom Baeyens */ public abstract class EnvironmentImpl implements Serializable, Environment { /** * searches a named object in all the contexts in the default search order. * @return the object if it exists in the environment, null if there is no object with the given name in the environment. */ public abstract Object get(String name); /** * searches a named object in all the contexts in the given search order. The given * search order doesn't have to include all contexts. It can be a subset of the * contexts available. * @param searchOrder list of contexts names. The object will be searched in these contexts, in the given order. * @return the object if it exists in the environment, null if there is no object with the given name in the specified searchOrder contexts. */ public abstract Object get(String name, String[] searchOrder); /** searches an object based on type. The search doesn take superclasses of the context elements * into account. * @return the first object of the given type or null in case no such element was found. */ public abstract T get(Class type); /** searches an object based on type. The search doesn take superclasses of the context elements * into account. * @return the first object of the given type or null in case no such element was found. */ public abstract T get(Class type, String[] searchOrder); /** get the authenticated user id */ public abstract String getAuthenticatedUserId(); /** set the authenticated user id */ public abstract void setAuthenticatedUserId(String authenticatedUserId); /** * closes the EnvironmentImpl by removing all its contexts. */ public abstract void close(); public abstract Context getContext(String contextName); public abstract void setContext(Context context); public abstract Context removeContext(Context context); public abstract Context removeContext(String contextName); public abstract ClassLoader getClassLoader(); public abstract void setClassLoader(ClassLoader classLoader); // current environment ////////////////////////////////////////////////////// /** the current environment is maintained in the currentEnvironment thread local */ static ThreadLocal currentEnvironment = new ThreadLocal(); /** in case of nested environments, the current environment stack maintains the outer environments */ static ThreadLocal> currentEnvironmentStack = new ThreadLocal>(); /** gets the most inner open environment. */ public static EnvironmentImpl getCurrent() { return currentEnvironment.get(); } public static T getFromCurrent(Class type) { return getFromCurrent(type, true); } public static T getFromCurrent(Class type, boolean required) { EnvironmentImpl environment = getCurrent(); if (environment==null) { if (required) { throw new JbpmException("no environment to get "+type.getName()); } return null; } T object = environment.get(type); if (object==null) { if (required) { throw new JbpmException("no "+type.getName()+" in current environment"); } return null; } return object; } public static Object getFromCurrent(String name) { return getFromCurrent(name, true); } public static Object getFromCurrent(String name, boolean required) { EnvironmentImpl environment = getCurrent(); if (environment==null) { if (required) { throw new JbpmException("no environment to get '"+name+"'"); } return null; } Object object = environment.get(name); if (object==null) { if (required) { throw new JbpmException("no '"+name+"' in current environment"); } return null; } return object; } static Stack getStack() { // lazy initialize the current environment stack Stack stack = currentEnvironmentStack.get(); if (stack==null) { stack = new Stack(); currentEnvironmentStack.set(stack); } return stack; } /** pops the closing context from the stack of current contexts. This * is the first thing that needs to be done when an environment is closed. * @see EnvironmentFactory#push(EnvironmentImpl) */ public static synchronized EnvironmentImpl popEnvironment() { EnvironmentImpl popped = currentEnvironment.get(); currentEnvironment.set(null); Stack stack = currentEnvironmentStack.get(); if ( (stack!=null) && (! stack.isEmpty()) ) { currentEnvironment.set(stack.pop()); } return popped; } /** after opening of a new environment succeeded, the environment * must be pushed in the stack of current environments. * * @see EnvironmentImpl#pop() */ public static synchronized void pushEnvironment(EnvironmentImpl environment) { EnvironmentImpl current = currentEnvironment.get(); if (current!=null) { getStack().push(current); } currentEnvironment.set(environment); } }