Standard JAX-RS uses an Application
as its configuration class. ResourceConfig
extends Application
.
There are three main ways (in a servlet container) to configure Jersey (JAX-RS):
- With only web.xml
- With both web.xml and an
Application/ResourceConfig
class
- With only an
Application/ResourceConfig
class annotated with @ApplicationPath
.
With only web.xml
It is possible to configure the application in a standard JAX-RS way, but the following is specific to Jersey
<web-app>
<servlet>
<servlet-name>jersey-servlet</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.mypackage.to.scan</param-value>
</init-param>
</servlet>
...
<servlet-mapping>
<servlet-name>jersey-servlet</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
...
</web-app>
Since Jersey runs in a servlet container, it is only right that the Jersey application runs as a servlet. The Jersey Servlet that handles incoming requests is the ServletContainer
. So here we declare it as the <servlet-class>
. We also configure an <init-param>
telling Jersey which package(s) to scan for our @Path
and @Provider
classes so it can register them.
Under the hood, Jersey will actually create a ResourceConfig
instance, as that's what it uses to configure the application. Then it will register all the classes that it discovers through the package scan.
With both web.xml and Application/ResourceConfig
If we want to programmatically configure our application with an Application
or ResourceConfig
subclass, we can do so with one change to the above web.xml. Instead of setting an init-param to scan for packages, we use an init-param to declare our Application/ResourceConfig
subclass.
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.example.JerseyApplication</param-value>
</init-param>
<servlet-mapping>
<servlet-name>jersey-servlet</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
</servlet>
Note: With Jersey 3.x (JakartaEE), you will be using the param-name
- jakarta.ws.rs.Application
.
package com.example;
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
packages("com.abc.jersey.services");
}
}
Here, we configure the init-param
javax.ws.rs.Application
with the fully qualified name of our ResourceConfig
subclass. And instead of using the init-param
that tells Jersey which package(s) to scan, we just use the convenience method packages()
of the ResourceConfig
.
We could also use the methods register()
and property()
to register resources and providers, and to configure Jersey properties. With the property()
method, anything that can be configured as an init-param
, can also be configured using the property()
method. For instance instead of calling packages()
, we could do
public JerseyApplication() {
property("jersey.config.server.provider.packages",
"com.mypackage.to.scan");
}
With only Application/ResourceConfig
Without a web.xml, Jersey needs a way for us to provide the servlet-mapping. We do this with the @ApplicationPath
annotation.
// 'services', '/services', or '/services/*'
// is all the same. Jersey will change it to be '/services/*'
@ApplicationPath("services")
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
packages("com.abc.jersey.services");
}
}
Here with the @ApplicationPath
, it's just like if we configured the servlet mapping in the web.xml
<servlet-mapping>
<servlet-name>JerseyApplication</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
When using only Java code for configuration, there needs to be some way for Jersey to discover our configuration class. This is done with the use of a ServletContanerInitializer
. This is something that was introduced in the Servlet 3.0 Specification, so we cannot use "Java only" configuration in earlier servlet containers.
Basically what happens is that the implementor of the initializer can tell the servlet container what classes to look for, and the servlet container will pass those classes to the initializer onStartup()
method. In Jersey's implementation of the initializer, Jersey configures it to look for Application
classes and classes annotated with @ApplicationPath
. See this post for further explanation. So when the servlet container starts the application, Jersey's initializer will get passed our Application/ResourceConfig
class.
What things can I do inside the subclass of ResourceConfig
Just look at the javadoc. Its mostly just registration of classes. Not much else you need to do with it. The main methods you will be using are the register()
, packages()
, and property()
methods. The register()
method lets you manually register classes and instances of resources and providers manually. The packages()
method, discussed earlier, lists the package(s) you want Jersey to scan for @Path
and @Provider
classes and register them for you. And the property()
method allows you to set some configurable properties 1.
The ResourceConfig
is just a convenience class. Remember, it extends Application
, so we could even use the standard Application
class
@ApplicationPath("/services")
public class JerseyApplication extends Application {
private final Set<Class<?>> classes;
private final Set<Object> singletons;
public JerseyApplication() {
// configure in constructor as Jersey
// may call the getXxx methods multiple times
this.classes = new HashSet<>();
this.classes.add(MyResource.class);
this.singletons = new HashSet<>();
this.singletons.add(new MyProvider());
}
@Override
public Set<Class<?>> getClasses() {
return this.classes;
}
@Override
public Set<Object> getSingletons() {
return this.singletons;
}
@Override
public Map<String, Object> getProperties() {
final Map<String, Object> properties = new HashMap<>();
properties.put("jersey.config.server.provider.packages",
"com.mypackage.to.scan");
return properties;
}
}
With a ResourceConfig
, we would just do
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
register(MyResource.class);
register(new MyProvider());
packages("com.mypackages.to.scan");
}
}
Aside from being more convenient, there are also a few thing under the hood that help Jersey configure the application.
An SE Environment
All the examples above assume you are running in an installed server environment, e.g. Tomcat. But you can also run the app in an SE environment, where you run an embedded server and start the app from a main
method. You will sometimes see these examples when searching around for info, so I want to show what that looks like, so that if you ever do come across this, you are not surprised and know how it differs from your setup.
So sometimes you will see an example like
ResourceConfig config = new ResourceConfig();
config.packages("com.my.package");
config.register(SomeFeature.class);
config.property(SOME_PROP, someValue);
What is most likely happening here is that the example is using an embedded server, like Grizzly. The rest of the code to start the server might be something like
public static void main(String[] args) {
ResourceConfig config = new ResourceConfig();
config.packages("com.my.package");
config.register(SomeFeature.class);
config.property(SOME_PROP, someValue);
String baseUri = "http://localhost:8080/api/";
HttpServer server = GrizzlyHttpServerFactory
.createHttpServer(URI.create(baseUri), config);
server.start();
}
So in this example, there is a standalone server being started and the ResourceConfig
is used to configure Jersey. The different here and from previous examples is that in this example, we are not extending the ResourceConfig
, but instead just instantiating it. It wouldn't be any different if we were to do
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("com.my.package");
register(SomeFeature.class);
property(SOME_PROP, someValue);
}
}
HttpServer server = GrizzlyHttpServerFactory
.createHttpServer(URI.create(baseUri), new JerseyConfig());
Say you were going through some tutorial and it showed a configuration for a standalone app where they instantiate the ResourceConfig
. But you are running your app in an installed servlet container and have been using the earlier configuration where you are extending the ResourceConfig
. Well now you know what the difference is and what changes you need to make. I've seen people do some really weird stuff because they didn't understand this difference. For example I saw someone instantiating a ResourceConfig
inside a resource class. So this is why I added this extra little piece; so you don't make the same mistake.
Footnotes
1. There are a number of different configurable properties. The link to the ServerProperties
are just some general properties. There are also different properties related to specific features. The documentation should mention these properties in the section of the docs related to that feature. For a complete list of all configurable properties, you can look at all the Jersey constants and look for the ones where the string value starts with jersey.config
. If you are using a web.xml, then you would use the string value as the init-param
param-name
. If you are using Java config (ResourceConfig
), then you would call property(ServerProperties.SOME_CONF, value)
jakarta.ws.rs.Application
asparam-name
! – Chancre