eclipse-ee4j / jersey

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

Deployment of an application without an Application subclass using web.xml descriptor does not work in Servlet 3.x container. #3564

Open jerseyrobot opened 7 years ago

jerseyrobot commented 7 years ago

I deploy a simple application that I created from Jersey Maven Archetype. It has no javax.ws.rs.core.Application subclass and I configured it exactly as the 4.7.2.3.1. JAX-RS application without an Application subclass paragraph of the documentation said. So the web.xml is

<web-app version="3.0"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <servlet>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
    </servlet>

    <servlet-mapping>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
        <url-pattern>/myresources/*</url-pattern>
    </servlet-mapping>
</web-app>

and there is only one dependency in the pom.xml

        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet</artifactId>
        </dependency>

However, when I package it to a war and deploy to Jetty 9.4 (supports Servlet API 3.1) I get

javax.servlet.UnavailableException: No class in holder javax.ws.rs.core.Application@83f91b4==null,jsp=null,order=-1,inst=false
    at org.eclipse.jetty.servlet.BaseHolder.doStart(BaseHolder.java:86)
    at org.eclipse.jetty.servlet.ServletHolder.doStart(ServletHolder.java:362)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:743)
    at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:348)
    at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1501)
    at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1463)
    at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:785)
    at org.eclipse.jetty.servlet.ServletContextHandler.doStart(ServletContextHandler.java:261)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:545)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.deploy.bindings.StandardStarter.processBinding(StandardStarter.java:41)
    at org.eclipse.jetty.deploy.AppLifeCycle.runBindings(AppLifeCycle.java:188)
    at org.eclipse.jetty.deploy.DeploymentManager.requestAppGoal(DeploymentManager.java:499)
    at org.eclipse.jetty.deploy.DeploymentManager.addApp(DeploymentManager.java:147)
    at org.eclipse.jetty.deploy.providers.ScanningAppProvider.fileAdded(ScanningAppProvider.java:180)
    at org.eclipse.jetty.deploy.providers.WebAppProvider.fileAdded(WebAppProvider.java:452)
    at org.eclipse.jetty.deploy.providers.ScanningAppProvider$1.fileAdded(ScanningAppProvider.java:64)
    at org.eclipse.jetty.util.Scanner.reportAddition(Scanner.java:610)
    at org.eclipse.jetty.util.Scanner.reportDifferences(Scanner.java:529)
    at org.eclipse.jetty.util.Scanner.scan(Scanner.java:392)
    at org.eclipse.jetty.util.Scanner.doStart(Scanner.java:313)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.deploy.providers.ScanningAppProvider.doStart(ScanningAppProvider.java:150)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.deploy.DeploymentManager.startAppProvider(DeploymentManager.java:561)
    at org.eclipse.jetty.deploy.DeploymentManager.doStart(DeploymentManager.java:236)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:131)
    at org.eclipse.jetty.server.Server.start(Server.java:452)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:113)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:113)
    at org.eclipse.jetty.server.Server.doStart(Server.java:419)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.xml.XmlConfiguration$1.run(XmlConfiguration.java:1511)
    at org.eclipse.jetty.xml.XmlConfiguration$1.run(XmlConfiguration.java:1438)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.eclipse.jetty.xml.XmlConfiguration.main(XmlConfiguration.java:1437)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.eclipse.jetty.start.Main.invokeMain(Main.java:222)
    at org.eclipse.jetty.start.Main.start(Main.java:486)
    at org.eclipse.jetty.start.Main.main(Main.java:79)

Additionally, I also tried to deploy on Tomcat 8.0.35 (supports Servlet API 3.1) and it deployed successfully, but complained when I actually requested a resource

java.lang.NullPointerException
    at org.apache.catalina.loader.WebappClassLoaderBase.binaryNameToPath(WebappClassLoaderBase.java:2524)
    at org.apache.catalina.loader.WebappClassLoaderBase.findLoadedClass0(WebappClassLoaderBase.java:2692)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1181)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1139)
    at org.apache.catalina.core.StandardWrapper.servletSecurityAnnotationScan(StandardWrapper.java:1180)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1099)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1520)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1476)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

Clone the project from my repository to reproduce the bug. It seems that Jersey (versions 2.25.1 and 2.26-b04) does not dynamically add a container Servlet as the documentation stated. (I also assume that the bug has something to do with #3517 because there is no servlet-class element in both cases).

jerseyrobot commented 6 years ago
jerseyrobot commented 7 years ago

@pavelbucek Commented Hi @andriy-simonov,

thanks for filing the issue. I tried to reproduce locally (on glassfish built from the master branch) and I'm able to deploy and access your application:

$ glassfish/bin/asadmin start-domain
Waiting for domain1 to start .....
Successfully started the domain : domain1
domain  Location: /Users/pavel/glassfish/github/main/appserver/distributions/glassfish/target/stage/glassfish5/glassfish/domains/domain1
Log File: > /Users/pavel/glassfish/github/main/appserver/distributions/glassfish/target/stage/glassfish5/glassfish/domains/domain1/logs/server.log
Admin Port: 4848
Command start-domain executed successfully.
$ glassfish/bin/asadmin deploy ~/jersey/bugs/3564/simple-service-webapp/target/simple-service-webapp.war 
Application deployed with name simple-service-webapp.
Command deploy executed successfully.
$ curl -vvv http://localhost:8080/simple-service-webapp/myresources/myresource
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /simple-service-webapp/myresources/myresource HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.51.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: GlassFish Server Open Source Edition  5.0 
< X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  5.0  Java/Oracle Corporation/1.8)
< Content-Type: text/plain
< Date: Tue, 30 May 2017 08:41:43 GMT
< Content-Length: 7
< 
Got it!

The only issue I do see is that the example packs jersey into WEB-INF/lib, which is known to be problematic when the container already does have Jersey on classpath. I would suggest making the dependency provided:

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <scope>provided</scope>
</dependency>
jerseyrobot commented 7 years ago

@andriy-simonov Commented @pavelbucek Thank you for your reply. I tried to run the application with the dependency scope set to provided (although I didn't hope it would help, as Jetty/Tomcat don't have any Jersey jars in their classpath) without any success. As glassfish is fully-fledged Java EE application server, there is some functionality that Jetty/Tomcat lack for them to work with this configuration I guess. However, as far as I understand the documentation promises that Jersey integrates with any Servlet containers supporting Servlet API 3.x, hence it still seems to me as a bug.