Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Swagger-JAX-RS library breaks discovery of my ContextResolver<ObjectMapper> #862

Closed
unoexperto opened this issue Feb 9, 2015 · 29 comments

Comments

@unoexperto
Copy link

I have problem initializing Swagger using Jersey 2.

Here is how my web.xml looks like

<servlet>
    <servlet-name>javax.ws.rs.core.Application</servlet-name>
</servlet>
<servlet-mapping>
    <servlet-name>javax.ws.rs.core.Application</servlet-name>
    <url-pattern>/api/*</url-pattern>
</servlet-mapping>

On server start I get

[FATAL] A resource model has ambiguous (sub-)resource method for HTTP method GET and input mime-types as defined by"@Consumes" and "@Produces" annotations at Java methods public javax.ws.rs.core.Response com.wordnik.swagger.jaxrs.listing.ApiListingResource.resourceListing(javax.ws.rs.core.Application,javax.servlet.ServletConfig,javax.ws.rs.core.HttpHeaders,javax.ws.rs.core.UriInfo) and public javax.ws.rs.core.Response com.wordnik.swagger.jersey.listing.ApiListingResource.resourceListing(javax.ws.rs.core.Application,org.glassfish.jersey.servlet.WebConfig,javax.ws.rs.core.HttpHeaders,javax.ws.rs.core.UriInfo) at matching regular expression /api\-docs. These two methods produces and consumes exactly the same mime-types and therefore their invocation as a resource methods will always fail.; source='org.glassfish.jersey.server.model.RuntimeResource@3d13bf1a',

which expected considering there are two classes mapping to the same endpoint: com.wordnik.swagger.jersey.listing.ApiListingResource and com.wordnik.swagger.jaxrs.listing.ApiListingCache.

Why do we have jersey listener in jaxrs module jar ?
Is there way to make it work ?

I also tried using example from your tutorial which is

<servlet>
    <servlet-name>jersey</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.wordnik.swagger.jaxrs.json,
            com.mycompany.api2
        </param-value>
    </init-param>
    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>
            com.wordnik.swagger.jersey.listing.ApiListingResourceJSON,
            com.wordnik.swagger.jersey.listing.JerseyApiDeclarationProvider,
            com.wordnik.swagger.jersey.listing.JerseyResourceListingProvider
        </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

Sadly this version ignores my other provider located in com.mycompany.api2.serialization.

Thanks!

@webron
Copy link
Contributor

webron commented Feb 9, 2015

Which version of swagger-core do you use?

What does your Application class look like?

@unoexperto
Copy link
Author

Here is list of my dependencies

            "org.glassfish.jersey.containers" % "jersey-container-servlet" % "2.15",
            "org.glassfish.jersey.media" % "jersey-media-json-jackson" % "2.15",
            "com.fasterxml.jackson.core" % "jackson-databind" % "2.5.0",
            "com.fasterxml.jackson.core" % "jackson-core" % "2.5.0",
            "com.fasterxml.jackson.core" % "jackson-annotations" % "2.5.0",
            "com.wordnik" % "swagger-jersey2-jaxrs_2.11" % "1.3.12",

I don't have application class. Interestingly enough all my @Path classes work and serve content in both methods of initialization via web.xml.

@webron
Copy link
Contributor

webron commented Feb 9, 2015

Okay, that's interesting.

As for the second method, I suspect your provider isn't picked up because you didn't include it in the jersey.config.server.provider.classnames init-param value.

@unoexperto
Copy link
Author

Yeah, I thought that too and tried adding it after I posted this issue but strangely it didn't help. I'm investigating it right now.

As per Swagger I can't come up with working solution for now :(

@webron
Copy link
Contributor

webron commented Feb 9, 2015

I can come up with a solution, but you may not like it :) The set up guide in the wiki provides several alternatives, one of which would be to use the Application class, but that would require you to manually set to classes. With Jersey 2 you can also use the ResourceConfig which can help by only going over the packages instead of explicit classes.

@unoexperto
Copy link
Author

He he :) Could you please give me ResourceConfig version of the solution ?

By the way, why do you need com.wordnik:swagger-jaxrs_2.11 in com.wordnik:swagger-jersey2-jaxrs_2.11 ? I might try to exclude it in maven script.

UPDATE: Ah damn, it's because of com.wordnik.swagger.annotations...

@webron
Copy link
Contributor

webron commented Feb 9, 2015

the jersey2 module extends the jaxrs module.

Unfortunately, I don't have a specific usage example of ResourceConfig.

However, I think if you look both here and in the ResourceConfig section here you'd be able to combine the two into a working example. Basically, the packages(...) and register(...) methods replace the direct work done with the Application class.

@unoexperto
Copy link
Author

Could you decouple these two libraries instead so that there are no duplicating JAX-RS provides in the classpath ?

@webron
Copy link
Contributor

webron commented Feb 9, 2015

They use different packages, which is what is meant to isolate them. And at the moment, there won't be an easy way to decouple them.

@unoexperto
Copy link
Author

I see. So I'm still fighting with it here here is what I found.

  • I switched to com.wordnik:swagger-jaxrs_2.11
  • Use javax.ws.rs.core.Application method of initialization

If I have Swaggers providers in classpath my ContextResolver<ObjectMapper> never initialized. It seems like presense of Swagger breaks Jersey's autodiscovery mechanism.

@unoexperto
Copy link
Author

Sadly I couldn't figure out why it's happening. If you find root cause please update this issue.

I only found this. In your com.wordnik.swagger.jaxrs.json.JacksonJsonProvider you call super.setMapper(commonMapper). That sets non-null value to ProviderBase._mapperConfig._mapper. Later when http request handler attempts to serialize instance of my class call ends up in ProviderBase.locateMapper which has following body

    public MAPPER locateMapper(Class<?> type, MediaType mediaType)
    {
        // First: were we configured with a specific instance?
        MAPPER m = _mapperConfig.getConfiguredMapper();
        if (m == null) {
            // If not, maybe we can get one configured via context?
            m = _locateMapperViaProvider(type, mediaType);
            if (m == null) {
                // If not, let's get the fallback default instance
                m = _mapperConfig.getDefaultMapper();
            }
        }
        return m;
    }

in correct code-flow _mapperConfig.getConfiguredMapper() returns null which subsequently causes invocation of _locateMapperViaProvider which finds my custom mapper. With Swagger it defaults to com.fasterxml.jackson.jaxrs.json.JsonMapperConfigurator and my custom json serializers never get invoked.

I'll try to create small isolated sample project in few days that reproduces the issue and will post it on GitHub.

In the mean time I'll have to maintain API documentation manually :(

@unoexperto unoexperto changed the title Failure initialize Swagger with Jersey Adding Swagger-JAX-RS library breaks discovery of my ContextResolver<ObjectMapper> Feb 10, 2015
@unoexperto
Copy link
Author

@webron
Copy link
Contributor

webron commented Feb 10, 2015

Are you sure its docs are right? I get 404 for pretty much any URL.

@unoexperto
Copy link
Author

Oops. I accidentally left wrong webroot from my big project. Please update.

@webron
Copy link
Contributor

webron commented Feb 10, 2015

Okay, that works.

To be clear, you changed the issue being described here? Technically, even with that sample, you don't get proper API documentation.

@webron
Copy link
Contributor

webron commented Feb 10, 2015

Another thing, will you be able to try using 1.5.0-M1 instead? Testing it, it seems the API call that doesn't work with the older dependency works with the newer one.

@unoexperto
Copy link
Author

I changed description of the issue as I discovered real cause of it. Initially I thought problem lies in how I enable Jersey in web.xml.

Yep, I intentionally simplified example project. In my real project API documentation is working.

Yes, I tested 1.5.0-M1 as well. Web-service doesn't give error about missing serializer but returns empty json instead. ObjectMapperProvider is still not used.

@webron
Copy link
Contributor

webron commented Feb 10, 2015

Okay, that's at least some progress. @fehguy would have to jump in here now.

@unoexperto
Copy link
Author

Thank you, guys ! If we fix it I'll be tremendously happy.Let me know if I can help somehow.

@fehguy
Copy link
Contributor

fehguy commented Feb 10, 2015

will be looking in a bit.

@webron
Copy link
Contributor

webron commented Feb 10, 2015

@CPPExpert - well, if it makes you happy, it makes us happy.

@steowens
Copy link

I am running up against the same thing. Adding swagger breaks my perfectly good service thanks to it's dependencies on out dated JSR-311 implementations.

Caused by: java.lang.NoSuchMethodError: javax.ws.rs.core.Response.hasEntity()Z
at org.apache.cxf.jaxrs.utils.ExceptionUtils.convertFaultToResponse(ExceptionUtils.java:67)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.convertFaultToResponse(JAXRSUtils.java:1516)
at org.apache.cxf.jaxrs.JAXRSInvoker.handleFault(JAXRSInvoker.java:320)
at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:214)
at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:101)
at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:58)
at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:94)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272)
at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:241)
at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:248)
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:222)
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:153)
at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:171)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:286)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:206)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:262)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)

@webron
Copy link
Contributor

webron commented Feb 20, 2015

@steowens - I don't see how those two issues are related (the one you're talking about and the one the OP is). Do you also have your own ObjectMapper?

@fehguy
Copy link
Contributor

fehguy commented Feb 21, 2015

jersey (1) or jersey 2? Can you share your pom.xml?

@steowens
Copy link

I managed to fix the problem. In my case I am using JSR-339, and all of the features that it entails. Swagger pulls in a compile time dependency on JSR-311. By adding an exclude for the compile time dendency to the swagger dependency I was able to resolve my issue.

@fehguy
Copy link
Contributor

fehguy commented Feb 25, 2015

Thanks, I will keep this open to look at the dependencies.

@fehguy fehguy added this to the v1.5-M2 milestone Mar 4, 2015
@fehguy
Copy link
Contributor

fehguy commented Mar 8, 2015

@steowens you should not need to exclude the JSR-311 as it's already excluded by the swagger-jersey2-jaxrs artifact.

@fehguy
Copy link
Contributor

fehguy commented Mar 8, 2015

@CPPExpert I've been looking at this and have some thoughts. (kudos for being the first person I've seen using sbt for a non-play java project!)

  • Your build.scala has both the older, swagger 1.3.12 module as well as jackson 2.5.0. I don't believe that swagger version works with jackson 2.5 in scala. It may in java, but scala pulls in all kinds of stuff that can cause resolution & eviction issues.
  • Whether you use a custom object mapper or not, it shouldn't matter, meaning, you can have multiple object mappers live in your app at once. Swagger will use it's internal mapper for reading and deserializing. To write the swagger spec JSON, you can load com.wordnik.swagger.jaxrs.listing.SwaggerSerializers provider and it will write only the swagger spec.

I believe that the issue isn't swagger, but a library incompatibility and configuration settings. I've sent a PR for the following:

  • Upgrade to swagger-jersey-jaxrs-1.5.2-M1
  • Removed unused dependencies from build.scala
  • Enabled swagger in the web.xml
  • Removed the json object mapper from the web.xml packages
  • Added the swagger serializer which lets it use the mapper only for the swagger spec

I've sent a PR to your repo with the changes, Please check it out.

@unoexperto
Copy link
Author

Guys, I just tried switching to new 1.5.0

Sadly I can't have it get initialized now. I get following exception

{
  "error": {
    "type": "GenericException",
    "message": "NotFoundException: HTTP 404 Not Found",
    "stackTrace": "javax.ws.rs.NotFoundException: HTTP 404 Not Found\r\n\tat org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:305)\r\n\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)\r\n\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)\r\n\tat org.glassfish.jersey.internal.Errors.process(Errors.java:315)\r\n\tat org.glassfish.jersey.internal.Errors.process(Errors.java:297)\r\n\tat org.glassfish.jersey.internal.Errors.process(Errors.java:267)\r\n\tat org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)\r\n\tat org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:291)\r\n\tat org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1140)\r\n\tat org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:403)\r\n\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:386)\r\n\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:334)\r\n\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:221)\r\n\tat org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:751)\r\n\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1666)\r\n\tat org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:176)\r\n\tat org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:145)\r\n\tat org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:92)\r\n\tat org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:381)\r\n\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1636)\r\n\tat org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:564)\r\n\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)\r\n\tat org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:578)\r\n\tat org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:221)\r\n\tat org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1111)\r\n\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:498)\r\n\tat org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:183)\r\n\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1045)\r\n\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)\r\n\tat org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:199)\r\n\tat org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:109)\r\n\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:98)\r\n\tat org.eclipse.jetty.server.Server.handle(Server.java:461)\r\n\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:284)\r\n\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:244)\r\n\tat org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:534)\r\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607)\r\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:536)\r\n\tat java.lang.Thread.run(Thread.java:745)\r\n"
  }
}

Also I see in the debugger that this class io.swagger.jaxrs.listing.ApiListingResource is never invoked. Could it be regression of this bug #862 ?

My web.xml has this

    <servlet>
        <servlet-name>DefaultJaxrsConfig</servlet-name>
        <servlet-class>io.swagger.jaxrs.config.DefaultJaxrsConfig</servlet-class>
        <init-param>
            <param-name>api.version</param-name>
            <param-value>1.0.0</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>

and ResourceConfig has this

        packages("com.company.api2", "io.swagger.jaxrs.listing");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants