OpenLiberty / open-liberty

Open Liberty is a highly composable, fast to start, dynamic application server runtime environment
https://openliberty.io
Eclipse Public License 2.0
1.14k stars 583 forks source link

Moving from JavaEE8 to JakartaEE10 leads to ClassNotFoundException: org.apache.myfaces.push.WebsocketComponentRenderer during startup #26760

Open UlrichLohrmann opened 9 months ago

UlrichLohrmann commented 9 months ago

I use Open Liberty 23.0.0.9 on Windows 10 with Adoptium 17 JDK. I try to move an application from JavaEE namespace to JakartaEE nachspace and the application is already running on the Open Liberty JakartaEE-10.0 feature. The application has a JSF GUI (Jakarta Faces GUI) and uses Primefaces 13.0.2.

One thing is the following exception during startup:

java.lang.ClassNotFoundException: org.apache.myfaces.push.WebsocketInitRenderer cannot be found by io.openliberty.jakarta.faces.4.0_1.0.81.cl230920230904-1158 at org.eclipse.osgi.internal.loader.BundleLoader.generateException(BundleLoader.java:541) at org.eclipse.osgi.internal.loader.BundleLoader.findClass0(BundleLoader.java:536) at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:416) at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:168) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:467) at org.apache.myfaces.core.api.shared.lang.ClassUtils.classForName(ClassUtils.java:146) at org.apache.myfaces.core.api.shared.lang.ClassUtils.simpleClassForName(ClassUtils.java:181) at org.apache.myfaces.core.api.shared.lang.ClassUtils.simpleClassForName(ClassUtils.java:164) at org.apache.myfaces.core.api.shared.lang.ClassUtils.newInstance(ClassUtils.java:340) at org.apache.myfaces.config.FacesConfigurator.configureRenderKits(FacesConfigurator.java:1082) at org.apache.myfaces.config.FacesConfigurator.configure(FacesConfigurator.java:472) at org.apache.myfaces.webapp.FacesInitializerImpl.buildConfiguration(FacesInitializerImpl.java:382) at org.apache.myfaces.webapp.FacesInitializerImpl.initContainerIntegration(FacesInitializerImpl.java:709) at org.apache.myfaces.webapp.FacesInitializerImpl.initFaces(FacesInitializerImpl.java:179) at org.apache.myfaces.webapp.StartupServletContextListener.contextInitialized(StartupServletContextListener.java:54) at com.ibm.ws.webcontainer.webapp.WebApp.notifyServletContextCreated(WebApp.java:2465) at com.ibm.ws.webcontainer31.osgi.webapp.WebApp31.notifyServletContextCreated(WebApp31.java:512) at com.ibm.ws.webcontainer.webapp.WebApp.initialize(WebApp.java:1063) at com.ibm.ws.webcontainer.webapp.WebApp.initialize(WebApp.java:6702) at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.startWebApp(DynamicVirtualHost.java:474) at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.startWebApplication(DynamicVirtualHost.java:469) at com.ibm.ws.webcontainer.osgi.WebContainer.startWebApplication(WebContainer.java:1197) at com.ibm.ws.webcontainer.osgi.WebContainer.access$100(WebContainer.java:112) at com.ibm.ws.webcontainer.osgi.WebContainer$3.run(WebContainer.java:994) at com.ibm.ws.threading.internal.ExecutorServiceImpl$RunnableWrapper.run(ExecutorServiceImpl.java:247) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:833)

Despite of the exception the application is running without errors.

It would be great if someone could give me additional hints on how to find the root cause of the exception. Trace level settings, additional investigations, any comment is appreciated.

Thanky in advance Ulrich

volosied commented 9 months ago

Thanks for finding this problem. This could be similar to https://issues.apache.org/jira/browse/MYFACES-4449, where the API class-loader is looking for an implementation class, but it's not visible. We may need to move some methods around to ensure the implementation class-loader looks for implementation classes.

I'll try to see if I can reproduce this / provide a fix sometime soon. (within the next week or so).

UlrichLohrmann commented 9 months ago

Thanks for providing information and for trying to reproduce this issue. If you need more information you can contact me at any time.

volosied commented 9 months ago

@UlrichLohrmann Sorry for the delay, but I'm looking at this now.

I'm not seeing how WebsocketInitRenderer is being loaded here. Can you provide the web.xml and faces-config.xml? Do you have a custom renderer?

UlrichLohrmann commented 9 months ago

I attach a ZIP file with web.xml and faces-config.xml.

We do not use custom renderer in the application. As the application uses Primefaces-13.0.2 and Spring Webflow maybe one of these frameworks adds a cusom renderer.

I also tried to run the application with Open Liberty using JakartaEE-9.1 instead of JakartaEE-10.0 and with this the exception does not occur. The JakartaEE-10.0 server ist based on the official "JakartaEE 10" ZIP Download of Open Liberty. The JakartaEE-9.1 Server is based on the "Kernel" ZIP Download and based on that "Kernel" Server I installed JakartaEE-9.1 feature using "featureUtility installFeature JakartaEE-9.1". web_faces-config.zip

volosied commented 9 months ago

Thanks for the quick reply.

I used the debugger to force the code into the path indicated above, and was able to reproduce the issue here.

It is a similar problem as in MYFACES-4449 where the impl classes were searched via the API classloader.

It related to why type of renderKit is used. If the renderKit is an instance of LazyRenderKit then the exception won't occur. However, if it's not, then the exception will occur. At least that's what I found out.

LazyRenderKit code was added here: https://github.com/apache/myfaces/commit/93a2ddf060d04cea658f58f9223c22b5badeea90

I'm not sure where this non-LazyRenderKit instance is coming from. Let me make a JIRA nonetheless.

Edit: Created https://issues.apache.org/jira/browse/MYFACES-4639

UlrichLohrmann commented 9 months ago

Thank you for your further investigations.

I ran my application in debugger and stopped on the ClassNotFoundException. The stackstrace in case of the ClassNotFoundException is

<init>:71, ClassNotFoundException (java.lang)
loadClass:641, BuiltinClassLoader (jdk.internal.loader)
loadClass:188, ClassLoaders$AppClassLoader (jdk.internal.loader)
loadClass:525, ClassLoader (java.lang)
findSystemClass:1257, ClassLoader (java.lang)
loadClassNoException0:212, GatewayClassLoader (com.ibm.ws.classloading.internal)
loadClassNoException:224, GatewayClassLoader (com.ibm.ws.classloading.internal)
findOrDelegateLoadClass:115, ParentLastClassLoader (com.ibm.ws.classloading.internal)
loadClass:580, AppClassLoader (com.ibm.ws.classloading.internal)
loadClassNoException:39, LibertyLoader (com.ibm.ws.classloading.internal)
findOrDelegateLoadClass:115, ParentLastClassLoader (com.ibm.ws.classloading.internal)
loadClass:580, AppClassLoader (com.ibm.ws.classloading.internal)
loadClassNoException:39, LibertyLoader (com.ibm.ws.classloading.internal)
loadClass0:129, UnifiedClassLoader (com.ibm.ws.classloading.internal)
loadClass:87, UnifiedClassLoader$Delegation (com.ibm.ws.classloading.internal)
loadClass:106, UnifiedClassLoader (com.ibm.ws.classloading.internal)
loadClass:525, ClassLoader (java.lang)
forName0:-1, Class (java.lang)
forName:467, Class (java.lang)
classForName:139, ClassUtils (org.apache.myfaces.core.api.shared.lang)
simpleClassForName:181, ClassUtils (org.apache.myfaces.core.api.shared.lang)
simpleClassForName:164, ClassUtils (org.apache.myfaces.core.api.shared.lang)
newInstance:340, ClassUtils (org.apache.myfaces.core.api.shared.lang)
configureRenderKits:1082, FacesConfigurator (org.apache.myfaces.config)
configure:472, FacesConfigurator (org.apache.myfaces.config)
buildConfiguration:382, FacesInitializerImpl (org.apache.myfaces.webapp)
initContainerIntegration:709, FacesInitializerImpl (org.apache.myfaces.webapp)
initFaces:179, FacesInitializerImpl (org.apache.myfaces.webapp)
.
.
.

The method configureRenderKits is the one that creates the Non Lazy render kit. There are two renderkits that will be processed in this method:

org.apache.myfaces.renderkit.html.HtmlRenderKitImpl org.springframework.faces.webflow.FlowRenderKit

And If I check the implementation of org.springframework.faces.webflow.FlowRenderKit I find

public class FlowRenderKit extends RenderKitWrapper {
.
.
}

public abstract class RenderKitWrapper extends RenderKit implements FacesWrapper<RenderKit> {
.
.
}

I assume its the "extends RenderKit" where the non lazy render kit is used.

What do you think?

UlrichLohrmann commented 9 months ago

As my application runs with Jakarta-9.1 based Open Liberty without exception I wonder whether it is a valid scenario to run a Spring 6/Spring Webflow 3 application on a Open Liberty Jakarta-10.0 based server? Shall I open an issue on the Spring WebFlow project?

volosied commented 9 months ago

I appreciate you debugging there and finding this out! The FlowRenderKit makes sense now. However, I think this is a bug solely in MyFaces. LazyRenderKit is an implementation class, not the API. Not much Spring WebFlow can do.

As to why it doesn't occur in EE9 / EE9.1, Faces 4.0 has a large number of refactoring and changes. In particular, ClassUtil was created and placed in the API. However, it's caused trouble with OSGI with all the class-loader separation.

I'll try to get a fix in soon for the next Liberty release. I'll keep you updated.