mojarra
mojarra copied to clipboard
Mojarra impl gives NPE if user accidentally uses non-Mojarra API with Mojarra JSF impl
Using Mojarra impl 2.2.14 from: https://mvnrepository.com/artifact/com.sun.faces/jsf-impl/2.2.14
Since the Mojarra JSF implementation only works with the Mojarra set of JSF API, it would be nice if the Mojarra JSF implementation could clearly warn the user if they are not using the Mojarra set of JSF API early on in JSF initialization.
Additionally, it would be nice if some future rev of the JSF spec could come out with an implementation-agnostic set of API.
Here is the stack trace I got when I accidentally used the Mojarra JSF API with MyFaces JSF impl:
Stack Dump = java.lang.NullPointerException
at com.sun.faces.config.InitFacesContext.cleanupInitMaps(InitFacesContext.java:280)
at com.sun.faces.config.InitFacesContext.<init>(InitFacesContext.java:106)
at com.sun.faces.config.FacesInitializer.onStartup(FacesInitializer.java:130)
at com.ibm.ws.webcontainer.webapp.WebApp.initializeServletContainerInitializers(WebApp.java:2495)
at com.ibm.ws.webcontainer.webapp.WebApp.initialize(WebApp.java:1002)
at com.ibm.ws.webcontainer.webapp.WebApp.initialize(WebApp.java:6574)
at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.startWebApp(DynamicVirtualHost.java:467)
at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.startWebApplication(DynamicVirtualHost.java:462)
at com.ibm.ws.webcontainer.osgi.WebContainer.startWebApplication(WebContainer.java:1123)
at com.ibm.ws.webcontainer.osgi.WebContainer.access$000(WebContainer.java:105)
at com.ibm.ws.webcontainer.osgi.WebContainer$2.run(WebContainer.java:935)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:522)
at java.util.concurrent.FutureTask.run(FutureTask.java:277)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1153)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.lang.Thread.run(Thread.java:785)
Also, if things are mixed up the other way around (Mojarra API w/ other JSF impl) we get the following error because the javax.faces.FactoryFinderInstance needs to import the com.sun.faces.spi.InjectionProvider class.
[11/8/17 18:08:27:380 CST] 00000028 com.ibm.ws.webcontainer.webapp E SRVE0283E: Exception caught while initializing context: java.lang.NoClassDefFoundError: com.sun.faces.spi.InjectionProvider
at javax.faces.FactoryFinderInstance.copyInjectionProviderFromFacesContext(FactoryFinderInstance.java:174)
at javax.faces.FactoryFinderInstance.<init>(FactoryFinderInstance.java:157)
at javax.faces.CurrentThreadToServletContext.getApplicationFactoryManager(CurrentThreadToServletContext.java:150)
at javax.faces.CurrentThreadToServletContext.getApplicationFactoryManager(CurrentThreadToServletContext.java:91)
at javax.faces.FactoryFinder.getFactory(FactoryFinder.java:279)
at org.apache.myfaces.context.servlet.FacesContextImplBase.getApplication(FacesContextImplBase.java:169)
at org.apache.myfaces.context.servlet.FacesContextImplBase.getELContext(FacesContextImplBase.java:231)
at javax.faces.component.UIViewRoot.setLocale(UIViewRoot.java:1488)
at org.apache.myfaces.webapp.AbstractFacesInitializer._createFacesContext(AbstractFacesInitializer.java:529)
at org.apache.myfaces.webapp.AbstractFacesInitializer.initStartupFacesContext(AbstractFacesInitializer.java:501)
at org.apache.myfaces.webapp.StartupServletContextListener.contextInitialized(StartupServletContextListener.java:115)
at com.ibm.ws.webcontainer.webapp.WebApp.notifyServletContextCreated(WebApp.java:2384)
at com.ibm.ws.webcontainer31.osgi.webapp.WebApp31.notifyServletContextCreated(WebApp31.java:514)
at com.ibm.ws.webcontainer.webapp.WebApp.initialize(WebApp.java:1012)
at com.ibm.ws.webcontainer.webapp.WebApp.initialize(WebApp.java:6574)
at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.startWebApp(DynamicVirtualHost.java:467)
at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.startWebApplication(DynamicVirtualHost.java:462)
at com.ibm.ws.webcontainer.osgi.WebContainer.startWebApplication(WebContainer.java:1123)
at com.ibm.ws.webcontainer.osgi.WebContainer.access$000(WebContainer.java:105)
at com.ibm.ws.webcontainer.osgi.WebContainer$2.run(WebContainer.java:935)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:522)
at java.util.concurrent.FutureTask.run(FutureTask.java:277)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1153)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.lang.Thread.run(Thread.java:785)
Caused by: java.lang.ClassNotFoundException: com.sun.faces.spi.InjectionProvider
at com.ibm.ws.classloading.internal.AppClassLoader.findClassCommonLibraryClassLoaders(AppClassLoader.java:504)
at com.ibm.ws.classloading.internal.AppClassLoader.findClass(AppClassLoader.java:276)
at java.lang.ClassLoader.loadClassHelper(ClassLoader.java:797)
at java.lang.ClassLoader.loadClass(ClassLoader.java:775)
at com.ibm.ws.classloading.internal.AppClassLoader.findOrDelegateLoadClass(AppClassLoader.java:482)
at com.ibm.ws.classloading.internal.AppClassLoader.loadClass(AppClassLoader.java:443)
at java.lang.ClassLoader.loadClass(ClassLoader.java:752)
... 25 more
Additionally, it would be nice if some future rev of the JSF spec could come out with an implementation-agnostic set of API.
I'd like to clarify here that JSF API is already implementation agnostic. It's just that MyFaces has chosen to write their own JSF API classes.
The Mojarra API jar is only intended to be used to compile against. Due to historical reasons the API contains code indeed, which can not easily be undone.
The API jar should not be part of the runtime.
I don't understand how the JSF API could be excluded at runtime, then where would javax.faces.* classes be loaded from? If I am truly misunderstanding this port, then the rest of this comment will all be moot.
I understand that JSF has a long history, and am not asking the API-Impl dependencies to be untangled here. I am just suggesting that a check could be made early on in JSF initialization to warn the user if this situation is detected. For example, checking javax.faces.FactoryFinder.class.getPackage().getImplementationTitle().toUpperCase().contains("MOJARRA")
I don't understand how the JSF API could be excluded at runtime, then where would javax.faces.* classes be loaded from? If I am truly misunderstanding this port, then the rest of this comment will all be moot.
The only jar that should be on the runtime class path is the full mojarra jar, which contains both the API and implementation classes.
A proper JDK 9 build may make the existence of the API jar obsolete.
I'll try to see if we can do the kind of check you've asked for indeed, but maybe there are much more such places that would need guards. I'll think about this a little. Thx for your report!
Thanks for clarifying Arjan! Where would someone get the full mojarra jar? I've been getting the individual pieces from maven here: https://mvnrepository.com/artifact/com.sun.faces/jsf-api/2.2.14 https://mvnrepository.com/artifact/com.sun.faces/jsf-impl/2.2.14
Side note:
In case you are familiar with Liberty (or OpenLiberty), I'm working on a feature called jsfContainer-2.2 which will allow people to plug in a different JSF impl than the default one (MyFaces) and still get CDI integrations. I'm planning to include this check there as well, since people not very familiar with JSF (like myself) can make this mistake.
Single JARs can be found in org.glassfish:javax.faces repo. https://mvnrepository.com/artifact/org.glassfish/javax.faces/2.2.15