Bug 57190

Summary: ServletContext.getContext(String) cannot return context when using parallel deployments
Product: Tomcat 7 Reporter: Charles <charles.phillips>
Component: CatalinaAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED FIXED    
Severity: normal CC: charles.phillips
Priority: P2    
Version: 7.0.56   
Target Milestone: ---   
Hardware: Macintosh   
OS: All   

Description Charles 2014-11-05 17:13:18 UTC
When using ServletContext.getContext(String) in a parallel deployment the context is not returned.

For example:
foo_a##001 and foo_b##001 are deployed.
Page on /foo_a/test.jsp calls ServletContext.getContext("/foo_b").  In non parallel deployments this returns the ServletContext for "/foo_b". However in parallel deployments the ServletContext for "/" is returned instead.

This was encountered using a custom filter that is set to forward certain requests to files in one application to a central default application.

CrossContext is set to true for the context.xml.  This works as long as the wars do not use the version name such as foo_b.war instead of foo_b##001.war.

I would expect ServletContext.getContext("/foo_b") to return the current active context for a URL.  Instead it looks like it will require to specify the exact version of a context instead such as ServletContext.getContext("/foo_b##001").  

This puts the burden of finding the active version of the context on the calling application rather than the underlying servlet container to provide the most current context when ServletContext.getContext(String) is referenced.
Comment 1 Christopher Schultz 2014-11-05 18:41:52 UTC
I'd like to point out that this gets complicated since Tomcat uses session identification to determine which version of the target web application will be used.

Since the client likely didn't send their session identifier for the /other/ web application, ServletContext.getContext(String path) can't actually determine the correct ServletContext to return.

Is it possible for you to avoid using ServletContext.getContext(String) in your application, and instead issue a blind redirect?
Comment 2 Charles 2014-11-05 22:03:03 UTC
Blind redirect isn't an option.  We would rather just not use parallel deployments instead.

This seams like a limiting factor of the servlet implementation when using parallel deployments.  Is there no other way to get the context for cross context operations w/o knowing the specific version?
Comment 3 Mark Thomas 2014-11-13 10:19:14 UTC
I think the correct behaviour here would be for getContext("/foo_b") to return the latest version of foo_b, exactly as would happen if a user requested that URL.

I like the idea of being able to specify a specific version if necessary although I'm not sure on the best way of doing this. I like /foo_b##ver but I wouldn't want that exposed to external clients.
Comment 4 Christopher Schultz 2014-11-13 14:22:27 UTC
I suppose that's the best we can do, but if the client has a session with an older-than-latest-version of the /foo_b context, things might behave oddly.

Also, when the request-processing switches to the other web application, there will either be no session available there (I've never bothered to read about the session implications of cross-context forwards) or the wrong session (the one from /foo_a).

Charles, I think that even if Tomcat can do this for you, your application still might not be able to tolerate the situation.

But I agree with both of you: doing *something* is better than doing nothing.

+1 to returning the ServletContext that matches the latest-deployed version.
Comment 5 Mark Thomas 2014-12-01 15:20:59 UTC
This has been fixed in trunk, 8.0.x (for 8.0.16 onwards) and 7.0.x (for 7.0.58 onwards).

An explicit version can be selected by appending ##version to the end of the context path. Otherwise, the latest available version will be returned.
Comment 6 Konstantin Kolinko 2014-12-06 14:28:49 UTC
REOPENING. Reviewing r1642697

Using Mapper.map() for this purpose returns wrong result for missing contexts. The mapper maps prefix of an URL, and so for getContext("/foo/bar") it may return the context of "/foo" or the ROOT context.

I added a test case in r1643536, but commented-out the check, as it currently fails.

This needs either a special method in Mapper or in Host (such as getLatestVersion(contextName)), or a check that the path of returned context is the same as expected.

A difference between Mapper and Host is that Mapper returns only those contexts that are available. Using Host may return a context that has been stopped or have not started yet.  The same concern for the exact match branch of this method: does it need to return a non-started context, even if the name matches exactly?
Comment 7 Mark Thomas 2014-12-12 17:38:08 UTC
It needs to be a currently running context in all cases.

I have a patch for this. I'll apply it shortly.
Comment 8 Mark Thomas 2014-12-12 18:18:49 UTC
Fixed. Fixed versions remain unchanged.