Struts从AsyncContext发送响应

时间:2017-10-18 10:52:55

标签: asynchronous struts2 long-polling servlet-3.0

我正在尝试在struts Web应用程序中进行长轮询。我在ActionSupport操作方法中启动AsyncContext,执行一些耗时的异步工作,然后将SUCCESS响应发送到struts。

我知道我可以做PrintWriter pw = asyncContext.getResponse().getWriter();并写一个原始响应,但我想以某种方式发出struts信号来继续struts.xml中的预定义结果。这可能吗?

<action name="myAction" method="action1" class="myActionClass">
    <result name="success" type="redirectAction">
        /pages/myPage.jsp        <!-- I want to run this from async --->
    </result>
</action>

在非异步操作中,我可以简单地返回SUCCESS并且struts会处理所有事情,但是我在使用异步操作实现类似效果方面遇到了麻烦。这就是我到目前为止所做的:

public void action1() {
    HttpServletRequest req = ServletActionContext.getRequest();
    HttpServletResponse res = ServletActionContext.getResponse();

    final AsyncContext asyncContext = req.startAsync(req, res);

    asyncContext.start(new Runnable() {
        public void run() {
            // Some time-consuming polling task is done here

            asyncContext.complete();

            // Can I somehow proceed to predefined struts result from here?
        }
    });
}

1 个答案:

答案 0 :(得分:0)

目前似乎无法做到清楚。我正在工作,如果我可以将此支持导入Struts但是现在,我有一个有效的黑客。我将StrutsExecuteFilter扩展如下:

package me.zamani.yasser.ww_convention.utils;

import org.apache.struts2.dispatcher.PrepareOperations;
import org.apache.struts2.dispatcher.filter.StrutsExecuteFilter;
import org.apache.struts2.dispatcher.filter.StrutsPrepareFilter;
import org.apache.struts2.dispatcher.mapper.ActionMapping;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * Created by user on 8/31/2017.
 */
public class MYStrutsAsyncExecuteFilter extends StrutsExecuteFilter {
    public final int REQUEST_TIMEOUT = 240000;//set your desired timeout here
    private ExecutorService exe;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        int size = 41;//set your desired pool size here
        exe = Executors.newFixedThreadPool(
                size,
                new ThreadFactory() {
                    public Thread newThread(Runnable r) {
                        return new Thread(r, "My Struts Async Processor");
                    }
                }
        );

        super.init(filterConfig);
    }

    @Override
    public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException {

        final HttpServletRequest request = (HttpServletRequest) req;
        final HttpServletResponse response = (HttpServletResponse) res;

        if (excludeUrl(request)) {
            chain.doFilter(request, response);
            return;
        }

        // This is necessary since we need the dispatcher instance, which was created by the prepare filter
        if (execute == null) {
            lazyInit();
        }

        final ActionMapping mapping = prepare.findActionMapping(request, response);

        //if recursion counter is > 1, it means we are in a "forward", in that case a mapping will still be
        //in the request, if we handle it, it will lead to an infinite loop, see WW-3077
        final Integer recursionCounter = (Integer) request.getAttribute(PrepareOperations.CLEANUP_RECURSION_COUNTER);

        if (mapping == null || recursionCounter > 1) {
            boolean handled = execute.executeStaticResourceRequest(request, response);
            if (!handled) {
                chain.doFilter(request, response);
            }
        } else {
            /* I ADDED THESE */
            final AsyncContext context = req.startAsync();
            context.setTimeout(REQUEST_TIMEOUT);

            context.addListener(new AsyncListener() {
                public void onComplete(AsyncEvent asyncEvent) throws IOException {
                }

                public void onTimeout(AsyncEvent asyncEvent) throws IOException {
                    context
                            .getResponse()
                            .getWriter().write("Request Timeout");
                }

                public void onError(AsyncEvent asyncEvent) throws IOException {
                    context
                            .getResponse()
                            .getWriter().write("Processing Error");
                }

                public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
                }
            });
            exe.execute(new ContextExecution(context, mapping));
        }
    }

    private boolean excludeUrl(HttpServletRequest request) {
        return request.getAttribute(StrutsPrepareFilter.class.getName() + ".REQUEST_EXCLUDED_FROM_ACTION_MAPPING") != null;
    }

    @Override
    public void destroy() {
        exe.shutdown();
        super.destroy();
    }

    class ContextExecution implements Runnable {

        final AsyncContext context;
        ActionMapping mapping;

        public ContextExecution(AsyncContext context, ActionMapping mapping) {
            this.context = context;
            this.mapping=mapping;
        }

        public void run() {
            try {
                execute.executeAction((HttpServletRequest) context.getRequest(),
                        (HttpServletResponse) context.getResponse(), mapping);

                context.complete();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

然后

<filter>
    <filter-name>struts2asyncexecute</filter-name>
    <filter-class>me.zamani.yasser.ww_convention.utils.MYStrutsAsyncExecuteFilter</filter-class>
    <async-supported>true</async-supported>
</filter>

然后将您想要的异步操作放在特定的包中,并将其从Strut的原始过滤器中排除,但将它们映射到web.xml中的上述过滤器。

我正在努力改进这一点,使其更具可配置性和清晰度,然后导入Struts。

你可以在你的应用程序中测试吗?请随时让我知道任何想法。