Question

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 (vFabric TC Server) and Spring Framework 3.2.8.

I have configured the Tomcat conf/context.xml to have: `

<Context crossContext="true">...</Context>`. 

In the webapp2 JSP I use `

<c:import context="/webapp1" url="/myurl" />`.

When I call the webapp2 JSP I have this error:

HTTP Status 500 - javax.servlet.ServletException: javax.servlet.jsp.JspException: `java.lang.ClassCastException:` `org.springframework.web.context.request.async.WebAsyncManager cannot be cast to` org.springframework.web.context.request.async.WebAsyncManager`

Has anyone else encountered this?

Was it helpful?

Solution

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

OTHER TIPS

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.

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.

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:

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top