/* Copyright 2005-2006 Tim Fennell
*
* 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 net.sourceforge.stripes.controller;
import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.util.Log;
import java.util.Collection;
import java.util.Iterator;
import java.lang.reflect.Method;
/**
* <p>Holds the execution context for processing a single request. The ExecutionContext is made
* available to {@link Interceptor} classes that are interleaved with the regular request
* processing lifecycle.</p>
*
* <p>The ExecutionContext is not populated all at once, but in pieces as the request progresses.
* Check the accessor method for each item for information on when that item becomes available
* in the request processing lifecycle.</p>
*
* @author Tim Fennell
* @since Stripes 1.3
*/
public class ExecutionContext {
private static final Log log = Log.getInstance(ExecutionContext.class);
private static final ThreadLocal<ExecutionContext> currentContext = new ThreadLocal<ExecutionContext>();
/** Get the execution context for the current thread. */
public static final ExecutionContext currentContext() {
return currentContext.get();
}
private Collection<Interceptor> interceptors;
private Iterator<Interceptor> iterator;
private Interceptor target;
private ActionBeanContext actionBeanContext;
private ActionBean actionBean;
private Method handler;
private Resolution resolution;
private LifecycleStage lifecycleStage;
private boolean resolutionFromHandler = false;
/**
* Used by the {@link DispatcherServlet} to initialize and/or swap out the list of
* {@link Interceptor} instances which should wrap the current {@link LifecycleStage}.
*
* @param stack a non-null (though possibly empty) ordered collection of interceptors
*/
public void setInterceptors(Collection<Interceptor> stack) {
this.interceptors = stack;
}
/**
* Used by the {@link DispatcherServlet} to wrap a block of lifecycle code in
* {@link Interceptor} calls.
*
* @param target a block of lifecycle/request processing code that is contained inside
* a class that implements Interceptor
* @return a Resolution instance or null depending on what is returned by the lifecycle
* code and any interceptors which intercept the execution
* @throws Exception if the lifecycle code or an interceptor throws an Exception
*/
public Resolution wrap(Interceptor target) throws Exception {
this.target = target;
this.iterator = null;
// Before executing RequestInit, set this as the current execution context
if (lifecycleStage == LifecycleStage.RequestInit)
currentContext.set(this);
try {
return proceed();
}
finally {
// Make sure the current execution context gets cleared after RequestComplete
if (LifecycleStage.RequestComplete == getLifecycleStage())
clearContextThreadLocal();
}
}
static void clearContextThreadLocal() {
currentContext.set(null);
}
/**
* Retrieves the ActionBeanContext associated with the current request. Available to all
* interceptors regardless of {@link LifecycleStage}.
*
* @return the current ActionBeanContext
*/
public ActionBeanContext getActionBeanContext() {
return actionBeanContext;
}
/** Sets the ActionBeanContext for the current request. */
public void setActionBeanContext(ActionBeanContext actionBeanContext) {
this.actionBeanContext = actionBeanContext;
}
/**
* Retrieves the ActionBean instance that is associated with the current request. Available
* to interceptors only after {@link LifecycleStage#ActionBeanResolution} has occurred.
*
* @return the current ActionBean instance, or null if not yet resolved
*/
public ActionBean getActionBean() { return actionBean; }
/** Sets the ActionBean associated with the current request. */
public void setActionBean(ActionBean actionBean) { this.actionBean = actionBean; }
/**
* Retrieves the handler Method that is targeted by the current request. Available
* to interceptors only after {@link LifecycleStage#HandlerResolution} has occurred.
*
* @return the current handler method, or null if not yet resolved
*/
public Method getHandler() { return handler; }
/** Sets the handler method that will be invoked to process the current request. */
public void setHandler(Method handler) { this.handler = handler; }
/**
* Gets the Resolution that will be executed at the end of the execution. This value
* is generally not populated until just prior to {@link LifecycleStage#ResolutionExecution}.
*
* @return the Resolution associated with this execution
*/
public Resolution getResolution() { return resolution; }
/** Sets the Resolution that will be executed to terminate this execution. */
public void setResolution(Resolution resolution) { this.resolution = resolution; }
/**
* Gets the current LifecycleStage being processed. This is always set to the appropriate
* lifecycle stage before invoking any interceptors or lifecycle code, so that interceptors
* that intercept at multiple lifecycle stages can be aware of which stage is being
* intercepted.
*
* @return the LifecycleStage currently being processed/intercepted
*/
public LifecycleStage getLifecycleStage() { return lifecycleStage; }
/** Sets the current stage in the request processing lifecycle. */
public void setLifecycleStage(LifecycleStage lifecycleStage) {
this.lifecycleStage = lifecycleStage;
}
/**
* Continues the flow of execution. If there are more interceptors in the stack intercepting
* the current lifecycle stage then the flow continues by calling the next interceptor. If
* there are no more interceptors then the lifecycle code is invoked.
*
* @return a Resolution if the lifecycle code or one of the interceptors returns one
* @throws Exception if the lifecycle code or one of the interceptors throws one
*/
public Resolution proceed() throws Exception {
if (this.iterator == null) {
log.debug("Transitioning to lifecycle stage ", lifecycleStage);
this.iterator = this.interceptors.iterator();
}
if (this.iterator.hasNext()) {
return this.iterator.next().intercept(this);
}
else {
return this.target.intercept(this);
}
}
public boolean isResolutionFromHandler() {
return resolutionFromHandler;
}
public void setResolutionFromHandler(boolean resolutionFromHandler) {
this.resolutionFromHandler = resolutionFromHandler;
}
}