jersey icon indicating copy to clipboard operation
jersey copied to clipboard

javax.servlet.ServletException: Resource configuration class [Application] is not a subclass of class javax.ws.rs.core.Application

Open minfrin opened this issue 2 years ago • 5 comments

I have a web application where the javax.ws.rs.core.Application is provided by tomcat rather than embedded in the war file.

This triggers failure as follows:

        javax.servlet.ServletException: Resource configuration class [Application] is not a subclass of class javax.ws.rs.core.Application.
                at org.glassfish.jersey.servlet.WebComponent.createResourceConfig(WebComponent.java:517)
                at org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:275)

This happens because a check is being made with class.isAssignableFrom(), which fails if the classes are in different classloaders.

https://github.com/eclipse-ee4j/jersey/blob/2.x/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java#L513

The fix seems to be changing this check to not use class.isAssignableFrom().

minfrin avatar Jul 23 '23 21:07 minfrin

Removing isAssignableFrom sounds like later one would get ClassCastException instead. Why are there two Classloaders? What Tomcat and Jersey versions do you use?

jansupol avatar Jul 24 '23 10:07 jansupol

Upgrading tomcat7/java8 code to tomcat9/java17.

Jersey (2.40) jars are in the tomcat class path, while application is inside a war file.

Moving jersey out of tomcat and into war works round problem, but we have loads of apps and suddenly it's huge.

Looks like isAssignableFrom does a naive check, which fails where an actual cast would succeed.

minfrin avatar Jul 24 '23 11:07 minfrin

As I understand the issue, you have one classloader for JAX-RS Application class loaded by the classloader of Tomcat and the second classloader that loads the war application. But then, if isAssignableFrom fails, there would need to be the Application class loaded by each classloader, i.e. either there are two Application classes, or the classloaders need to be completely independent, i.e. both classloaders would load each class (including Application), so that each class is loaded twice. Correct?

Looks like isAssignableFrom does a naive check, which fails where an actual cast would succeed.

No, to my knowledge where isAssignableFrom fails, the actual cast throws ClassCastException

jansupol avatar Jul 24 '23 11:07 jansupol

As I understand the issue, you have one classloader for JAX-RS Application class loaded by the classloader of Tomcat and the second classloader that loads the war application. But then, if isAssignableFrom fails, there would need to be the Application class loaded by each classloader, i.e. either there are two Application classes, or the classloaders need to be completely independent, i.e. both classloaders would load each class (including Application), so that each class is loaded twice. Correct?

I assume that the tomcat classloader is inherited in some fashion by the war file, otherwise the war code wouldn't be able to call tomcat.

This used to work fine when it was tomcat7+java8+jersey[2.old], but now that it's upgraded to tomcat9 with java17 and jersey v2.40, the application class in the war file no longer works, most specifically it is claimed that the application class is not a subclass of javax.ws.rs.core.Application when it really is.

public class [Application] extends javax.ws.rs.core.Application {

minfrin avatar Jul 24 '23 20:07 minfrin

It would help us if you could create a sample project that fails with Tomcat to try. Otherwise, it can take some time to set up the testing env.

jansupol avatar Jul 26 '23 14:07 jansupol