X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.slc.core%2Fsrc%2Forg%2Fargeo%2Fslc%2Fcore%2Fexecution%2FExecutionScope.java;fp=org.argeo.slc.core%2Fsrc%2Forg%2Fargeo%2Fslc%2Fcore%2Fexecution%2FExecutionScope.java;h=a2a5a9b0ca7f81ec3f0a949c7ad10469e8768210;hb=a9b97cc33383ded70277f49aa287f84903334e70;hp=0000000000000000000000000000000000000000;hpb=d1298659fe6f179d1cbbc8c89f108a0bbc5b4edf;p=gpl%2Fargeo-slc.git diff --git a/org.argeo.slc.core/src/org/argeo/slc/core/execution/ExecutionScope.java b/org.argeo.slc.core/src/org/argeo/slc/core/execution/ExecutionScope.java new file mode 100644 index 000000000..a2a5a9b0c --- /dev/null +++ b/org.argeo.slc.core/src/org/argeo/slc/core/execution/ExecutionScope.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.slc.core.execution; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.SlcException; +import org.argeo.slc.UnsupportedException; +import org.argeo.slc.execution.ExecutionContext; +import org.argeo.slc.execution.ExecutionFlow; +import org.argeo.slc.execution.ExecutionSpec; +import org.argeo.slc.execution.ExecutionStack; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.config.Scope; + +/** + * When Spring beans are instantiated with this scope, the same instance is + * reused across an execution. + */ +public class ExecutionScope implements Scope { + private final static Log log = LogFactory.getLog(ExecutionScope.class); + + private final ThreadLocal executionStack = new ThreadLocal(); + public final ThreadLocal executionStackBeanName = new ThreadLocal(); + + private final ThreadLocal executionContext = new ThreadLocal(); + private final ThreadLocal executionContextBeanName = new ThreadLocal(); + + public Object get(String name, ObjectFactory objectFactory) { + if (log.isTraceEnabled()) + log.debug("Get execution scoped bean " + name); + + // shortcuts + if (executionStackBeanName.get() != null + && name.equals(executionStackBeanName.get())) { + return executionStack.get(); + } + + if (executionContextBeanName.get() != null + && name.equals(executionContextBeanName.get())) { + return executionContext.get(); + } + + // execution context must be defined first + if (executionContext.get() == null) { + Object obj = objectFactory.getObject(); + if (obj instanceof ExecutionContext) { + return dealWithSpecialScopedObject(name, executionContext, + executionContextBeanName, (ExecutionContext) obj); + } else { + // TODO: use execution context wrapper + throw new SlcException("No execution context has been defined."); + } + } + + // for other scoped objects, an executions stack must be available + if (executionStack.get() == null) { + Object obj = objectFactory.getObject(); + if (obj instanceof ExecutionStack) { + return dealWithSpecialScopedObject(name, executionStack, + executionStackBeanName, (ExecutionStack) obj); + } else { + throw new SlcException("No execution stack has been defined."); + } + } + + // see if the execution stack already knows the object + Object obj = executionStack.get().findScopedObject(name); + if (obj == null) { + obj = objectFactory.getObject(); + if (obj instanceof ExecutionContext) + throw new SlcException( + "Only one execution context can be defined per thread"); + if (obj instanceof ExecutionStack) + throw new SlcException( + "Only one execution stack can be defined per thread"); + + checkForbiddenClasses(obj); + + executionStack.get().addScopedObject(name, obj); + } + return obj; + + } + + protected T dealWithSpecialScopedObject(String name, + ThreadLocal threadLocal, + ThreadLocal threadLocalBeanName, T newObj) { + + T obj = threadLocal.get(); + if (obj == null) { + obj = newObj; + threadLocal.set(obj); + threadLocalBeanName.set(name); + if (log.isTraceEnabled()) { + log.debug(obj.getClass() + " instantiated. (beanName=" + name + + ")"); + } + return obj; + } else { + throw new SlcException("Only one scoped " + obj.getClass() + + " can be defined per thread"); + } + + } + + protected void checkForbiddenClasses(Object obj) { + Class clss = obj.getClass(); + if (ExecutionFlow.class.isAssignableFrom(clss) + || ExecutionSpec.class.isAssignableFrom(clss)) { + throw new UnsupportedException("Execution scoped object", clss); + } + } + + public String getConversationId() { + // TODO: is it the most relevant? + return executionContext.get().getUuid(); + } + + public void registerDestructionCallback(String name, Runnable callback) { + if (Thread.currentThread() instanceof ExecutionThread) { + ExecutionThread executionThread = (ExecutionThread) Thread + .currentThread(); + executionThread.registerDestructionCallback(name, callback); + } + } + + public Object remove(String name) { + if (log.isDebugEnabled()) + log.debug("Remove object " + name); + throw new UnsupportedOperationException(); + } + + public Object resolveContextualObject(String key) { + return executionContext.get().getVariable(key); + } + +}