Configuring Swagger with Jersey

2,324 views
Skip to first unread message

er...@fulminatus.com

unread,
May 6, 2014, 3:41:43 PM5/6/14
to swagger-sw...@googlegroups.com
I'm trying to get swagger running on my Jersey web application and not having much luck. I'm running Jersey 2.4 and swagger-jersey2-jaxrs_2.10 version 1.3.5.
I read the quickstart and tried to follow it, but I'm configuring Jersey through a ResourceConfig instance, not web.xml. This is where I am so far:

web.xml:
    <servlet>
        <servlet-name>JerseyJaxrsConfig</servlet-name>
        <servlet-class>com.wordnik.swagger.jersey.config.JerseyJaxrsConfig</servlet-class>
        <init-param>
            <param-name>api.version</param-name>
            <param-value>0.01</param-value>
        </init-param>
        <init-param>
            <param-name>swagger.api.basepath</param-name>
            <param-value>http://localhost:8002/</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>

my resource config:
   public MyResourceConfig() {
        ...
        this.registerClasses(MyResource.class);
        ...
        final Map<String, Object> properties = new HashMap<String, Object>();
        properties.put(ServerProperties.PROVIDER_PACKAGES, "com.wordnik.swagger.jersey.listing");
        ...
        this.addProperties(properties);
    }


My application works correctly, but localhost:8080/api-docs returns a 404. The only swagger-related item in the log files is:
14:56:02.251 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] DEBUG c.w.s.jaxrs.config.WebXMLReader - set api.version to 0.01
14:56:02.251 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] DEBUG c.w.s.jaxrs.config.WebXMLReader - set swagger.api.basepath to http://localhost:8002/


Can anybody provide any insight into what I'm doing wrong?

Thanks,
Eric

tony tam

unread,
May 6, 2014, 4:35:18 PM5/6/14
to swagger-sw...@googlegroups.com
What is the root context of your app?  the api-docs will be mounted on that.  If you're deploying to webapps/ROOT, you should find your api-docs under /api-docs.  If you have configured jersey to listen to /api, it would be under /api/api-docs.  So you need to share some more of your configuration before we can say for sure.

er...@fulminatus.com

unread,
May 7, 2014, 9:42:27 AM5/7/14
to swagger-sw...@googlegroups.com
Tony,
  Thanks for answering!

  I deploy to {$catalina.home}/webapps. My application name is eim-query. My API is deployed to /*. So I access a top-level API resource through http://localhost:8080/eim-query/queries. I should then expect to see the swagger-generated documentation at http://localhost:8080/eim-query/api-docs, right? There's nothing there. Also, I think it's wrong that there's nothing showing up in the log file - shouldn't swagger be logging *something* on startup?

Eric

er...@fulminatus.com

unread,
May 7, 2014, 9:44:01 AM5/7/14
to swagger-sw...@googlegroups.com
Sorry, when I say "There's nothing there", what I mean is that I get a 404 at that url.

Ron

unread,
May 7, 2014, 10:06:15 AM5/7/14
to swagger-sw...@googlegroups.com
Based on what you described, the path should indeed be http://localhost:8080/eim-query/api-docs. Also, based on the logs you showed, it looks like Swagger does *something* and you should at least get an empty reply.
Can you please update the swagger.api.basepath parameter to "http://localhost:8080/eim-query"?
Also, can you please the full artifact you use for swagger from your pom.xml?


--
You received this message because you are subscribed to the Google Groups "Swagger" group.
To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

er...@fulminatus.com

unread,
May 7, 2014, 10:37:37 AM5/7/14
to swagger-sw...@googlegroups.com
Thanks for helping out, Ron. I updated the basepath to what you suggested, but I still see a 404. :-( I've included my pom dependencies, web.xml, and the jersey stack trace below.

pom.xml:
        ...
        <!-- Jersey -->
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-server</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-jackson</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.ext</groupId>
            <artifactId>project</artifactId>
            <version>2.4</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-client</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.test-framework</groupId>
            <artifactId>jersey-test-framework-core</artifactId>
            <version>2.4</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
            <version>2.4</version>
            <scope>test</scope>
        </dependency>

        <!-- Swagger -->
        <dependency>
            <groupId>com.wordnik</groupId>
            <artifactId>swagger-jersey2-jaxrs_2.10</artifactId>
            <version>1.3.5</version>
        </dependency>

        ...

web.xml:
    ...
    <servlet>
        <servlet-name>API</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>path.to.MyResourceConfig</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>API</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>JerseyJaxrsConfig</servlet-name>
        <servlet-class>com.wordnik.swagger.jersey.config.JerseyJaxrsConfig</servlet-class>
        <init-param>
            <param-name>api.version</param-name>
            <param-value>0.01</param-value>
        </init-param>
        <init-param>
            <param-name>swagger.api.basepath</param-name>
            <param-value>http://localhost:8080/eim-query</param-value>

        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>

    ...

Trace:
10:30:38.236 [http-8080-2] DEBUG org.glassfish.jersey.tracing.general - START baseUri=[http://localhost:8080/eim-query/] requestUri=[http://localhost:8080/eim-query/api-docs] method=[GET] authScheme=[n/a] accept=[text/html, application/xhtml+xml, */*] accept-encoding=[gzip, deflate] accept-charset=n/a accept-language=[en-US] content-type=n/a content-length=n/a  [ ---- ms]
10:30:38.236 [http-8080-2] TRACE org.glassfish.jersey.tracing.general - START_HEADERS Other request headers: user-agent=[Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)] host=[localhost:8080] dnt=[1] connection=[Keep-Alive]  [ ---- ms]
10:30:38.237 [http-8080-2] INFO  o.g.jersey.filter.LoggingFilter - 2 * LoggingFilter - Request received on thread http-8080-2
2 > GET http://localhost:8080/eim-query/api-docs
2 > accept: text/html, application/xhtml+xml, */*
2 > accept-language: en-US
2 > user-agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)
2 > accept-encoding: gzip, deflate
2 > host: localhost:8080
2 > dnt: 1
2 > connection: Keep-Alive

10:30:38.238 [http-8080-2] DEBUG org.glassfish.jersey.tracing.general - PRE_MATCH Filter by [org.glassfish.jersey.filter.LoggingFilter @1c88a970 #-2147483648] [ 0.11 ms]
10:30:38.238 [http-8080-2] DEBUG org.glassfish.jersey.tracing.general - PRE_MATCH_SUMMARY PreMatchRequest summary: 1 filters [ 0.28 ms]
10:30:38.238 [http-8080-2] DEBUG org.glassfish.jersey.tracing.general - MATCH_PATH_FIND Matching path [/api-docs] [ ---- ms]
10:30:38.238 [http-8080-2] TRACE org.glassfish.jersey.tracing.general - MATCH_PATH_NOT_MATCHED Pattern [/application\.wadl(/)?] is NOT matched [ ---- ms]
10:30:38.238 [http-8080-2] TRACE org.glassfish.jersey.tracing.general - MATCH_PATH_NOT_MATCHED Pattern [/application\.wadl(/.*)?] is NOT matched [ ---- ms]
10:30:38.238 [http-8080-2] TRACE org.glassfish.jersey.tracing.general - MATCH_PATH_NOT_MATCHED Pattern [/queries(/)?] is NOT matched [ ---- ms]
10:30:38.238 [http-8080-2] DEBUG org.glassfish.jersey.tracing.general - MATCH_SUMMARY RequestMatching summary [ 0.51 ms]
10:30:38.239 [http-8080-2] DEBUG org.glassfish.jersey.tracing.general - EXCEPTION_MAPPING Exception mapper [path.to.RuntimeExceptionMapper @6a1e5cf0] maps [javax.ws.rs.NotFoundException @53371566 <404/CLIENT_ERROR|Not Found|-no-entity->] ('HTTP 404 Not Found') to <404/CLIENT_ERROR|Not Found> [ 0.02 ms]
10:30:38.239 [http-8080-2] INFO  o.g.jersey.filter.LoggingFilter - 2 * LoggingFilter - Response received on thread http-8080-2
2 < 404

10:30:38.239 [http-8080-2] DEBUG org.glassfish.jersey.tracing.general - RESPONSE_FILTER Filter by [org.glassfish.jersey.filter.LoggingFilter @7897aaa6 #-2147483648] [ 0.07 ms]
10:30:38.239 [http-8080-2] DEBUG org.glassfish.jersey.tracing.general - RESPONSE_FILTER_SUMMARY Response summary: 1 filters [ 0.20 ms]
10:30:38.239 [http-8080-2] DEBUG org.glassfish.jersey.tracing.general - FINISHED Response status: 404/CLIENT_ERROR|Not Found [ ---- ms]
10:30:38.242 [http-8080-2] TRACE o.g.j.process.internal.RequestScope - [DEBUG] Released scope instance Instance{id=b74bc5bd-7839-48e1-92c0-cde382b0b4bc, referenceCounter=0, store size=0} on thread http-8080-2
To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggersocket+unsub...@googlegroups.com.

Ron

unread,
May 7, 2014, 10:39:44 AM5/7/14
to swagger-sw...@googlegroups.com
I didn't expect it to help, just wanted to fix it for clarity.

Can you change your dependency to this and try again?


        <dependency>
            <groupId>com.wordnik</groupId>
            <artifactId>swagger-jersey2-jaxrs_2.10</artifactId>
            <scope>compile</scope>
            <version>1.3.5</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.ws.rs</groupId>
                    <artifactId>jsr311-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>


To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.

er...@fulminatus.com

unread,
May 7, 2014, 11:09:58 AM5/7/14
to swagger-sw...@googlegroups.com
No change. :-(
To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggersocket+unsubscri...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

tony tam

unread,
May 7, 2014, 11:19:19 AM5/7/14
to swagger-sw...@googlegroups.com
Something needs to trigger the Swagger Scanner, and you also need to add a resource.  Can you share what your configuration looks like, if you're not using web.xml?
To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggersocket+unsubscri...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

er...@fulminatus.com

unread,
May 7, 2014, 11:31:20 AM5/7/14
to swagger-sw...@googlegroups.com
Tony,
  What resource do I need to add?



    public MyResourceConfig() {
        super();

        this.registerClasses(
                JacksonFeature.class,
                JsonFactoryResolver.class);
        this.registerClasses(
                QueryResource.class);  // This is my resource. Calls to it work.
        this.registerClasses(
                RuntimeExceptionMapper.class);
        this.registerClasses(
                LoggingFilter.class);


        final Map<String, Object> properties = new HashMap<String, Object>();
        properties.put("com.sun.jersey.api.json.POJOMappingFeature", Boolean.TRUE);
        properties.put(ServerProperties.TRACING, "ALL");
        properties.put(ServerProperties.TRACING_THRESHOLD, "VERBOSE");
        properties.put(ServerProperties.PROVIDER_PACKAGES, "com.wordnik.swagger.jersey.listing");
        this.addProperties(properties);
    }

tony tam

unread,
May 7, 2014, 11:35:16 AM5/7/14
to swagger-sw...@googlegroups.com
I see a couple things.

First, you need to register two @Provider resources.  I believe you are doing that in your ServerProperties.PROVIDER_PACKAGES

Next, you need to add the swagger resource listing path.   I don't think the PROVIDER_PACKAGES will cover this--I'm not sure you you're telling Jersey to add your actual API resources.  You'll need to include the swagger resource at com.wordnik.swagger.jersey.listing.ApiListingResourceJSON

Finally, you need to initialize a scanner.  Since you're not using web.xml, use the BeanConfig:

// in your setupSwaggerContextHandler()
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion( "1.0.2" );
beanConfig.setResourcePackage( "com.company.swagger.domain.rest" ); // replace with your packages
beanConfig.setBasePath( "http://localhost:8080/myApp/resources/" );
beanConfig.setDescription( "My RESTful resources" );
beanConfig.setTitle( "My RESTful API" );
beanConfig.setScan( true );

Then you should be set

er...@fulminatus.com

unread,
May 7, 2014, 12:04:16 PM5/7/14
to swagger-sw...@googlegroups.com
I now have an error on startup and a 500 when I hit the page:


    public MyResourceConfig() {
        super();

        this.registerClasses(
                JacksonFeature.class,
                JsonFactoryResolver.class);
        this.registerClasses(
                QueryResource.class);
        this.registerClasses(
                RuntimeExceptionMapper.class);
        this.registerClasses(
                LoggingFilter.class);
        this.registerClasses(
                ApiListingResourceJSON.class);


        final Map<String, Object> properties = new HashMap<String, Object>();
        properties.put("com.sun.jersey.api.json.POJOMappingFeature", Boolean.TRUE);
        properties.put(ServerProperties.TRACING, "ALL");
        properties.put(ServerProperties.TRACING_THRESHOLD, "VERBOSE");
        properties.put(ServerProperties.PROVIDER_PACKAGES, "com.wordnik.swagger.jersey.listing");
        this.addProperties(properties);

        final BeanConfig beanConfig = new BeanConfig();
        beanConfig.setVersion("1.0");
        beanConfig.setResourcePackage("my.rest.package");
        beanConfig.setBasePath("http://localhost:8080/eim-query");

        beanConfig.setDescription("My RESTful resources");
        beanConfig.setTitle("My RESTful API");
        beanConfig.setScan(true);
    }

Startup:
11:58:51.525 [main] INFO  o.g.j.s.i.scanning.JarFileScanner - Unable to read the next jar entry.
java.io.IOException: Stream closed
    at java.util.zip.ZipInputStream.ensureOpen(ZipInputStream.java:52) ~[na:1.6.0_31]
    at java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:76) ~[na:1.6.0_31]
    at java.util.jar.JarInputStream.getNextEntry(JarInputStream.java:130) ~[na:1.6.0_31]
    at java.util.jar.JarInputStream.getNextJarEntry(JarInputStream.java:167) ~[na:1.6.0_31]
    at org.glassfish.jersey.server.internal.scanning.JarFileScanner.hasNext(JarFileScanner.java:87) ~[jersey-server-2.4.jar:na]

Runtime:
org.codehaus.jackson.map.JsonMappingException: No serializer found for class com.wordnik.swagger.model.ResourceListing and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) )
    at org.codehaus.jackson.map.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:52) ~[jackson-mapper-asl-1.9.11.jar:1.9.11]
    at org.codehaus.jackson.map.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:25) ~[jackson-mapper-asl-1.9.11.jar:1.9.11]
    at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:610) ~[jackson-mapper-asl-1.9.11.jar:1.9.11]
    at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:256) ~[jackson-mapper-asl-1.9.11.jar:1.9.11]
    at org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:1613) ~[jackson-mapper-asl-1.9.11.jar:1.9.11]
    at org.codehaus.jackson.jaxrs.JacksonJsonProvider.writeTo(JacksonJsonProvider.java:559) ~[jackson-jaxrs-1.9.11.jar:1.9.11]

tony tam

unread,
May 7, 2014, 12:12:24 PM5/7/14
to swagger-sw...@googlegroups.com
Great, we're getting closer.  That means the @Providers are note being loaded:

com.wordnik.swagger.jersey.listing.JerseyApiDeclarationProvider
com.wordnik.swagger.jersey.listing.JerseyResourceListingProvider

Those are responsible for serializing the ResourceListing class into JSON.

er...@fulminatus.com

unread,
May 7, 2014, 12:25:54 PM5/7/14
to swagger-sw...@googlegroups.com
<happy-dance>Okay, I now have swagger-core working. </happy-dance>
Ron, Tony, thank you so much for your help. You were both fabulous.

Two questions:
0. I'm still seeing that first exception on startup, the JarFileScanner one. Do you know what that's about?
1. In addition to JSON, I'd like to get the pretty UI working. Is there a tutorial I can visit to show me how to go about that?

Thanks,
Eric

Ron

unread,
May 7, 2014, 12:29:36 PM5/7/14
to swagger-sw...@googlegroups.com
0. That's an odd one. I'd try running mvn clean package -U (notice the -U) and see if that helps (possibly even delete the .m2 dir though that would start the 'Downloading the Internet (TM)' process of maven).
1. Fairly simple - clone https://github.com/wordnik/swagger-ui, go to the dist dir, open index.html and point it towards your api-docs file.


To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.

er...@fulminatus.com

unread,
May 7, 2014, 12:32:00 PM5/7/14
to swagger-sw...@googlegroups.com
Curious. I pulled

        properties.put(ServerProperties.PROVIDER_PACKAGES, "com.wordnik.swagger.jersey.listing");


since I'd added the classes you mentioned explicitly via

        this.registerClasses(
                ApiListingResourceJSON.class,
                JerseyApiDeclarationProvider.class,
                JerseyResourceListingProvider.class);


That took care of the startup error. I wonder why. <shrug/> I'll worry about it if and when I need to move to directory scanning.

er...@fulminatus.com

unread,
May 7, 2014, 1:00:13 PM5/7/14
to swagger-sw...@googlegroups.com
I changed
  <script type="text/javascript">
    $(function () {
      window.swaggerUi = new SwaggerUi({
      url: "http://localhost:8080/eim-query/api-docs",


but I'm seeing:

Can't read from server. It may not have the appropriate access-control-origin settings.

Could this be a CORS issue, even though everything is on the same machine? That's all I'm seeing on the internet about this error. Seems odd, though.

Eric

Ron

unread,
May 7, 2014, 1:09:14 PM5/7/14
to swagger-sw...@googlegroups.com
Yes, you need to enable CORS in your application. It's not enough to be on the same machine, they need to be in the same application if you don't want to enable CORS.
You can follow the guidelines here - https://github.com/wordnik/swagger-core/wiki/CORS.


To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.

er...@fulminatus.com

unread,
May 7, 2014, 1:56:02 PM5/7/14
to swagger-sw...@googlegroups.com
Putting them in the same application is the preferred state, so I'll skip CORS. My initial effort met with failure, because I have my API running directly off of the context root. The jersey servlet consumes /*, which of course means it tries to handle swagger-ui's index.html. I know that's not a problem with swagger, but is there a known good solution? Or am I forced to drop my jersey servlet down a level?

Eric

Ron

unread,
May 7, 2014, 2:04:12 PM5/7/14
to swagger-sw...@googlegroups.com

I'm no expert when it comes to mapping configurations but I'm fairly certain you can add inclusions to files and/or directories. Try searching for that, I'll try looking for it a bit later if you don't find how.

To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.

er...@fulminatus.com

unread,
May 7, 2014, 2:50:43 PM5/7/14
to swagger-sw...@googlegroups.com
It appears there's a solution that bootstraps jersey as a filter, but when I do that, api-docs disappears (404, no errors in log file, no indication jersey is looking for the URI):

web.xml:
<!--
    <servlet>
        <servlet-name>API</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.locustec.eim.query.rest.QueryResourceConfig</param-value>

        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>API</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
-->


    <filter>
        <filter-name>API</filter-name>
        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.locustec.eim.query.rest.QueryResourceConfig</param-value>
        </init-param>
        <init-param>
            <param-name>jersey.config.servlet.filter.staticContentRegex</param-name>
            <param-value>/(images/.*|css/.*|index\.html|.*\.js)</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>API</filter-name>
        <url-pattern>/</url-pattern>
    </filter-mapping>

er...@fulminatus.com

unread,
May 7, 2014, 3:00:03 PM5/7/14
to swagger-sw...@googlegroups.com
And it appears that changing / to /* makes all the difference. Okay, I'm going to let this thread die. Thanks again, guys. You've been *super* helpful.

Eric
...
Reply all
Reply to author
Forward
0 new messages