Reputation: 722
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
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
.
One simple and inflexible way around it, is not to define any resource mapping to root path @Path("/")
.
@Path
mappingApiListingResource
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 {}
Add my.api.package.swagger
instead of io.swagger.jaxrs.listing
in Swagger package config.
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:
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.
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