]> git.argeo.org Git - gpl/argeo-slc.git/blob - org.argeo.slc.spring/src/org/argeo/slc/core/execution/ExecutionScope.java
Re-activate Spring runtime unit tests.
[gpl/argeo-slc.git] / org.argeo.slc.spring / src / org / argeo / slc / core / execution / ExecutionScope.java
1 /*
2 * Copyright (C) 2007-2012 Argeo GmbH
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.argeo.slc.core.execution;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20 import org.argeo.slc.SlcException;
21 import org.argeo.slc.UnsupportedException;
22 import org.argeo.slc.execution.ExecutionContext;
23 import org.argeo.slc.execution.ExecutionFlow;
24 import org.argeo.slc.execution.ExecutionSpec;
25 import org.argeo.slc.execution.ExecutionStack;
26 import org.argeo.slc.runtime.ExecutionThread;
27 import org.springframework.beans.factory.ObjectFactory;
28 import org.springframework.beans.factory.config.Scope;
29
30 /**
31 * When Spring beans are instantiated with this scope, the same instance is
32 * reused across an execution.
33 */
34 public class ExecutionScope implements Scope {
35 private final static Log log = LogFactory.getLog(ExecutionScope.class);
36
37 private final ThreadLocal<ExecutionStack> executionStack = new ThreadLocal<ExecutionStack>();
38 public final ThreadLocal<String> executionStackBeanName = new ThreadLocal<String>();
39
40 private final ThreadLocal<ExecutionContext> executionContext = new ThreadLocal<ExecutionContext>();
41 private final ThreadLocal<String> executionContextBeanName = new ThreadLocal<String>();
42
43 public Object get(String name, ObjectFactory<?> objectFactory) {
44 if (log.isTraceEnabled())
45 log.debug("Get execution scoped bean " + name);
46
47 // shortcuts
48 if (executionStackBeanName.get() != null
49 && name.equals(executionStackBeanName.get())) {
50 return executionStack.get();
51 }
52
53 if (executionContextBeanName.get() != null
54 && name.equals(executionContextBeanName.get())) {
55 return executionContext.get();
56 }
57
58 // execution context must be defined first
59 if (executionContext.get() == null) {
60 Object obj = objectFactory.getObject();
61 if (obj instanceof ExecutionContext) {
62 return dealWithSpecialScopedObject(name, executionContext,
63 executionContextBeanName, (ExecutionContext) obj);
64 } else {
65 // TODO: use execution context wrapper
66 throw new SlcException("No execution context has been defined.");
67 }
68 }
69
70 // for other scoped objects, an executions stack must be available
71 if (executionStack.get() == null) {
72 Object obj = objectFactory.getObject();
73 if (obj instanceof ExecutionStack) {
74 return dealWithSpecialScopedObject(name, executionStack,
75 executionStackBeanName, (ExecutionStack) obj);
76 } else {
77 throw new SlcException("No execution stack has been defined.");
78 }
79 }
80
81 // see if the execution stack already knows the object
82 Object obj = executionStack.get().findScopedObject(name);
83 if (obj == null) {
84 obj = objectFactory.getObject();
85 if (obj instanceof ExecutionContext)
86 throw new SlcException(
87 "Only one execution context can be defined per thread");
88 if (obj instanceof ExecutionStack)
89 throw new SlcException(
90 "Only one execution stack can be defined per thread");
91
92 checkForbiddenClasses(obj);
93
94 executionStack.get().addScopedObject(name, obj);
95 }
96 return obj;
97
98 }
99
100 protected <T> T dealWithSpecialScopedObject(String name,
101 ThreadLocal<T> threadLocal,
102 ThreadLocal<String> threadLocalBeanName, T newObj) {
103
104 T obj = threadLocal.get();
105 if (obj == null) {
106 obj = newObj;
107 threadLocal.set(obj);
108 threadLocalBeanName.set(name);
109 if (log.isTraceEnabled()) {
110 log.debug(obj.getClass() + " instantiated. (beanName=" + name
111 + ")");
112 }
113 return obj;
114 } else {
115 throw new SlcException("Only one scoped " + obj.getClass()
116 + " can be defined per thread");
117 }
118
119 }
120
121 protected void checkForbiddenClasses(Object obj) {
122 Class<?> clss = obj.getClass();
123 if (ExecutionFlow.class.isAssignableFrom(clss)
124 || ExecutionSpec.class.isAssignableFrom(clss)) {
125 throw new UnsupportedException("Execution scoped object", clss);
126 }
127 }
128
129 public String getConversationId() {
130 // TODO: is it the most relevant?
131 return executionContext.get().getUuid();
132 }
133
134 public void registerDestructionCallback(String name, Runnable callback) {
135 if (Thread.currentThread() instanceof ExecutionThread) {
136 ExecutionThread executionThread = (ExecutionThread) Thread
137 .currentThread();
138 executionThread.registerDestructionCallback(name, callback);
139 }
140 }
141
142 public Object remove(String name) {
143 if (log.isDebugEnabled())
144 log.debug("Remove object " + name);
145 throw new UnsupportedOperationException();
146 }
147
148 public Object resolveContextualObject(String key) {
149 return executionContext.get().getVariable(key);
150 }
151
152 }