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

Reply via email to