Spring and cross context: WebAsyncManager cannot be cast to WebAsyncManager

前端 未结 4 1107
我在风中等你
我在风中等你 2020-12-18 09:03

I want to use the cross context feature in a Spring application so I can import some webapp1 JSP into a webapp2 JSP. I\'m using Eclipse STS with the included Tomcat 7.0.42 (

相关标签:
4条回答
  • 2020-12-18 09:09

    We fought with this exception in our hybrid applications (development: Spring Boot app / production: Liferay portlet; the exception occured on our Liferay servers) for several days.

    In our case disabling all (unneeded) servlet filters helped:

    • https://stackoverflow.com/questions/28421966/prevent-spring-boot-from-registering-a-servlet-filter (dimafeng's answer)
    • http://dimafeng.com/2015/11/27/dynamic-bean-definition/
    • https://github.com/dimafeng/dimafeng-examples/tree/master/dynamic-bean-def/src/main/java/com/dimafeng/examples/dynamic_bean_def
    0 讨论(0)
  • 2020-12-18 09:10

    Seems like Spring is not ready for cross context request processing (at least not without a bit of hacking).

    FrameworkServlet always tries to get WebAsyncManager from the request attributes. And the way it is extracted can not work across different contexts (class loaders).

    I see two possibilities how to workaround this:

    • Implement your own include JSP tag, which will wrap the original request so that Spring specific attributes are not visible (usually the ones starting with org.springframework) to the second context.
    • Put Spring JARs in a shared class loader path (that would be probably the easier way).
    0 讨论(0)
  • 2020-12-18 09:22

    Atlassian seems to have a work-around by using this filter:

    https://docs.atlassian.com/atlassian-confluence/latest/com/atlassian/confluence/internal/web/filter/spring/IgnoreWebAsyncManagerFilter.html

    According to the documentation:

    This filter exists to work around an issue with plugins that use SpringMVC. Spring's OncePerRequestFilter calls WebAsyncUtils.getAsyncManager(ServletRequest) to get a WebAsyncManager to determine if the request is asynchronous. getAsyncManager caches the WebAsyncManager it creates as an attribute on the ServletRequest.

    Since the host application and the plugin framework are using Spring classes from different ClassLoaders, that cached WebAsyncManager causes ClassCastExceptions for plugins which use SpringMVC.

    To avoid those exceptions, this filter detects when Spring attempts to cache its WebAsyncManager and ignores the call, ensuring it's never added to the request. Each call to getAsyncManager(ServletRequest will just create a new one, which is then immediately thrown away. But that means the part of the request that is handled in the host application creates instances from its ClassLoader, and the part that is handled in the plugin framework creates instances from the OSGi ClassLoader.

    Maybe you can try to get the source code, If you do so please reply and send me the class, or you can reproduce the behaviour. Seems to create a new object on each request so it avoids serializing it for cross context.

    0 讨论(0)
  • 2020-12-18 09:28

    I came across this issue today and based on @Sylvain Lecoy's answer I came up with this implementation of a servlet filter that acts in the way the Atlassian filter javadoc describes. Putting it before any filter that calls WebAsyncUtils.getAsyncManager(ServletRequest) seems to fix any classloader/cross-context issues.

    public class IgnoreWebAsyncManagerFilter implements Filter {
    
    static class IgnoreWebAsyncManagerCacheServletRequest extends HttpServletRequestWrapper {
    
        /**
         * Creates a ServletRequest adaptor wrapping the given request object.
         *
         * @param request the {@link ServletRequest} to wrap.
         * @throws IllegalArgumentException if the request is null
         */
        IgnoreWebAsyncManagerCacheServletRequest(HttpServletRequest request) {
            super(request);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void setAttribute(String name, Object o) {
            if (!name.equals(WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE)) {
                super.setAttribute(name, o);
            }
        }
    }
    
    
    /**
     * {@inheritDoc}
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        chain.doFilter(new IgnoreWebAsyncManagerCacheServletRequest((HttpServletRequest) request), response);
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public void destroy() {
    }
    

    Note: This assumes all requests are HttpServletRequest instances but could be modified to be more safe.

    0 讨论(0)
提交回复
热议问题