Deployment of a JAX-RS application using web.xml, Servlet 3.0 and Jersey
Asked Answered
G

5

7

I'm trying to deploy my application, using web.xml, servlet 3.0, and jersey API. Unfortunately, it doesn't work.

This is MyApplication.class :

package com.example;

public class MyApplication extends Application {
   public Set<Class<?>> getClasses() {
       Set<Class<?>> s = new HashSet<Class<?>>();
       s.add(MyResource.class);
       return s;
   }
}

This is MyResource :

@Path("/helloworld")
@Produces(MediaType.TEXT_PLAIN)
public class MyResource {
    @GET
    public String getHello() {
        return "HelloWorld !";
    }
}

And my web.xml :

 <web-app>
     <servlet>
         <servlet-name>com.example.MyApplication</servlet-name>
     </servlet>
     <servlet-mapping>
         <servlet-name>com.example.MyApplication</servlet-name>
         <url-pattern>/webapi/*</url-pattern>
     </servlet-mapping>
 </web-app>

On client side, I'm using this url : http://localhost:8080/[projectname]/webapi/helloworld

And i have this error :

java.lang.NullPointerException
    sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
    java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1629)
    org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:491)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:1852)
    java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
    java.lang.Thread.run(Thread.java:662)

What's wrong ? :/ I'm using Tomcat 7.

PS : with a servlet 2.x, it works :

    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.example</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/webapi/*</url-pattern>
    </servlet-mapping>

but i will need asynchronous mode later.

Thanks !

Glorious answered 3/9, 2013 at 14:36 Comment(1)
Since writing my answer, I've found out a way to avoid needing the web.xml on Tomcat using the official Glassfish Jersey implementation. It's just as easy as this method if not easier. Look here for details.Mordancy
M
9

Update: Since writing this answer, I've found out a way to avoid needing a web.xml on Tomcat using the official Glassfish Jersey implementation. Look here for details.

If you're using a standard Tomcat install (or some other servlet container), AFAIK you can't avoid explicitly telling it what servlets to start in the web.xml file*. Since you have to use web.xml anyway, the simplest way to get restful web services working is to forget extending javax.ws.rs.core.Application entirely and just specify the context path there. You can still use standard jax-rs annotations to declare the actual web services.

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  version="3.0"
>
  <servlet>
    <servlet-name>rest-test</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>com.domain.mypackage</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name> rest-test</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

Two noteworthy points:

  1. You will need to bundle a REST implementation in your WAR file, since servlet containers don't usually contain one. Since Jersey is the reference implementation for JAX-RS, that's the one I'm using in the servlet-class element above. You can replace this with Apache CXF implementation if you want.

  2. The init-param element tells Jersey which of your packages to search for Java files with web service annotations. Edit this to point to your web services. Note that if you opt to use apache CXF instead of Jersey, the stuff needed in any init-param elements will be different. Someone who knows CXF please post what they would be.

If you're using Maven, just add a dependency to jersey-servlet in the dependencies section of your pom.xml file:

<dependencies>
  <dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-servlet</artifactId>
    <version>1.18.2</version>
  </dependency>
  ...
</dependencies>

After this, declaring your web services is straight forward using the standard JAX-RS annotations in your Java classes:

package com.domain.mypackage;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.MatrixParam;
import javax.ws.rs.Path;

// It's good practice to include a version number in the path so you can have
// multiple versions deployed at once. That way consumers don't need to upgrade
// right away if things are working for them.
@Path("calc/1.0")
public class CalculatorV1_0 {
  @GET
  @Consumes("text/plain")
  @Produces("text/plain")
  @Path("addTwoNumbers")
  public String add(@MatrixParam("firstNumber") int n1, @MatrixParam("secondNumber") int n2) {
    return String.valueOf(n1 + n2);
  }
}

This should be all you need. If your Tomcat install is running locally on port 8080 and you deploy your WAR file to the context myContext, going to

http://localhost:8080/myContext/rest/calc/1.0/addTwoNumbers;firstNumber=2;secondNumber=3

...should produce the expected result (5).

Cheers!

* Someone please correct me if you know of a way to a add the Jersey servlet to the context in Tomcat without using web.xml--maybe by using a context or life cycle listener?

Mordancy answered 3/11, 2014 at 20:5 Comment(2)
Since writing this answer, I've found out a way to avoid needing the web.xml on Tomcat using the official Glassfish Jersey implementation. Look here for details.Mordancy
I was able to run it on Tomcat without web.xml and without the dependencies listed in your linked answer. Things likely changed over the years. See my answer.Butte
S
1
package com.example;

@ApplicationPath("/webapi")

public class MyApplication extends Application {

     public Set<Class<?>> getClasses() {
       Set<Class<?>> s = new HashSet<Class<?>>();
       s.add(MyResource.class);
       return s;
   }
}
Salahi answered 1/4, 2016 at 14:33 Comment(0)
L
0

You need to create web.xml under WEB-INF folder with following line of code

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <servlet>
        <servlet-name>ServletAdaptor</servlet-name>
        <servlet-class>
            org.glassfish.jersey.servlet.ServletContainer
        </servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>package where MyResource  resides</param-value>
        </init-param>
        <init-param>
            <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
            <param-value>true</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>ServletAdaptor</servlet-name>
        <url-pattern>/webresources/*</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
</web-app>

Now you can delete your MyApplication Class. Deploy and test web service. it will work

Libau answered 22/10, 2013 at 12:19 Comment(0)
B
0

Following the latest Eclipse Jersey Getting Started Guide and using the Maven archetype artifact id jersey-quickstart-webapp version 2.41 in a Tomcat 9.0 servlet 4.0 container I found that you can drop web.xml if you also extend Application to provide the ApplicationPath annotation in addition to switching the org.glassfish.jersey.containers from jersey-container-servlet-core to jersey-container-servlet. You may also need to set failOnMissingWebXml to false.

I created the test project using the following Maven command:

mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-webapp \
            -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \
            -DgroupId=com.example -DartifactId=simple-service-webapp -Dpackage=com.example \
            -DarchetypeVersion=2.41

I was able to run the application in Tomcat 9 with the Maven Archetype generated Servlet 2.5 web.xml.

<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container,
 see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>com.example</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>Jersey Web Application</servlet-name>
    <url-pattern>/webapi/*</url-pattern>
</servlet-mapping>
</web-app>

The generated pom.xml has these declared dependencies:

    <dependencies>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet-core</artifactId>
        <!-- use the following artifactId if you don't need servlet 2.x compatibility -->
        <!-- artifactId>jersey-container-servlet</artifactId -->
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.inject</groupId>
        <artifactId>jersey-hk2</artifactId>
    </dependency>
    <!-- uncomment this to get JSON support
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-binding</artifactId>
    </dependency>
    -->
</dependencies>

I attempted to switch to jersey-container-servlet and move web.xml out of the way but Maven needed to have failOnMissingWebXml despite using maven 3.8.6 and requiring maven-war-plugin 3.4.0.

            <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.4.0</version>
            <configuration>
                <failOnMissingWebXml>false</failOnMissingWebXml>
            </configuration>
        </plugin>

This built and ran, but the resource was not found no matter what URL I tried until I provided a javax.ws.rs.core.Application with the javax.ws.rs.ApplicationPath annotation.

package com.example;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("webapi")
public class ApplicationConfig extends Application {

}

Once this was in place the resource worked just as it did when using the web.xml to map the servlet.

The Eclipse EE4J Jersey 2.41 User Guide section 4.7.2 confirms this setup:

For simple deployments, no web.xml is necessary at all. Instead, an @ApplicationPath annotation can be used to annotate the custom Application subclass and define the base application URI for all JAX-RS resources configured in the application:

Butte answered 3/1 at 0:47 Comment(0)
D
-1

Visit this link may help: https://jersey.java.net/documentation/latest/deployment.html#deployment.servlet.2

section: 4.7.1. Servlet 2.x Container

<web-app>
    <servlet>
        <servlet-name>MyApplication</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            ...
        </init-param>
    </servlet>
    ...
    <servlet-mapping>
        <servlet-name>MyApplication</servlet-name>
        <url-pattern>/myApp/*</url-pattern>
    </servlet-mapping>
    ...
</web-app>
Dale answered 27/4, 2014 at 10:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.