We have a test suite that runs against an app server (WLS) that has multiple ear/war files deployed.
The test suite fails sporadically.
The failures typically occur under some servlet other than the FacesServlet - eg. inside of Trinidad's ResourceServlet.
The reason for the failures come down to the fact that FacesContext.getCurrentInstance() is returning an InitFacesContext... but not the InitFacesContext for the current application!
Let's use a simplified example to demonstrate the problem.
We've got 2 JSF-based applications with the following context paths:
- / app2
Both of these apps also have the Trinidad ResourceServlet mapped to the "/trinidad-resource" servlet path.
When a request of the form:
Arrives at app1's ResourceServlet, from time to time we'll see FacesContext.getCurrentInstance() returning app2's InitFacesContext. As a result, app2's ServletContext leaks into app1 and a range of failures can occur.
This problem is a regression related to changes made for:
In revision 8826 of:
This commit removes a key call to InitFacesContext.release() that had previously been made in ConfigureListener.contextInitialized().
Instead, the intention seems to be to allow the InitFacesContext to be cleaned up at the first call to FacesServlet.service().
However, this assumes that FacesService.service() will be called immediately on the same thread after ConfigureListener.contextInitialized() completes.
This is not a valid assumption and leaves open the following path to an InitFacesContext/ServletContext/ThreadLocal leak:
1. Request arrives at /app2/trinidad-resource.
2. app2 is initialized.
3. ConfigureListener.contextInitalized() sets up app2's the InitFacesContext
4. Request completes without hitting the FacesServlet.
5. Thread is returned to the app server's thread pool, with app2's InitFacesContext.
6. A request arrives at /app1/trinidad-resource
7. A thread is pulled from the app server's thread pool to service the request.
8. As luck would have it, this thread happens to have the thread local with app2's InitFacesContext.
9. Some of app1's code calls FacesContext.getCurrentInstance() to grab the FacesContext.
10. app1 now has access to app2's InitFacesContext/ServletContext.
11. Bad things happen.
a) There is no guarantee that FacesServlet.service() will be called on the same thread after ConfigureListener.contextInitialized() is called. And...
b) There is no guarantee that the app server will clean up random thread locals before re-using a thread for some other request.
Mojarra need to ensure that it cleans up any thread locals that it sets up before the end of each request. I believe this means that ConfigureListener.contextInitialized() must call release() on the InitFacesContext, as was previously the case before the above fixes were applied.