ludc
2025-01-16 986aa62ed00bee39363bab41b4eeb8259d446efd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
/*
 * 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.
 * 
 * <h3>Introduction</h3>
 * 
 * <p>Objects have different lifecycles and different context's (aka scopes).  An 
 * environment provides the structure to easily manage objects with different 
 * contexts.
 * </p>
 * 
 * <p>Examples of contexts are: 
 * <ul>
 *   <li><b>process-engine</b>: The process-engine context is used to store e.g. data sources,
 *     session factories and other static resources needed by an application.
 *     The process-engine context lives for the complete duration of the {@link EnvironmentFactory}.  
 *     So if the {@link EnvironmentFactory} is maintained in a static member field, the 
 *     process-engine context lives for the duration of the application.
 *     The same process-engine context is shared for all the Environments produced by one 
 *     EnvironmentFactory.</li>
 *   <li><b>environment</b>: The environment context is used for e.g. a transaction 
 *     and transactional resources, user authentication.  This results in an efficient and 
 *     configurable use of transactional resources that need to be lazily initialized.</li>
 *   <li>The environment can accomodate other contexts as well.  They can be added 
 *     and removed dynamically.  Examples of other potential contexts are web-request, web-session, 
 *     web-application, business processDefinition, jobImpl, ... </li>
 * </ul>    
 *      
 * <center><img src="environment.gif"/></center>
 * 
 * <p>An environment is typically installed like this
 * </p>
 * 
 * <b><pre>static EnvironmentFactory environmentFactory = new DefaultEnvironmentFactory();
 * 
 * ...
 * 
 * EnvironmentImpl environment = environmentFactory.openEnvironment();
 * try {
 * 
 *   ... everything available in this block ... 
 * 
 * } finally {
 *   environment.close();
 * }
 * </pre></b>
 *
 * <h3>Purpose</h3>
 * 
 * <p>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. 
 * </p>
 * 
 * <p>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.
 * </p>
 * 
 * <h3>Search order</h3>
 * 
 * <p>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.
 * </p>
 * 
 * <p>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. 
 * </p>
 *
 * <h3>Transaction, username and classloader</h3>
 * 
 * <p>Three objects are used so frequently in an environment that they get 
 * special treatment:
 * </p>
 * 
 * <ul>
 *   <li><b>Transaction</b>: an abstraction for marking a transaction with 
 *   setRollbackOnly.</li>
 *   <li><b>Classloader</b>: the current class loader.</li>
 *   <li><b>Username</b>: the name of the currently authenticated user.</li>
 * </ul>
 * 
 * <p>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. 
 * </p>
 * 
 * 
 * @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, <code>null</code> 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, <code>null</code> 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> T get(Class<T> 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> T get(Class<T> 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<EnvironmentImpl> currentEnvironment = new ThreadLocal<EnvironmentImpl>();
 
  /** in case of nested environments, the current environment stack maintains the outer environments */
  static ThreadLocal<Stack<EnvironmentImpl>> currentEnvironmentStack = new ThreadLocal<Stack<EnvironmentImpl>>();
 
  /** gets the most inner open environment. */
  public static EnvironmentImpl getCurrent() {
    return currentEnvironment.get();
  }
  
  public static <T> T getFromCurrent(Class<T> type) {
    return getFromCurrent(type, true);
  }
 
  public static <T> T getFromCurrent(Class<T> 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<EnvironmentImpl> getStack() {
    // lazy initialize the current environment stack
    Stack<EnvironmentImpl> stack = currentEnvironmentStack.get();
    if (stack==null) {
      stack = new Stack<EnvironmentImpl>();
      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<EnvironmentImpl> 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);
  }
}