Bug 56658

Summary: Concurrency issue with wrapper mappings in Mapper
Product: Tomcat 8 Reporter: Konstantin Kolinko <knst.kolinko>
Component: CatalinaAssignee: Tomcat Developers Mailing List <dev>
Severity: normal    
Priority: P2    
Version: 8.0.9   
Target Milestone: ----   
Hardware: PC   
OS: All   

Description Konstantin Kolinko 2014-06-22 16:59:35 UTC
When a context starts up, its servlet mappings are registered in a Mapper.

The issue is that MapperListener.registerContext() is implemented as

        for (Container container : context.findChildren()) {
            registerWrapper((Wrapper) container);

It registers a context first, then registers it wrappers.  So there is a time slot when the Context have already been registered in the Mapper, but its Wrappers are not yet registered there.  If a request comes it, it can be mapped to a wrong servlet.

In CoyoteAdapter.postParseRequest there is code that protects from this issue when a Context is being reloaded.
- An "if (request.getContext().getPaused())" check.

In case if Context is deployed on an already running server, there is no protection.

I think the way to fix this it to amend the Mapper.addContextVersion() method and pass wrapper mappings to it. In this case the Mapper should be create a correct full configuration for a web application at once.

Alternatively, we may change the default value of StandardContext.paused flag to be "true" and switch it to "false" when the context has already started up.

Alternatively, we may check Context state in CoyoteAdapter.postParseRequest. Can this get rid of the "started" flag?
Comment 1 Konstantin Kolinko 2014-06-30 12:39:27 UTC
Fixed in Tomcat 8 (r1606714) and will be in 8.0.10 onwards.
Comment 2 Konstantin Kolinko 2014-07-09 12:21:13 UTC
Fixed in Tomcat 7 (r1609112) and will be in 7.0.55 onwards.
Comment 3 Konstantin Kolinko 2014-07-10 20:56:21 UTC
The fix for this issue caused a regression for web application reload.
(It was reported on the [VOTE] thread for 8.0.10 release candidate).

Steps to reproduce:
1. Start Tomcat
2. Open http://localhost:8080/examples/index.html
The page loads, as expected.

3. Touch $CATALINA_BASE/webapps/examples/WEB-INF/web.xml. This causes reload of the examples webapp.
4. Wait until reload of examples webapp completes successfully.

5. Refresh (Ctrl+F5) http://localhost:8080/examples/index.html
Expected: the page
Actual: error 404

From debugging:
After reload the lists of wrappers in MApper$ContextVersion object for this web application are empty. In other words, none of the servlets of this webapp are registered with the the mapper.

The cause:
Old code did 
        for (Container container : context.findChildren()) {
            registerWrapper((Wrapper) container);

The first call "addContextVersion(...)" after reload tried to re-register the same context with the mapper. As the context has already been registered, the call actually failed, but subsequent "registerWrapper()" calls did work successfully.

The new code essentially skips registration of wrappers if "addContextVersion" fails.
Comment 4 Konstantin Kolinko 2014-07-13 14:38:09 UTC
(In reply to Konstantin Kolinko from comment #3)

Fixed in trunk by r1610186 and will be in 8.0.11 onwards.
Fixed in tc7.0.x by r1610199 and will be in 7.0.55 onwards.