Search code examples
jsfcditomcat8omnifaces

fixing OmniFaces BeanManager initialization issues when Tomcat starts


We have recently moved to Tomcat 8.5.4 (from 8.5.3) and to Omnifaces 2.4 (from 2.3), and we have also changed a few things in our Web Application. Since then, our Web Application does not start anymore, with the following exception in the logs:

Exception sending context initialized event to listener instance of class org.omnifaces.ApplicationListener

java.lang.ExceptionInInitializerError
at org.omnifaces.ApplicationListener.checkCDIAvailable(ApplicationListener.java:77)
at org.omnifaces.ApplicationListener.contextInitialized(ApplicationListener.java:61)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4716)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5178)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:152)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1403)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1393)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

Caused by: java.lang.IllegalStateException: CDI BeanManager instance is not available in JNDI.
at org.omnifaces.config.BeanManager.<init>(BeanManager.java:100)
at org.omnifaces.config.BeanManager.<clinit>(BeanManager.java:49)
... 11 more

Caused by: java.lang.IllegalStateException: javax.naming.NamingException: WELD-001300: Unable to locate BeanManager
at org.omnifaces.util.JNDI.lookup(JNDI.java:95)
at org.omnifaces.config.BeanManager.<init>(BeanManager.java:96)
... 12 more

Caused by: javax.naming.NamingException: WELD-001300: Unable to locate BeanManager
at org.jboss.weld.resources.ManagerObjectFactory.getObjectInstance(ManagerObjectFactory.java:62)
at org.apache.naming.factory.FactoryBase.getObjectInstance(FactoryBase.java:94)
at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:321)
at org.apache.naming.NamingContext.lookup(NamingContext.java:840)
at org.apache.naming.NamingContext.lookup(NamingContext.java:160)
at org.apache.naming.NamingContext.lookup(NamingContext.java:828)
at org.apache.naming.NamingContext.lookup(NamingContext.java:160)
at org.apache.naming.NamingContext.lookup(NamingContext.java:828)
at org.apache.naming.NamingContext.lookup(NamingContext.java:174)
at org.apache.naming.SelectorContext.lookup(SelectorContext.java:163)
at javax.naming.InitialContext.lookup(InitialContext.java:417)
at org.omnifaces.util.JNDI.lookup(JNDI.java:90)
... 13 more

Exception sending context initialized event to listener instance of class com.sun.faces.config.ConfigureListener

java.lang.RuntimeException: java.lang.NoClassDefFoundError: Could not initialize class org.omnifaces.config.BeanManager
at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:292)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4714)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5178)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:152)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1403)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1393)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.omnifaces.config.BeanManager
at org.omnifaces.util.Beans.getManager(Beans.java:88)
at org.omnifaces.util.Beans.getReference(Beans.java:113)
at org.omnifaces.application.OmniApplication.<init>(OmniApplication.java:70)
at org.omnifaces.application.OmniApplicationFactory.createOmniApplication(OmniApplicationFactory.java:89)
at org.omnifaces.application.OmniApplicationFactory.getApplication(OmniApplicationFactory.java:54)
at com.sun.faces.application.InjectionApplicationFactory.getApplication(InjectionApplicationFactory.java:93)
at com.sun.faces.config.InitFacesContext.getApplication(InitFacesContext.java:142)
at com.sun.faces.lifecycle.ClientWindowFactoryImpl.<init>(ClientWindowFactoryImpl.java:62)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at javax.faces.FactoryFinderInstance.getImplGivenPreviousImpl(FactoryFinderInstance.java:405)
at javax.faces.FactoryFinderInstance.getImplementationInstance(FactoryFinderInstance.java:251)
at javax.faces.FactoryFinderInstance.getFactory(FactoryFinderInstance.java:543)
at javax.faces.FactoryFinder.getFactory(FactoryFinder.java:283)
at com.sun.faces.config.processor.FactoryConfigProcessor.verifyFactoriesExist(FactoryConfigProcessor.java:328)
at com.sun.faces.config.processor.FactoryConfigProcessor.process(FactoryConfigProcessor.java:236)
at com.sun.faces.config.ConfigManager.initialize(ConfigManager.java:439)
at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:227)

Our configuration:

  • Tomcat 8.5.4
  • CDI Weld 2.3.5.Final
  • JSF 2.2.13
  • Omnifaces 2.4
  • Primefaces 6.0
  • An empty beans.xml file in WEB-INF
  • A BeanManager resource entry in the context of our root Web Application
  • Tomcat is started by our Java code (embedded usage).

The BeanManager resource entry:

<Resource name="BeanManager"
          auth="Container"
          type="javax.enterprise.inject.spi.BeanManager"
          factory="org.jboss.weld.resources.ManagerObjectFactory"/>

We've moved back to Tomcat 8.5.3 and to omnifaces 2.3, and tried different combinations of component versions (8.5.3 with 2.4, 8.5.4 with 2.3, etc...) in order to identify the root cause of the problem, but to no avail.

Finally, we suspect a racing condition issue between the initialization of all these components (Tomcat, Weld, Onmnifaces, ...).

In a last resort, I have removed the BeanManager resource entry from the context XML file.

Removed from the context:

<Resource name="BeanManager"
          auth="Container"
          type="javax.enterprise.inject.spi.BeanManager"
          factory="org.jboss.weld.resources.ManagerObjectFactory"/>

And it fixed the problem.

I had a look to the change logs of Tomcat 8.5.4, and I could find this change:

Do not attempt to start web resources during a web application's initialisation phase since the web application is not fully configured at that point and the web resources may not be correctly configured. (markt)

I don't know if this Web Application's initialization phase issue reported in the Tomcat change logs could be linked to the problem we had. The reason why the removal of the BeanManager resource from the context fixed the problem is still unclear.

Any idea?


Solution

  • javax.naming.NamingException: WELD-001300: Unable to locate BeanManager

    This basically means that the BeanManager JNDI resource definition (as defined in context.xml) is found, but that the concrete BeanManager instance behind the JNDI resource isn't created yet.

    This indeed matches the described change in Tomcat 8.5.4.

    Do not attempt to start web resources during a web application's initialisation phase since the web application is not fully configured at that point and the web resources may not be correctly configured. (markt)

    I can't tell the reasoning of Mark Thomas for this decision and there's also no issue link behind it. I guess he was trying to avoid confusing behavior caused by intermittently broken deployments caused by potential initialization ordering issues. This is perhaps a good thing, but I think Mark actually overlooked the possibility of defining the initialization order via <ordering> element in web.xml and web-fragment.xml files. I indeed recall that Tomcat never respected this as to invocation order of @WebListener-annotated or programmatically created instances (it however does that as to <listener> declared instances). Perhaps Mark should better have fixed that part instead of disabling JNDI altogether during initialization phase.

    As to the change in OmniFaces 2.4, as per issue 243 this version added a fallback to a Weld-specific servlet context attribute. This will be used when the JNDI resource doesn't exist, and Weld is being used as CDI implementation. Namely, Weld internally stores the BeanManager instance as a servlet context attribute too, so OmniFaces could just grab it without the need for JNDI. This means, the context.xml is no longer necessary. In Tomcat 8.5.4, you should actually remove the context.xml too because OmniFaces 2.4 still first tries to inspect the JNDI resource, which would eventually throw an exception because the BeanManager instance is unexpectedly unavailable as consequence of that Tomcat 8.5.4 change.

    In OmniFaces 2.5, the CDI initialization has been reworked and improved as per issue 281 which describes the migration of CDI 1.0 to 1.1. OmniFaces no longer uses JNDI to inspect the BeanManager. OmniFaces will now use CDI 1.1-introduced CDI API.

    BeanManager beanManager = CDI.current().getBeanManager();
    

    This works regardless of JNDI configuration/initialization.

    OmniFaces 2.5 is not final yet, but the 2.5-RC1 is available in Maven central as of yesterday.