83

Is it possible to set up a JAX-RS application using annotations only? (using Servlet 3.0 and JAX-RS Jersey 1.1.0)

I tried and had no luck. Using some web.xml seems required.


Configuration A (working, but has web.xml configuration)

web.xml

   ...
   <servlet>
      <servlet-name>org.foo.rest.MyApplication</servlet-name>
   </servlet>
   <servlet-mapping>
       <servlet-name>org.foo.rest.MyApplication</servlet-name>
       <url-pattern>/*</url-pattern>
   </servlet-mapping>
   ...

Java

@ApplicationPath("/")
public class MyApplication extends Application {
    ...
}

Configuration B (not working, exception thrown)

@ApplicationPath("/")
@WebServlet("/*") // <-- 
public class MyApplication extends Application {
    ...
}

The latter seems to insist that the Application will be a subclass of Servlet (the exception leaves no guesswork)

java.lang.ClassCastException: org.foo.rest.MyApplication cannot be cast to javax.servlet.Servlet

Questions

  1. Why the web.xml definition worked but the annotation didn't? What's the difference?

  2. Is there a way to have it worked, e.g. have a JAX-RS Application with no web.xml?

xingbin
  • 27,410
  • 9
  • 53
  • 103
Eran Medan
  • 44,555
  • 61
  • 184
  • 276
  • 1
    If you can try with NetBeans, there is a wizard for creating RESTFul web services. It seems that what you are trying to do is what this wizard does in ver 6.8. I am using 7.0.1 and the new approach is simpler but uses a single servlet for the purpose, that is com.sun.jersey.spi.container.servlet.ServletContainer but it' defined in web.xml – perissf Feb 21 '12 at 06:51

6 Answers6

173

** PLEASE READ IF YOU USE TOMCAT OR JETTY! **

The accepted answer does work, but only if the webapp is deployed to an app server like Glassfish or Wildfly, and possibly servlet containers with EE extensions like TomEE. It doesn't work on standard servlet containers like Tomcat, which I'm sure most people looking for a solution here want to use.

If you're using a standard Tomcat install (or some other servlet container), you need to include a REST implementation since Tomcat doesn't come with one. If you're using Maven, add this to the dependencies section:

<dependencies>
  <dependency>
    <groupId>org.glassfish.jersey.bundles</groupId>
    <artifactId>jaxrs-ri</artifactId>
    <version>2.13</version>
  </dependency>
  ...
</dependencies>

Then just add an application config class to your project. If you don't have any special configuration needs aside from setting the context path for the rest services, the class can be empty. Once this class is added, you don't need to configure anything in web.xml (or have one at all):

package com.domain.mypackage;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("rest") // set the path to REST web services
public class ApplicationConfig extends Application {}

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

Alvin Thompson
  • 5,388
  • 3
  • 26
  • 39
  • 1
    How does this answer relate to the question? The author asks about specific problem, not how to run JAX-RS in Tomcat. – ᄂ ᄀ Jan 02 '15 at 22:02
  • 2
    @alvin-thompson how do you build the war? Because i have an exception ===== Error assembling WAR: webxml attribute is required (or pre-existing WEB-INF/web.xml if executing in update mode) -> [Help 1] – Java Dude Jan 16 '15 at 23:20
  • 6
    @JavaDude - You have to configure Maven to build the WAR file even if the `web.xml` isn't present. In your `pom.xml`, under "build > plugins", add (if not already there): `org.apache.maven.pluginsmaven-war-plugin2.3false`. – Alvin Thompson Jan 16 '15 at 23:29
  • 6
    @JavaDude - the important part is the `false` option. – Alvin Thompson Jan 16 '15 at 23:32
  • 3
    @JavaDude - If that's not it, you likely didn't create a class that extends `Application` and add the `@ApplicationPath` annotation to it. The class can be empty, but it must be there. – Alvin Thompson Jan 16 '15 at 23:37
  • Thank you so much! Been looking for a reason why it wasn't working. It was working before I converted my project to Maven. After converting my Jersey Project to Maven this solution got my application up and running again. – NewfrontSolutions Apr 28 '15 at 15:10
  • This helped me. I also think annotations only w/o web.xml does not work on Apache Tomcat. It will be better if you can state the reason for this. – KiX Ortillan May 20 '15 at 12:41
  • 1
    @KiXOrtillan: Do you mean the reason the other answer doesn't work for servlet containers? That's because servlet containers don't normally include a REST implementation so you bundle one in your app. – Alvin Thompson May 20 '15 at 22:04
  • Yes. that's what I am asking. Thanks. – KiX Ortillan May 21 '15 at 10:55
  • 3
    It's enough to add only `jersey-container-servlet`, without pulling the whole `jaxrs-ri` dependency. E.g. in my case it was `'org.glassfish.jersey.core:jersey-server:2.18'` + `'org.glassfish.jersey.containers:jersey-container-servlet:2.18'`. – leveluptor Jun 16 '15 at 20:59
  • 1
    @tatianomnom: I imagine it depends on what you're doing. I would guess everything in `jaxrs-ri` is needed for some use-case or another. – Alvin Thompson Jun 16 '15 at 21:06
  • It might be nice to make this answer more googleable. I _knew_ my problem involved a non-ee server (since it worked locally on glassfish), so wouldn't have looked at this question quickly. I found this answer because of [this comment](http://stackoverflow.com/questions/20442632/create-restful-web-service-with-jax-rs-and-deploy-it-to-tomcat/26722059#comment-43303898). Do you have a site/blog or something? But still, thanks @AlvinThompson :-) – Hans Wouters Jul 27 '15 at 07:23
  • 3
    Since the question is clearly tagged with "java-ee", I do not understand the fuzz about providing and upvoting an answer that does NOT address a jee environment. -1 – Jan Galinski Sep 20 '15 at 16:16
  • 2
    @JanGalinski: I'm not sure what the confusion is. Even the standard version of Tomcat implements the Java EE web profile. This is indeed Java EE. – Alvin Thompson Sep 25 '15 at 18:14
  • But the terminology used in practice is 'servlet containers' vs '(full) ee containers'. TomEE is not called that because Tomcat itself is already considered an ee server.... au contaire. – Stijn de Witt Feb 22 '17 at 22:03
  • @AlvinThompson Regarding to your previous comment: Tomcat does *not* imlement the Java EE web profile. In your answer, you mention "standard servlet containers like Tomcat". Tomcat is just not standard, it's some implementation of parts of the specs (Servlet). "Common" would be a more appropriate word. – ymajoros Apr 28 '17 at 13:26
  • 1
    @ymajoros: You're correct; I should have said that Tomcat implements **parts** of the Java EE web profile and supports (or at least isn't incompatible with) the rest. The part we're interested in here (servlet) is implemented in Tomcat and for the rest, you just have to bundle an implementation in your war. This doesn't materially change my point however. – Alvin Thompson Apr 28 '17 at 17:27
  • Just an addition for those who (like me) are using RestEasy instead. For this to work you have to include both 'org.jboss.resteasy:resteasy-jaxrs:' and 'org.jboss.resteasy:resteasy-servlet-initializer:. What was driving me crazy was I was missing the second dependency. Hope it helps – oidualc Jul 12 '17 at 08:20
  • Not working for me with tomcat-7.0.76 as provided by CentOS7, and jaxrs-ri 2.28. @ApplicationPath is not found, and the servlet is silently ignored. – Graham Leggett Mar 16 '19 at 16:06
  • 1
    @GrahamLeggett: I'm going to guess that Tomcat 7 and earlier does not scan your packages for resources/annotations--or at least this annotation--by default. You can try maybe upgrading Tomcat 7 to use servlet-3.1 instead of servlet-3.0, but I'm not sure how to do this or even if this would work. Probably the simplest solution is to convince your IT guys to install at least Tomcat 8.5, but I know that may be a pain depending on your org. Tomcat 8.5 does require java 7 and later, but I'm assuming you use java 8 already. – Alvin Thompson Mar 17 '19 at 17:31
  • @Bendemann: [this question] (https://stackoverflow.com/questions/20670310/why-would-java-lang-illegalstateexception-the-resource-configuration-is-not-mo) may help you track down the issue... – Alvin Thompson Aug 13 '19 at 19:35
  • @AlvinThompson tried some of the suggestions out but didn't help. I've also tried with web.xml (also following your answer in a similar post) but without success. Actually I was trying to debug using log4j2 and it seems that the Application class is being hit by the server but not the REST api... – Bendemann Aug 15 '19 at 15:56
53

It seems that all I needed to do is this (Servlet 3.0 and above)

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

@ApplicationPath("/*")
public class MyApplication extends Application {
    ...
}

And no web.xml configuration was apparently needed (tried on Tomcat 7)

derpdewp
  • 182
  • 7
Eran Medan
  • 44,555
  • 61
  • 184
  • 276
  • 1
    Not understand how this work, you can use only the annotation without any other configuration? I have tried like this and still not working... – Filipe Apr 12 '14 at 00:28
  • 2
    @Filipe: I'm guessing that you're using Tomcat or some other servlet container. This actually doesn't work on servlet containers like Tomcat--it only works with an app servers like Glassfish or Wildfly, and maybe TomEE. If you're using Tomcat see my (late) answer below. – Alvin Thompson Nov 03 '14 at 17:44
  • 8
    **This answer does NOT work for Tomcat or Jetty!** Please see my answer to get things working in those (and other) servlet containers. – Alvin Thompson Nov 19 '14 at 18:46
  • 1
    Depending on your implementation, you _do_ need a web.xml, although an empty one will suffice. See http://docs.jboss.org/resteasy/docs/3.0.9.Final/userguide/html_single/index.html#d4e40 – Benjamin Maurer Sep 08 '15 at 07:55
  • This works on WebSphere Liberty but not on WebSphere 8.x and 9.0 Classic. – Archimedes Trajano May 07 '17 at 15:07
  • It would be helpful if you could include your imports for those of us with multiple options on our build path. Thanks. – Mark W Aug 18 '17 at 13:09
15

Chapter 2 of the JAX-RS: Java™ API for RESTful Web Services specification describes the publication process of a JAX-RS application in Servlet environment (section 2.3.2 Servlet in the specification).

Please note that Servlet 3 environment is recommended only (section 2.3.2 Servlet, page 6):

It is RECOMMENDED that implementations support the Servlet 3 framework pluggability mechanism to enable portability between containers and to avail themselves of container-supplied class scanning facilities.

In short, if you want to use a no-web.xml approach, it's possible with a custom implementation of javax.ws.rs.core.Application that registers RESTful service resources with the javax.ws.rs.ApplicationPath annotation.

@ApplicationPath("/rest")

Although you asked specifically about Jersey you may also like to read the article Implementing RESTful services with JAX-RS and WebSphere 8.5 Liberty Profile in which I described the no-web.xml publication process for WebSphere Liberty Profile (with Apache Wink as the implementation of JAX-RS).

Jacek Laskowski
  • 72,696
  • 27
  • 242
  • 420
  • 1
    Thanks!, I have another question strongly related that is driving me nuts, is there a way to have the path in the root (e.g. @ApplicationPath("/*") ) and still serve JSP using Annotations only? (though even with web.xml configuration I couldn't make it happen) - question is here: http://stackoverflow.com/questions/10874188/jax-rs-application-on-the-root-context-how-can-it-be-done, since you seem to know JAX-RS pretty well, would you please take a look? :) perhaps you'll see what I'm missing... thanks! – Eran Medan Apr 02 '13 at 00:45
  • I don't know. Gut feelings tell me that it should work with no additional configuration as the process of resolving a resource to handle a request is from the exact match to URL patterns. I'll have a look and respond. Thanks for encouraging me to expand my knowledge! :-) – Jacek Laskowski Apr 02 '13 at 12:50
  • Im using `@ApplicationPath("")` to have my REST services in the root (relative to the webapp context-root) on JBoss EAP 7. I have neither `beans.xml` nor `web.xml`. – Julien Kronegg Mar 01 '18 at 06:23
7

You need to setup the right dependencies in pom.xml

<dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.0.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet</artifactId>
    </dependency>

More details here: Starter example for jax-rs

ACV
  • 9,964
  • 5
  • 76
  • 81
  • can I delete web.xml if ResourceConfig/Application used? – BAE Dec 14 '17 at 15:31
  • yes, but you need to add this in pom.xml i fusnig mave: `` – ACV Dec 14 '17 at 15:41
  • thanks. but is there anything web.xml can do but ResourceConfig cannot? – BAE Dec 14 '17 at 15:42
  • No, I think since servlet spec 3.0 web.xml is not required anymore. But sometimes it is more convenient to use web.xml as it doesn't require recompilation if you want to change something. – ACV Dec 14 '17 at 19:01
  • And what if you're not using *Maven*? My project is too big right now to switch back. – TheRealChx101 Dec 30 '17 at 12:12
  • @TheRealChx101 So what are you using? Just find the jars and add them to the classpath or a similar setup for gradle. – ACV Dec 30 '17 at 14:19
  • @ACV That's what I've been doing since last week. I'm using Netbeans and GlassFish 4 for development. My server has tomcat 9. My WAR file does not use web.xml when deployed under GlassFish. I extend `javax.ws.rs.core.Application` and register my REST resources. I've tried SOOOO many tutorials but I just get 404. It's been a week. I tried Jetty, TJWS, Undertow. None worked. – TheRealChx101 Dec 30 '17 at 16:58
  • My web.xml file I have when deploying under Tomcat doesn't work either. The servlet container package is always different on different tutorials, which again, don't work. – TheRealChx101 Dec 30 '17 at 17:00
  • @TheRealChx101, this cold be something different. Not related to this thread 404 could mean that it's all been deployed fine, you're just not using the right path to access the resource! Make sure you set application name, application path and request path correctly. – ACV Dec 30 '17 at 18:06
  • @ACV It's working fine now both under Tomcat(8+9) and GlassFish. I'm using Ant Build (NetBeans 8). So what I did was go to my project's dependencies, then included `JAX WS.x.x.x`, `Jersey x.x.x (JAX RS RI)`, `JAX RS x.x` where `x` denotes version code. I build the `.WAR` and drop it into Tomcat's `webapps` folder and can access it from the browser. Thanks. – TheRealChx101 Jan 01 '18 at 21:01
2

The previously mentioned dependencies did not work for me. From the Jersey User guide:

Jersey provides two Servlet modules. The first module is the Jersey core Servlet module that provides the core Servlet integration support and is required in any Servlet 2.5 or higher container:

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

To support additional Servlet 3.x deployment modes and asynchronous JAX-RS resource programming model, an additional Jersey module is required:

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

The jersey-container-servlet module depends on jersey-container-servlet-core module, therefore when it is used, it is not necessary to explicitly declare the jersey-container-servlet-core dependency.

https://jersey.github.io/documentation/latest/deployment.html#deployment.servlet.3

bzak
  • 483
  • 4
  • 14
0

As @Eran-Medan pointed out, JBoss EAP 7.1 (note without a Web Application so no servlet, I was doing it in a EJB 3.2 project) I had to add the "value" attribute as such as I was getting an exception that the value attribute was required.

This worked for me

    @ApplicationPath(value="/*")
        public class MyApplication extends Application {

            private Set singletons = new HashSet();

            public MyApplication() {
                singletons.add(new MyService());
            }

            ...
    }

Stack Trace

    Caused by: java.lang.annotation.IncompleteAnnotationException: javax.ws.rs.ApplicationPath missing element value
        at sun.reflect.annotation.AnnotationInvocationHandler.invoke(AnnotationInvocationHandler.java:80)
        at com.sun.proxy.$Proxy141.value(Unknown Source)
        ... 21 more
JGlass
  • 1,427
  • 2
  • 12
  • 26