Andrew
Andrew

Reputation: 722

Swagger JAX-RS with Jersey 1.19 gives 'Conflicting URI templates' error

I am trying to add Swagger to an existing application that uses Jersey 1.19. For adding Swagger to the application, I have been following this guide: https://github.com/swagger-api/swagger-core/wiki/Swagger-Core-Jersey-1.X-Project-Setup-1.5.

When I deploy the application on Apache Tomcat, I get the following error:

SEVERE: Conflicting URI templates. The URI template / for root resource class io.swagger.jaxrs.listing.ApiListingResource and the URI template / transform to the same regular expression (/.*)?

The odd thing is that my Jersey servlet is not deployed at the root context, but at the /api/* context as shown in the web.xml file:

<servlet>
    <servlet-name>MyApp Service</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>app.MyApplication</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>MyApp Service</servlet-name>
    <url-pattern>/api/*</url-pattern>
</servlet-mapping>

My MyApplication class is defined below:

public class MyApplication extends Application {

private final Set<Object> singletons = new HashSet<Object>();
private final Set<Class<?>> classes = new HashSet<Class<?>>();

public MyApplication() {

    MyResource resource= new MyResource();

    singletons.addAll(Arrays.asList(resource));

    BeanConfig beanConfig = new BeanConfig();
    beanConfig.setBasePath("/api");
    beanConfig.setResourcePackage(getClass().getPackage().getName());
    beanConfig.setTitle("REST API");
    beanConfig.setVersion("1.0.0");
    beanConfig.setScan(true);

    classes.add(io.swagger.jaxrs.listing.ApiListingResource.class);
    classes.add(io.swagger.jaxrs.listing.SwaggerSerializers.class);
}

@Override
public Set<Class<?>> getClasses() {
    return classes;
}

@Override
public Set<Object> getSingletons() {
    return singletons;
}}

I have tried other configurations, such as defining the Swagger servlet in the web.xml file instead of using the BeanConfig, but the same error still occurs. I have gotten Swagger to work this way on a different project that uses Jersey 2, but unfortunately the current project has to remain on Jersey 1.19. Here is the Swagger dependency defined in the pom.xml file:

<dependency>
  <groupId>io.swagger</groupId>
  <artifactId>swagger-jersey-jaxrs</artifactId>
  <version>1.5.0</version>
</dependency>

Any help would be appreciated.

Upvotes: 1

Views: 2663

Answers (1)

botchniaque
botchniaque

Reputation: 5084

Update 2: Looks like version 1.5.8 of swagger-core fixes that issue. See this commit for details.


Update: Instead of adding Swagger resource as sub-resource it much easier to just override @Path mapping. See Solution 2 for details.


I was facing exactly the same problem. The cause of that is Swagger resource being mapped to root @Path("/") public class ApiListingResource.


Solution 1 - No concurring mappings

One simple and inflexible way around it, is not to define any resource mapping to root path @Path("/").


Solution 2 - Override @Path mapping

2.1 Override Swagger classes

ApiListingResource should get a new @Path mapping

package my.api.package.swagger

import javax.ws.rs.Path;

@Path("swagger")
public class ApiListingResource extends io.swagger.jaxrs.listing.ApiListingResource {}

SwaggerSerializers should get new package

package my.api.package.swagger;

import javax.ws.rs.ext.Provider;

@Provider
public class SwaggerSerializers extends io.swagger.jaxrs.listing.SwaggerSerializers {}

2.2 Configure overriden classes

Add my.api.package.swagger instead of io.swagger.jaxrs.listing in Swagger package config.


Solution 3 - Swagger resource as sub-resource

Other solution is to move Swagger to a different path, allowing your resources to be mapped anywhere you like. To achieve this you need to:

3.1 Remove ApiListingResource from provider classes.

if you subclass Application:

public MyApplication() {
    //init BeanConfig
    //add your resource classes

    //classes.add(io.swagger.jaxrs.listing.ApiListingResource.class);
    classes.add(io.swagger.jaxrs.listing.SwaggerSerializers.class);
}

if you configure via web.xml using com.sun.jersey.config.property.packages param:

<servlet>
    <servlet-name>your-rest-api</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>
            {your_application_packages},
            <!--io.swagger.jaxrs.json,-->
            <!--io.swagger.jaxrs.listing-->
        </param-value>
    </init-param>
    <init-param>
        <param-name>com.sun.jersey.config.property.classnames</param-name>
        <param-value>
            io.swagger.jaxrs.listing.SwaggerSerializers,
            io.swagger.jaxrs.json.JacksonJsonProvider
        </param-value>
    </init-param>
</servlet>

BTW, I have noticed that GF 3.1.2.2 with Jersey configured using <filter/> in web.xml does not work with Swagger due to Grizzly related bug.

3.2 Add ApiListingResources as a subresource to one of your resources

@Path("/") 
class RootResource {
    @Context ServletContext context;

    @Path("/swagger")
    public ApiListingResource swagger() {
        return new ApiListingSubResource(context);
    }
}

As ApiListingResource is now not managed by Jersey, it does not get ServletContext injected. To overcome this problem you have to pass it as a constructor parameter, and for that subclass ApiListingResource and provide proper constructor:

// context has 'default' visibility
// so we need to stay in the same package 
// to be able to access it
package io.swagger.jaxrs.listing;

import javax.servlet.ServletContext;

public class ApiListingSubResource extends ApiListingResource {
    public ApiListingSubResource(ServletContext sc) { this.context = sc; }
}

Now your Swagger descriptors will be under http://hostname/app-path/swagger/swagger.json and you will still be able to use the root resource.

It's a little bit longer way , but works! Hope that helps.

Upvotes: 2

Related Questions