[
https://issues.apache.org/jira/browse/WW-4874?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16224273#comment-16224273
]
zhouyanming commented on WW-4874:
---------------------------------
Share my implementation
{code:java}
import java.util.Map;
import org.apache.struts2.impl.StrutsActionProxyFactory;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionProxy;
public class CallableActionProxyFactory extends StrutsActionProxyFactory {
@Override
public ActionProxy createActionProxy(String namespace, String
actionName, String methodName,
Map<String, Object> extraContext, boolean
executeResult, boolean cleanupContext) {
ActionInvocation inv = new
CallableActionInvocation(extraContext, true);
container.inject(inv);
return createActionProxy(inv, namespace, actionName,
methodName, executeResult, cleanupContext);
}
}
{code}
{code:java}
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.DefaultActionInvocation;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig;
public class CallableActionInvocation extends DefaultActionInvocation {
private static final long serialVersionUID = -4310552665942898360L;
private static Logger logger =
LoggerFactory.getLogger(CallableActionInvocation.class);
private static ExecutorService executorService;
protected Callable<String> callableResult;
public CallableActionInvocation(Map<String, Object> extraContext,
boolean pushAction) {
super(extraContext, pushAction);
}
@Override
public Result createResult() throws Exception {
if (callableResult != null) {
Callable<String> callable = callableResult;
ActionContext context = ActionContext.getContext();
SecurityContext sc = SecurityContextHolder.getContext();
HttpServletRequest request =
ServletActionContext.getRequest();
HttpServletResponse response =
ServletActionContext.getResponse();
if (executorService == null) {
try {
executorService =
WebApplicationContextUtils.getWebApplicationContext(request.getServletContext())
.getBean("executorService", ExecutorService.class);
} catch (NoSuchBeanDefinitionException e) {
logger.warn("No bean[executorService]
defined, use ForkJoinPool.commonPool() as fallback");
executorService =
ForkJoinPool.commonPool();
}
}
AsyncContext asyncContext = request.startAsync();
@SuppressWarnings("serial")
Result result = new Result() {
@Override
public void execute(ActionInvocation
actionInvocation) throws Exception {
executorService.submit(() -> {
try {
SecurityContextHolder.setContext(sc);
ServletActionContext.setContext(context);
String result =
callable.call();
ActionConfig config =
proxy.getConfig();
Map<String,
ResultConfig> results = config.getResults();
ResultConfig
resultConfig = results.get(result);
if (resultConfig ==
null) {
resultConfig =
results.get("*");
}
Result re = null;
if (resultConfig !=
null) {
try {
re =
objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
} catch
(Exception e) {
throw
new XWorkException(e, resultConfig);
}
} else if (result !=
null && !Action.NONE.equals(result)
&&
unknownHandlerManager.hasUnknownHandlers()) {
re =
unknownHandlerManager.handleUnknownResult(invocationContext,
proxy.getActionName(),
proxy.getConfig(), result);
}
((CallableActionInvocation) actionInvocation).reset();
actionInvocation.setResultCode(result);
re.execute(actionInvocation);
} catch (Exception e) {
logger.error(e.getMessage(), e);
try {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
e.getMessage());
} catch (IOException
ex) {
logger.error(ex.getMessage(), ex);
}
} finally {
SecurityContextHolder.clearContext();
ServletActionContext.setContext(null);
asyncContext.complete();
}
});
}
};
callableResult = null;
return result;
}
return super.createResult();
}
@SuppressWarnings("unchecked")
@Override
protected String saveResult(ActionConfig actionConfig, Object
methodResult) {
if ((methodResult instanceof Callable)) {
callableResult = ((Callable<String>) methodResult);
return null;
}
return super.saveResult(actionConfig, methodResult);
}
protected void reset() {
executed = false;
}
}
{code}
> Asynchronous action method
> --------------------------
>
> Key: WW-4874
> URL: https://issues.apache.org/jira/browse/WW-4874
> Project: Struts 2
> Issue Type: New Feature
> Components: Core Actions, Dispatch Filter
> Reporter: Yasser Zamani
> Labels: action, asynchronous
> Fix For: 2.5.14
>
> Original Estimate: 1,344h
> Remaining Estimate: 1,344h
>
> User will be able to return {{java.util.concurrent.Callable<String>}} in
> their actions. Struts when sees such result, runs {{resultCode =
> result.call();}} in it's own managed thread pool but exits from servlet's
> main thread with a null result, i.e. gives back main thread to container and
> leaves response open for concurrent processing. When {{resultCode =
> result.call();}} returned, Struts calls
> {{javax.servlet.AsyncContext.dispatch()}} and {{resumes request processing}}
> within a container's thread servlet to generate the appropriate result for
> user according to {{resultCode}}.
> This adds better support for SLS (Short request processing, Long action
> execution, Short response processing) via Servlet 3's Async API.
> Support of other cases like SSL (e.g. a download server) or LLL(e.g. a video
> converter server) is still open.
--
This message was sent by Atlassian JIRA
(v6.4.14#64029)