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 !
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:
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.
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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With