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

Using custom servlet in Quarkus app fails with ClassNotFoundException #19

Open
mvysny opened this issue Jan 7, 2021 · 3 comments
Open

Comments

@mvysny
Copy link
Member

mvysny commented Jan 7, 2021

Description of the bug / feature

Using custom servlet in a Quarkus+Vaadin app causes DevModeInitializer to fail with ClassNotFoundException.

The reason is that Quarkus uses multiple classloaders; it loads Vaadin jars in one classloader and the app code (including the Servlet class) in another classloader. The "Vaadin classloader" is then unable to load the Servlet class.

The exception stacktrace follows:

2021-01-07 09:52:04,044 ERROR [io.qua.run.boo.StartupActionImpl] (Quarkus Main Thread) Error running Quarkus: java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at io.quarkus.runner.bootstrap.StartupActionImpl$3.run(StartupActionImpl.java:134)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.ExceptionInInitializerError
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at java.base/java.lang.Class.newInstance(Class.java:584)
	at io.quarkus.runtime.Quarkus.run(Quarkus.java:61)
	at io.quarkus.runtime.Quarkus.run(Quarkus.java:38)
	at io.quarkus.runtime.Quarkus.run(Quarkus.java:104)
	at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)
	... 6 more
Caused by: java.lang.RuntimeException: Failed to start quarkus
	at io.quarkus.runner.ApplicationImpl.<clinit>(ApplicationImpl.zig:178)
	... 15 more
Caused by: java.lang.RuntimeException: java.lang.RuntimeException: javax.servlet.ServletException: java.lang.reflect.InvocationTargetException
	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder.bootServletContainer(UndertowDeploymentRecorder.java:519)
	at io.quarkus.deployment.steps.UndertowBuildStep$build-649634386.deploy_0(UndertowBuildStep$build-649634386.zig:1948)
	at io.quarkus.deployment.steps.UndertowBuildStep$build-649634386.deploy(UndertowBuildStep$build-649634386.zig:40)
	at io.quarkus.runner.ApplicationImpl.<clinit>(ApplicationImpl.zig:158)
	... 15 more
Caused by: java.lang.RuntimeException: javax.servlet.ServletException: java.lang.reflect.InvocationTargetException
	at io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:255)
	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder.bootServletContainer(UndertowDeploymentRecorder.java:508)
	... 18 more
Caused by: javax.servlet.ServletException: java.lang.reflect.InvocationTargetException
	at com.vaadin.flow.server.startup.ClassLoaderAwareServletContainerInitializer.onStartup(ClassLoaderAwareServletContainerInitializer.java:99)
	at io.undertow.servlet.core.DeploymentManagerImpl$1.call(DeploymentManagerImpl.java:205)
	at io.undertow.servlet.core.DeploymentManagerImpl$1.call(DeploymentManagerImpl.java:187)
	at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:42)
	at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$10$1.call(UndertowDeploymentRecorder.java:554)
	at io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:253)
	... 19 more
Caused by: java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at com.vaadin.flow.server.startup.ClassLoaderAwareServletContainerInitializer.onStartup(ClassLoaderAwareServletContainerInitializer.java:94)
	... 25 more
Caused by: javax.servlet.ServletException: Servlet class name (org.acme.servlet.MyServlet) can't be found!
	at com.vaadin.flow.server.startup.DevModeInitializer.process(DevModeInitializer.java:198)
	... 30 more
Caused by: java.lang.ClassNotFoundException: org.acme.servlet.MyServlet
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
	at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:412)
	at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:365)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:315)
	at com.vaadin.flow.server.startup.DevModeInitializer.isVaadinServletSubClass(DevModeInitializer.java:218)
	at com.vaadin.flow.server.startup.DevModeInitializer.process(DevModeInitializer.java:191)
	... 30 more

Minimal reproducible example

Please see the attached app.

vaadin-quarkus.zip

  1. Unzip the app
  2. Run mvn -C clean package quarkus:dev

Expected behavior

Vaadin should use Thread's context class loader to load the classes

Actual behavior

Vaadin uses Class.forName() which uses DevModeInitializer.class.getClassLoader().

Versions:

- Vaadin / Flow version: 14.4.4/2.4.3
- Java version: 11
- Application Server (if applicable): Quarkus+Vertx+Undertow
@denis-anisimov
Copy link

The problem is in fundamentally broken code in DevModeInitializer which tries to use DeploymentConfiguration when there is no any DeploymentConfiguration: deployment configuration is intended to be used for a servlet. DevModeInitializer is invoked before any other initialization and it may not expect that there are some servlets at all.
(and if there are several servlets then whose DeploymentConfiguration to use ?).

DevModeInitializer.isVaadinServletSubClass is used to find the Vaadin servlet to get its DeploymentConfiguration .

It's absolutely wrong to use DeploymentConfiguration in any ServletContextListener.
The only config which can be available in the ServletContextListener is an application configuration.
There is no any application configuration in V14 (and may be never be).

But this is already properly done in the master : Flow 6.0.
If you are able to use V19/Flow 6.0 then please use it and there should not be the issue at all.

@mvysny
Copy link
Member Author

mvysny commented Jan 7, 2021

Thank you ;) Yeah I've already took advantage of the com.vaadin.flow.di.ResourceProvider and implemented my own QuarkusResourceProvider which uses Thread.currentThread().getContextClassLoader() to load flow-build-info.json properly - excellent work! (sorry - not really related to this ticket; BUT using custom ResourceProvider fixes the original issue vaadin/flow#9713, which causes no need for a custom servlet, which renders this ticket unnecessary).

Unfortunately this kind of solution is not applicable to Vaadin 14 (since there is nothing similar to Lookup). Any chance of a backport of Lookup/ResourceProvider to Vaadin 14?

@denis-anisimov
Copy link

Lookup is only in the master.
The changes related to Lookup impl and usage are quite breaking.
I don't think they will be backported to the v14.......
At least I have no intention to do this.

But you may create a ticket about this: let's redirect this question to the decision makers.

@pleku pleku transferred this issue from vaadin/flow Aug 24, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants