Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible Bug with deferServletRequestListenerDestroyOnError #18281

Closed
volosied opened this issue Aug 20, 2021 · 3 comments · Fixed by #18333
Closed

Possible Bug with deferServletRequestListenerDestroyOnError #18281

volosied opened this issue Aug 20, 2021 · 3 comments · Fixed by #18333
Assignees
Labels
in:Web Components release bug This bug is present in a released version of Open Liberty release:210010 team:Sirius

Comments

@volosied
Copy link
Member

volosied commented Aug 20, 2021

Description

When deferServletRequestListenerDestroyOnError is true, and an error occurs during an asynchronous dispatch, then
ServletRequestListener.requestDestroyed() isn't called.

During an investigation a failing TCK test in PR #17210, I discovered this particular problem might be the root cause for that failure.

I've attached a sample app to the this issue (at the bottom). The logging from this sample app is shown in the snippets below.

WORKING Case: Liberty
-- deferServletRequestListenerDestroyOnError false (default)

AppRequestListener.requestInitialized called!
Async processing started
AppRequestListener.requestDestroyed called!
Async processing finished
AppRequestListener.requestInitialized called!
throwing ServletException!
[ERROR   ] SRVE0777E: Exception thrown by application class 'test.async.FailingServlet.service:33'
jakarta.servlet.ServletException
	at test.async.FailingServlet.service(FailingServlet.java:33)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:587)
	at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1258)
	at [internal classes]

AppRequestListener.requestDestroyed called! <------- Expected 
[WARNING ] SRVE8046E: An error occurred while invoking a call to AsyncContext dispatch.
jakarta.servlet.ServletException

FAILING Case: Liberty
-- deferServletRequestListenerDestroyOnError true

AppRequestListener.requestInitialized called!
Async processing started
AppRequestListener.requestDestroyed called!
Async processing finished
AppRequestListener.requestInitialized called!
throwing ServletException!
[ERROR   ] SRVE0777E: Exception thrown by application class 'test.async.FailingServlet.service:33'
jakarta.servlet.ServletException
	at test.async.FailingServlet.service(FailingServlet.java:33)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:587)
	at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1258)
	at [internal classes]

[WARNING ] SRVE8046E: An error occurred while invoking a call to AsyncContext dispatch.
jakarta.servlet.ServletException

Tomcat

AppRequestListener.requestInitialized called!
Async processing started
Async processing finished
throwing ServletException!
AppRequestListener.requestDestroyed called!  <------- Expected 

This scenario (and the app) appears to be valid case. I've compared Liberty, Tomcat, and Jetty.
EE9 App:
async-servletRequestListener-bug.war.zip

@dinob68
Copy link

dinob68 commented Dec 15, 2022

I am on IBM Liberty 22.0.0.8
It uses following features:

  <featureManager>
    <feature>jndi-1.0</feature>
    <feature>appSecurity-2.0</feature>
    <feature>localConnector-1.0</feature>
    <feature>monitor-1.0</feature>
    <feature>jsf-2.3</feature>
    <feature>jaxrs-2.1</feature>
    <feature>jsp-2.3</feature>
    <feature>jca-1.7</feature>
    <feature>jdbc-4.2</feature>
    <feature>jaxws-2.2</feature>
    <feature>ejbLite-3.2</feature>
    <feature>servlet-4.0</feature>
    <feature>microProfile-3.3</feature>
    <feature>javaee-8.0</feature>
  </featureManager>

I found your post on StackOverflow advising to use this property and set it to true (https://stackoverflow.com/questions/43891297/weld-001303-no-active-contexts-for-scope-type-javax-enterprise-context-sessions).

In my case, I use IBM Liberty server and if I dont have below in my server.xml
<webContainer deferServletRequestListenerDestroyOnError="true" />

When my JSF app throws a RuntimeException, I get :

Error Page Exception: com.ibm.ws.webcontainer.webapp.WebAppErrorReport: javax.servlet.ServletException: WELD-001303: No active contexts for scope type javax.enterprise.context.SessionScoped
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:236)
	at [internal classes]
	at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:64)
	at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:201)
	at [internal classes]
Caused by: javax.servlet.ServletException: WELD-001303: No active contexts for scope type javax.enterprise.context.SessionScoped
	... 6 more
Caused by: org.jboss.weld.contexts.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.SessionScoped
	at org.jboss.weld.manager.BeanManagerImpl.getContext(BeanManagerImpl.java:647)
	at [internal classes]
	at net.bootsfaces.listeners.AddResourcesListener.ensureExistBootsfacesComponent(AddResourcesListener.java:160)
	at net.bootsfaces.listeners.AddResourcesListener.processEvent(AddResourcesListener.java:137)
	at javax.faces.event.SystemEvent.processListener(SystemEvent.java:49)
	... 4 more

When I add to my server.xml line:

<webContainer deferServletRequestListenerDestroyOnError="true" />
, the error is gone and my error.xhtml page shows as expected.

But then I find this discussion on GitHub in which you suggest not using this setting set to true so I am confused and there is next to none explanation what this setting really does.

Even More Confusing

I have another application (the same one actually, just minor difference as one uses a RESTAPI and the other uses MQ to retrieve data but the rest is same). The other application server.xml does not need this property set at all and is handliner error pages just fine. Both are throwing the RuntimeException at the same place and under same condition, both have identically set pages, error pages, web.xml, faces-context.xml, both have same settings in server.xml, both running Liberty 22.0.0.8, yet one has this issue, the other does not.

Could you elaborate please and much appreciated

@volosied
Copy link
Member Author

volosied commented Dec 15, 2022

Hi @dinob68 ,

I agree that the explanation around this property could be improved.

What it does:
As the name implies, it delays the ServletRequestListener#requestDestroyed call(s).
https://javaee.github.io/javaee-spec/javadocs/javax/servlet/ServletRequestListener.html#requestDestroyed-javax.servlet.ServletRequestEvent-

Why this matters:
Weld, the CDI implementation, has a WeldInitialListener class that implements ServletRequestListener. When requestDestroyed is called, it deactivates the CDI context for that request.

https://github.com/weld/core/blob/3.0/modules/web/src/main/java/org/jboss/weld/module/web/servlet/WeldInitialListener.java#L132-L139 (Linked 3.0.0, but I don't recall which version cdi-2.0 uses.)

Webcontainer has a list of all ServletRequestListeners and call requestDestroyed for each at the end of the request. Here's where the problem begins: if an unhandled error occurs (ELException, for instance) during the processing of the request, it will call ServletRequestListener#requestDestroyed fairly early (compared to other servlet implementations). It then dispatches to the error handling, where the custom page will be processed. However, if this page uses any CDI beans, the ContextNotActiveException will occur since the CDI context was deactivated.

Additionally, The servlet spec doesn't specifically mention when to call the ServletRequestListener methods during errors.

Returning to the property, by delaying the ServletRequestListener#requestDestroyed (and hence WeldInitialListener ), we keep the CDI Context active which allows the beans to be available in the error page. The listener would be called after error handling finishes.

In summary...
deferServletRequestListenerDestroyOnError: false (default)

Request comes in scope
Some error during request processing
ServletRequestListener.requestDestroyed called (i.e WeldInitialListener) 
Internal Redirect to Custom Error Page

deferServletRequestListenerDestroyOnError: true (new behavior)

Request comes in scope
Some error (i.e. EL Exception) during request processing
Internal Redirect to Custom Error Page
ServletRequestListener.requestDestroyed called (ie. WeldInitialListener)

This particular bug (#18281) was created because we switched the default to true for EE9 and higher. The requestDestroyed calls failed to occur for Asynchronous Servlets. This problem was fixed last year.

The property isn't defaulted to true for older versions because Websphere was strict in preserving existing behavior in order not to break any users. See Bill's comment here: #9929 (comment)

Lastly, I'm not sure why the other application doesn't need this configuration given the description you provided. It's hard to tell why without more information. Perhaps you could collect a jsf, cdi, and webcontainer trace for both applications for me to compare? However, I might not be able to get back to you for a few weeks due to the holidays.

Since you're using 22.0.0.8, there shouldn't be any harm in deferServletRequestListenerDestroyOnError (since the async problem had been solved and not other bugs identified)
Alternatively, if you could use EE9, then you wouldn't need it to be set since it was changed to true by default (as mentioned before).

@dinob68
Copy link

dinob68 commented Dec 15, 2022

@volosied Thank you for clarifying this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in:Web Components release bug This bug is present in a released version of Open Liberty release:210010 team:Sirius
Projects
Archived in project
Web Tier Team
  
Completed Tasks
Development

Successfully merging a pull request may close this issue.

5 participants