Multiple jersey servlets in one single web.xml

19,604

Solution 1

Yes you can specify two or more servlets into a web.xml . Remember to specify a different servlet-mapping for each one.

<servlet>
    <servlet-name>servletOne</servlet-name>
    <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <param-value>com.packageOne</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet>
    <servlet-name>servletTwo</servlet-name>
    <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <param-value>com.packageTwo</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>ServletOne</servlet-name>
    <url-pattern>/v1/*</url-pattern>
</servlet-mapping>
    <servlet-mapping>
    <servlet-name>ServletTwo</servlet-name>
<url-pattern>/v2/*</url-pattern>
</servlet-mapping>

the initParameter loadOnStartup defines the order in which the servlet are loaded (in this case first servletOne and then servletTwo).

Solution 2

The thing is that when you use Jersey and Spring together, the Jersey/Spring servlet goes through all available Spring beans and registers every resource and provider classes it will find among them.

If you have multiple Jersey/Spring servlets using the same (root) context and thus sharing bean definitions, then the procedure is performed for each such servlet and resource and provider class are registered several times.

In order to avoid multiple registration of the same bean, define such beans in a child context of a respective Jesrey/Spring servlet.

It is even not necessary to provide initialization parameters for declaring classes in the web.xml unless a mixture of Spring-managed and Jersey-managed classes is required.

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/root-context.xml</param-value>
</context-param>

...

<servlet>
    <servlet-name>REST-V1</servlet-name>
    <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/context-v1.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet>
    <servlet-name>REST-V2</servlet-name>
    <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/context-v1.xml /WEB-INF/context-v2.xml</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
</servlet>

...

Solution 3

One more possibility is to override one method in com.sun.jersey.spi.spring.container.servlet.SpringServlet. The initiate method looks like this (version 1.19.1):

@Override
protected void initiate(ResourceConfig rc, WebApplication wa) {
    try {
        wa.initiate(rc, new SpringComponentProviderFactory(rc, getContext()));
    } catch (RuntimeException e) {
        LOGGER.log(Level.SEVERE, "Exception occurred when intialization", e);
        throw e;
    }
}

If you change the code in a subclass like this, then you can filter out the unwanted spring beans based on your criteria (for example package name):

@Override
protected void initiate(ResourceConfig rc, WebApplication wa) {
    try {
        SpringComponentProviderFactory springComponentProviderFactory = new SpringComponentProviderFactory(rc, getContext());
        rc.getClasses().removeIf( clazz -> clazz.getPackage().getName().startsWith( "bla" ));
        wa.initiate(rc, springComponentProviderFactory);
    } catch (RuntimeException e) {
        LOGGER.log(Level.SEVERE, "Exception occurred when intialization", e);
        throw e;
    }
}

A bit hacky solution, but works for us perfectly.

Solution 4

I know this topic is old to answer. But my answer can help others.

We can configure multiple resource packages in web.xml with these delimiters :

  1. White space
  2. Comma (,)
  3. Semicolon (;)
  4. Nextline

Example :

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>MultiplePackageRest</display-name>
  <servlet>
    <servlet-name>JerseyMultiplePackage</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>info.javadoff.rest1,info.javadoff.rest2,...</param-value>
    </init-param>
      <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>JerseyMultiplePackage</servlet-name>
    <url-pattern>/api/*</url-pattern>
  </servlet-mapping>
</web-app>
Share:
19,604
Ben
Author by

Ben

Updated on July 18, 2022

Comments

  • Ben
    Ben almost 2 years

    Is it possible to have multiple jersey servlets in one single web.xml? I am trying to do the RESTfull versioning in this way:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
    
      <display-name>myapi</display-name>
    
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/context-v1.xml /WEB-INF/context-v2.xml</param-value>
      </context-param>
    
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    
      <servlet>
        <servlet-name>REST-V1</servlet-name>
        <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
        <init-param>
          <param-name>com.sun.jersey.config.property.packages</param-name>
          <param-value>com.myapi.rest.v1</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
    
      <servlet-mapping>
        <servlet-name>REST-V1</servlet-name>
        <url-pattern>/v1/*</url-pattern>
      </servlet-mapping>
    
      <servlet>
        <servlet-name>REST-V2</servlet-name>
        <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
        <init-param>
          <param-name>com.sun.jersey.config.property.packages</param-name>
          <param-value>com.myapi.rest.v2</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
      </servlet>
    
      <servlet-mapping>
        <servlet-name>REST-V2</servlet-name>
        <url-pattern>/v2/*</url-pattern>
      </servlet-mapping>
    
      <welcome-file-list>
        <welcome-file>index.html</welcome-file>
      </welcome-file-list>
    
    </web-app>
    

    But the spring context-v1 and context-v2 should be loaded separately? Because they have beans, which have the same name etc.

    EDIT:

    If you look in my console output it's loading the resources (admin/info) two times for each servlet:

    15.07.2012 14:47:08 com.sun.jersey.api.core.PackagesResourceConfig init
    INFO: Scanning for root resource and provider classes in the packages:
      com.myapi.rest.v1
    15.07.2012 14:47:08 com.sun.jersey.api.core.ScanningResourceConfig logClasses
    INFO: Root resource classes found:
      class com.myapi.rest.v1.LOAdminResource
      class com.myapi.rest.v1.LOInfoResource
    15.07.2012 14:47:08 com.sun.jersey.api.core.ScanningResourceConfig init
    INFO: No provider classes found.
    15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.servlet.SpringServlet getContext
    INFO: Using default applicationContext
    15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
    INFO: Registering Spring bean, adminResource_v2, of type com.myapi.rest.v2.LOAdminResource as a root resource class
    15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
    INFO: Registering Spring bean, infoResource_v2, of type com.myapi.rest.v2.LOInfoResource as a root resource class
    15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
    INFO: Registering Spring bean, adminResource_v1, of type com.myapi.rest.v1.LOAdminResource as a root resource class
    15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
    INFO: Registering Spring bean, infoResource_v1, of type com.myapi.rest.v1.LOInfoResource as a root resource class
    15.07.2012 14:47:09 com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
    INFO: Initiating Jersey application, version 'Jersey: 1.8 06/24/2011 12:17 PM'
    15.07.2012 14:47:09 com.sun.jersey.api.core.PackagesResourceConfig init
    INFO: Scanning for root resource and provider classes in the packages:
      com.myapi.rest.v2
    15.07.2012 14:47:09 com.sun.jersey.api.core.ScanningResourceConfig logClasses
    INFO: Root resource classes found:
      class com.myapi.rest.v2.LOAdminResource
      class com.myapi.rest.v2.LOInfoResource
    15.07.2012 14:47:09 com.sun.jersey.api.core.ScanningResourceConfig init
    INFO: No provider classes found.
    15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.servlet.SpringServlet getContext
    INFO: Using default applicationContext
    15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
    INFO: Registering Spring bean, adminResource_v2, of type com.myapi.rest.v2.LOAdminResource as a root resource class
    15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
    INFO: Registering Spring bean, infoResource_v2, of type com.myapi.rest.v2.LOInfoResource as a root resource class
    15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
    INFO: Registering Spring bean, adminResource_v1, of type com.myapi.rest.v1.LOAdminResource as a root resource class
    15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
    INFO: Registering Spring bean, infoResource_v1, of type com.myapi.rest.v1.LOInfoResource as a root resource class
    15.07.2012 14:47:09 com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
    INFO: Initiating Jersey application, version 'Jersey: 1.8 06/24/2011 12:17 PM'
    
  • Ben
    Ben almost 12 years
    thats true, both servlets are loaded.. but take a look at my console output. my problem is that my resources (AdminResource, InfoResource) are loaded two times for each servlet...
  • Giorgio
    Giorgio almost 12 years
    mmm... strange since u've defined two different packages... in alternative you could try to define the Server and Servlet programmatically (as the code defined here stackoverflow.com/questions/11210734/… under Start Server Android Activity)
  • Aliaksandr Arashkevich
    Aliaksandr Arashkevich over 9 years
    This is not the correct answer! Spring will load every annotated class it finds in the main context no matter what you put here. So you will have two servlets that will serve up the exact same stuff. Beware and try it! The correct answer is above...
  • manikanta
    manikanta over 8 years
    This should be the selected answer.