struts-issues mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "zhouyanming (JIRA)" <j...@apache.org>
Subject [jira] [Commented] (WW-4874) Asynchronous action method
Date Mon, 30 Oct 2017 00:58:00 GMT

    [ 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)

Mime
View raw message