eclipse-ee4j / jersey

Eclipse Jersey Project - Read our Wiki:
https://github.com/eclipse-ee4j/jersey/wiki
Other
691 stars 357 forks source link

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

Open minfrin opened 1 year ago

minfrin commented 1 year ago

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().

jansupol commented 1 year ago

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

minfrin commented 1 year ago

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.

jansupol commented 1 year ago

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

minfrin commented 1 year ago

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 {
jansupol commented 1 year ago

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.