in core/src/main/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptor.java [233:328]
protected String doIntercept(ActionInvocation actionInvocation) throws Exception {
ActionProxy proxy = actionInvocation.getProxy();
String name = getBackgroundProcessName(proxy);
ActionContext context = actionInvocation.getInvocationContext();
Map<String, Object> session = context.getSession();
HttpSession httpSession = ServletActionContext.getRequest().getSession(true);
//sync on the real HttpSession as the session from the context is a wrap that is created
//on every request
synchronized (httpSession) {
// State flag processing moved within the synchronization block, to ensure consistency.
Boolean secondTime = true;
if (executeAfterValidationPass) {
secondTime = (Boolean) context.get(KEY);
if (secondTime == null) {
context.put(KEY, true);
secondTime = false;
} else {
secondTime = true;
context.put(KEY, null);
}
}
final String bp_SessionKey = KEY + name;
BackgroundProcess bp = (BackgroundProcess) session.get(bp_SessionKey);
LOG.debug("Intercepting invocation for BackgroundProcess - session key: {}, value: {}", bp_SessionKey, bp);
//WW-4900 Checks if from a de-serialized session? so background thread missed, let's start a new one.
if (bp != null && bp.getInvocation() == null) {
LOG.trace("BackgroundProcess invocation is null (remove key, clear instance)");
session.remove(bp_SessionKey);
bp = null;
}
if ((!executeAfterValidationPass || secondTime) && bp == null) {
LOG.trace("BackgroundProcess instance is null (create new instance) - executeAfterValidationPass: {}, secondTime: {}.", executeAfterValidationPass, secondTime);
bp = getNewBackgroundProcess(name, actionInvocation, threadPriority).prepare();
session.put(bp_SessionKey, bp);
if (executor == null || executor.isShutdown()) {
LOG.warn("Executor is shutting down (or null), cannot execute a new process, invoke next ActionInvocation step and return.");
return actionInvocation.invoke();
}
executor.execute(bp);
performInitialDelay(bp); // first time let some time pass before showing wait page
secondTime = false;
}
if ((!executeAfterValidationPass || !secondTime) && bp != null && !bp.isDone()) {
LOG.trace("BackgroundProcess instance is not done (wait processing) - executeAfterValidationPass: {}, secondTime: {}.", executeAfterValidationPass, secondTime);
actionInvocation.getStack().push(bp.getAction());
final String token = TokenHelper.getToken();
if (token != null) {
TokenHelper.setSessionToken(TokenHelper.getTokenName(), token);
}
Map<String, ResultConfig> results = proxy.getConfig().getResults();
if (!results.containsKey(WAIT)) {
LOG.warn("ExecuteAndWait interceptor has detected that no result named 'wait' is available. " +
"Defaulting to a plain built-in wait page. It is highly recommend you " +
"provide an action-specific or global result named '{}'.", WAIT);
// no wait result? hmm -- let's try to do dynamically put it in for you!
//we used to add a fake "wait" result here, since the configuration is unmodifiable, that is no longer
//an option, see WW-3068
FreemarkerResult waitResult = new FreemarkerResult();
container.inject(waitResult);
waitResult.setLocation("/org/apache/struts2/interceptor/wait.ftl");
waitResult.execute(actionInvocation);
return Action.NONE;
}
return WAIT;
} else if ((!executeAfterValidationPass || !secondTime) && bp != null && bp.isDone()) {
LOG.trace("BackgroundProcess instance is done (remove key, return result) - executeAfterValidationPass: {}, secondTime: {}.", executeAfterValidationPass, secondTime);
session.remove(bp_SessionKey);
actionInvocation.getStack().push(bp.getAction());
// if an exception occurred during action execution, throw it here
if (bp.getException() != null) {
throw bp.getException();
}
return bp.getResult();
} else {
LOG.trace("BackgroundProcess state fall-through (first instance, pass through), invoke next ActionInvocation step and return - executeAfterValidationPass: {}, secondTime: {}.", executeAfterValidationPass, secondTime);
// this is the first instance of the interceptor and there is no existing action
// already run in the background, so let's just let this pass through. We assume
// the action invocation will be run in the background on the subsequent pass through
// this interceptor
return actionInvocation.invoke();
}
}
}