swagger-api / swagger-core

Examples and server integrations for generating the Swagger API Specification, which enables easy access to your REST API
http://swagger.io
Apache License 2.0
7.36k stars 2.17k forks source link

Missing dependency `WebConfig` or `ServletConfig` with Jersey+Grizzly2+Swagger? #226

Closed dhalperi closed 11 years ago

dhalperi commented 11 years ago

I have a very straightforward use-case, following the Swagger JAX-RS example and anything I could find in 48 hours of Googling.

Here's the embedded Grizzly2 server:

URI baseUri = UriBuilder.fromUri("http://0.0.0.0/").port(port).build();
ResourceConfig masterApplication = new MasterApplication(); // This extends PackagesResourceConfig
webServer = GrizzlyServerFactory.createHttpServer(baseUri, masterApplication);

Here's the Swagger ApiListing:

import com.wordnik.swagger.jersey.listing.ApiListing;

@Path("/resources.json")
@Api("/resources")
@Produces(MediaType.APPLICATION_JSON)
public class MyriaApiListing extends ApiListing {
}

And here's the missing dependency error:

Jun 20, 2013 3:09:54 PM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
  SEVERE: Missing dependency for method public javax.ws.rs.core.Response com.wordnik.swagger.jersey.listing.ApiListing.apiListing(java.lang.String,javax.ws.rs.core.Application,com.sun.jersey.spi.container.servlet.WebConfig,javax.ws.rs.core.HttpHeaders,javax.ws.rs.core.UriInfo) at parameter at index 2
  SEVERE: Method, public javax.ws.rs.core.Response com.wordnik.swagger.jersey.listing.ApiListing.apiListing(java.lang.String,javax.ws.rs.core.Application,com.sun.jersey.spi.container.servlet.WebConfig,javax.ws.rs.core.HttpHeaders,javax.ws.rs.core.UriInfo), annotated with GET of resource, class com.wordnik.swagger.jersey.listing.ApiListing, is not recognized as valid resource method.
  SEVERE: Missing dependency for method public javax.ws.rs.core.Response com.wordnik.swagger.jersey.listing.ApiListing.resourceListing(javax.ws.rs.core.Application,com.sun.jersey.spi.container.servlet.WebConfig,javax.ws.rs.core.HttpHeaders,javax.ws.rs.core.UriInfo) at parameter at index 1
  SEVERE: Method, public javax.ws.rs.core.Response com.wordnik.swagger.jersey.listing.ApiListing.resourceListing(javax.ws.rs.core.Application,com.sun.jersey.spi.container.servlet.WebConfig,javax.ws.rs.core.HttpHeaders,javax.ws.rs.core.UriInfo), annotated with GET of resource, class com.wordnik.swagger.jersey.listing.ApiListing, is not recognized as valid resource method.
Exception in thread "main" com.sun.jersey.spi.inject.Errors$ErrorMessagesException
    at com.sun.jersey.spi.inject.Errors.processErrorMessages(Errors.java:170)
    at com.sun.jersey.spi.inject.Errors.postProcess(Errors.java:136)
    at com.sun.jersey.spi.inject.Errors.processWithErrors(Errors.java:199)
    at com.sun.jersey.server.impl.application.WebApplicationImpl.initiate(WebApplicationImpl.java:770)
    at com.sun.jersey.api.container.ContainerFactory.createContainer(ContainerFactory.java:172)
    at com.sun.jersey.api.container.ContainerFactory.createContainer(ContainerFactory.java:134)
    at com.sun.jersey.api.container.grizzly2.GrizzlyServerFactory.createHttpServer(GrizzlyServerFactory.java:242)
    at edu.washington.escience.myriad.api.MasterApiServer.<init>(MasterApiServer.java:43)
    at edu.washington.escience.myriad.daemon.MasterDaemon.<init>(MasterDaemon.java:48)
    at edu.washington.escience.myriad.daemon.MasterDaemon.main(MasterDaemon.java:30)

I've tried this with swagger-jaxrs and swagger-jersey-jaxrs, and that just makes the error message flip between ServletConfig and WebConfig.

Various issues on GitHub (#146, #136, #64) appear to reference this problem or problems like it, and seem to be merged with the code base, but I still have the issue.

Any advice?

Thanks! Dan

fehguy commented 11 years ago

Hi, I suggest you take 1.3.0-SNAPSHOT from this branch:

https://github.com/wordnik/swagger-core/tree/develop-1.3

which has updated examples of running swagger without any web.xml, ServletConfig or WebConfig dependencies. Relevant samples:

https://github.com/wordnik/swagger-core/tree/develop-1.3/samples/java-jaxrs

Note the configuration is set in bootstrap via web.xml

https://github.com/wordnik/swagger-core/tree/develop-1.3/samples/java-jaxrs-cxf

No web.xml configuration is done

fehguy commented 11 years ago

Please reopen if you have trouble with the 1.3.0-snapshot

dhalperi commented 11 years ago

Thanks, will do; I'm at conference so probably won't get to until Friday. Will update here then!

dhalperi commented 11 years ago

Hi @fehguy,

I just cloned the branch and ran mvn compile, and I get these compile errors. This makes me think that the branch is not in a good state:

[ERROR] /Users/dhalperi/escience/swagger-dev/swagger-core/modules/swagger-jaxrs/src/main/scala/com/wordnik/swagger/jaxrs/JaxrsApiReader.scala:267: error: type mismatch;
[ERROR]  found   : com.wordnik.swagger.model.ApiListing
[ERROR]  required: com.wordnik.swagger.jaxrs.ApiListing
[ERROR]       Some(ApiListing (
[ERROR]                       ^
[ERROR] /Users/dhalperi/escience/swagger-dev/swagger-core/modules/swagger-jaxrs/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala:40: error: type mismatch;
[ERROR]  found   : scala.collection.immutable.Map[String,com.wordnik.swagger.model.ApiListing]
[ERROR]  required: Map[String,com.wordnik.swagger.jaxrs.listing.ApiListing]
[ERROR]           _cache = Some((listings.map(m => (m.resourcePath, m))).toMap)
[ERROR]                                                                  ^
[ERROR] /Users/dhalperi/escience/swagger-dev/swagger-core/modules/swagger-jaxrs/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala:61: error: type mismatch;
[ERROR]  found   : com.wordnik.swagger.jaxrs.listing.ApiListing
[ERROR]  required: com.wordnik.swagger.model.ApiListing
[ERROR]         yield f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
[ERROR]                        ^
[ERROR] /Users/dhalperi/escience/swagger-dev/swagger-core/modules/swagger-jaxrs/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala:65: error: value resourcePath is not a member of Nothing
[ERROR]       ApiListingReference(listing.resourcePath, listing.description)
[ERROR]                                   ^
[ERROR] /Users/dhalperi/escience/swagger-dev/swagger-core/modules/swagger-jaxrs/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala:97: error: type mismatch;
[ERROR]  found   : com.wordnik.swagger.jaxrs.listing.ApiListing
[ERROR]  required: com.wordnik.swagger.model.ApiListing
[ERROR]         f.filter(spec, FilterFactory.filter, paramsToMap(uriInfo.getQueryParameters), cookiesToMap(headers), headersToMap(headers))
[ERROR]                  ^
[ERROR] 5 errors found
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] wordnik-swagger-project ........................... SUCCESS [1.970s]
[INFO] swagger-annotations ............................... SUCCESS [1.409s]
[INFO] swagger-core ...................................... SUCCESS [0.450s]
[INFO] swagger-jaxrs ..................................... FAILURE [15.696s]
[INFO] swagger-jersey-jaxrs .............................. SKIPPED
[INFO] swagger-servlet ................................... SKIPPED
[INFO] swagger-java-jaxrs-app ............................ SKIPPED
[INFO] swagger-scala-sample-app .......................... SKIPPED
[INFO] swagger-java-resteasy ............................. SKIPPED
[INFO] swagger-scala-servlet-server ...................... SKIPPED
[INFO] swagger-java-cxf-sample ........................... SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 20.330s
[INFO] Finished at: Mon Jul 01 14:36:18 PDT 2013
[INFO] Final Memory: 22M/222M
[INFO] ------------------------------------------------------------------------
dhalperi commented 11 years ago

P.S. I don't think I can reopen.

fehguy commented 11 years ago

Can you please run mvn clean install

I doubt that is it, but Travis-ci seems to think the build is clean.

dhalperi commented 11 years ago

yep, mvn clean install seemed to work. I tried again git reset --hard HEAD && mvn compile and that did not. Odd. Thanks, and I'll report back.

On Mon, Jul 1, 2013 at 2:56 PM, Tony Tam notifications@github.com wrote:

Can you please run mvn clean install

I doubt that is it, but Travis-ci seems to think the build is clean.

— Reply to this email directly or view it on GitHubhttps://github.com/wordnik/swagger-core/issues/226#issuecomment-20313501 .

fehguy commented 11 years ago

Great. There were some classes renamed that confused the compiler, I'm guessing

4lejandrito commented 11 years ago

Hi, I am having the same problem and mvn clean install does not work for me :(, any idea?

fehguy commented 11 years ago

Are you building at the top level? If not, press try that.

4lejandrito commented 11 years ago

Sorry, it is not exactly the same problem. I get this:

Tests in error: should honor a path param target at the class level(JavaPathParamTargetTest): 1 was not equal to 2`

I even tried to remove my .m2 folder but it remains the same. Maybe I should open a new issue...

fehguy commented 11 years ago

I suggest seeing what's wrong with your build--the tests are all passing so there must be a local modification or something leftover in your target folder?

https://travis-ci.org/wordnik/swagger-core

4lejandrito commented 11 years ago

rm -rf swagger-core git clone https://github.com/wordnik/swagger-core.git cd swagger-core git checkout develop-1.3 mvn clean install

Still the same error. It is a bit strange.

fehguy commented 11 years ago

it does seem like a localized problem for you, I just did a fresh clone on a new machine and got a passing build.

fehguy commented 11 years ago

I've also deployed the latest to sonatype snapshots, so you can pull from there.

dhalperi commented 11 years ago

Just for the record, @fehguy and @4lejandrito , I was able to successfully compile and test out the code in develop-1.3 branch. Here's what I did, starting with a fully clean checkout.

I'm using Mac OS 10.8.4, with the latest Java 7 (java version "1.7.0_21") and Maven version (Apache Maven 3.0.3 (r1075438; 2011-02-28 09:31:09-0800)).

git clone https://github.com/wordnik/swagger-core.git 
cd swagger-core
git checkout develop-1.3
JAVA_HOME=`/usr/libexec/java_home` mvn clean install
cd samples/java-jaxrs
JAVA_HOME=`/usr/libexec/java_home` mvn package -Dlog4j.configuration=file:./conf/log4j.properties jetty:run

where the last command comes from samples/java-jaxrs/README.md. After waiting a bit, the output shows that the jetty daemon has launched:

[INFO] Webapp directory = /Users/dhalperi/escience/swagger-dev/swagger-core/samples/java-jaxrs/target/swagger-java-sample-app_2.9.1-1.3.0-SNAPSHOT
2013-07-01 21:06:57.646:INFO:oejs.Server:jetty-8.1.11.v20130520
2013-07-01 21:07:00.110:INFO:oejpw.PlusConfiguration:No Transaction manager found - if your webapp requires one, please configure one.
Jul 01, 2013 9:07:11 PM com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Scanning for root resource and provider classes in the packages:
  com.wordnik.swagger.sample.resource
  com.wordnik.swagger.sample.util
  com.wordnik.swagger.jaxrs.listing
Jul 01, 2013 9:07:11 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFO: Root resource classes found:
  class com.wordnik.swagger.sample.resource.PetStoreResource
  class com.wordnik.swagger.jaxrs.listing.ApiListingResourceJSON
  class com.wordnik.swagger.sample.resource.PetResource
  class com.wordnik.swagger.sample.resource.UserResource
Jul 01, 2013 9:07:11 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFO: Provider classes found:
  class com.wordnik.swagger.jaxrs.listing.ApiDeclarationProvider
  class com.wordnik.swagger.jaxrs.listing.ResourceListingProvider
  class com.wordnik.swagger.sample.resource.SampleExceptionMapper
  class com.wordnik.swagger.sample.util.JacksonJsonProvider
Jul 01, 2013 9:07:11 PM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFO: Initiating Jersey application, version 'Jersey: 1.13 06/29/2012 05:14 PM'
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
2013-07-01 21:07:13.828:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:8002
[INFO] Started Jetty Server

Then (also as in README.md), I cloned the swagger-ui project, used dist/index.html, and it was able to display the UI.

dhalperi commented 11 years ago

Note: I am now working on integrating swagger-1.3.0 in my application. This is hard for me because I have, until now, only ever linked jars from Maven Central. Dependency management is hard now ;)

fehguy commented 11 years ago

You can still link via sonatype snapshots in your app:

  <repositories>
    <repository>
      <id>sonatype-snapshots</id>
      <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
    </repository>
  </repositories>

They are pretty much in sync with the current code.

dhalperi commented 11 years ago

oh, very cool!

dhalperi commented 11 years ago

Hi @fehguy,

I looked at the examples you supplied; they both appear to use com.wordnik.swagger.jaxrs.listing.ApiListing, which still indeed uses ServletConfig. See https://github.com/wordnik/swagger-core/blob/develop-1.3/modules/swagger-jaxrs/src/main/scala/com/wordnik/swagger/jaxrs/listing/ApiListing.scala#L20

Am I missing something major here?

Thanks!

fehguy commented 11 years ago

Howdy, good point. Essentially the ApiListing class you're referring to will use a pluggable JaxrsScanner, which may or may not use a ServletConfig. For example:

https://github.com/wordnik/swagger-core/blob/develop-1.3/modules/swagger-jaxrs/src/main/scala/com/wordnik/swagger/jaxrs/config/ReflectiveJaxrsScanner.scala

Does not use it. Since ServletConfig is a part of the JaxRS framework, referring to an object of that type optionally shouldn't cause problems. It simply doesn't need to be used.

There's a full example of using the BeanConfig here:

https://github.com/wordnik/swagger-core/tree/develop-1.3/samples/java-jaxrs-cxf

Where the entire configuration is done inside the applicationContext.xml

Make sense?

dhalperi commented 11 years ago

Yes, that makes good sense. Thanks!

The other wrench in my process is that I'm using Jersey in embedded mode---that is, I have no web.xml or applicationContext.xml files, and am instead configuring Jersey from Java as part of another application. So I need to figure out how to port your xml trickery over :). I don't think you have any embedded jax-rs examples. (When I get one working, I'll port it over!)

dhalperi commented 11 years ago

(Aside, even if ServletConfig isn't referenced, Jersey still throws an exception that I have no provider for it. So I have to set up a ServletConfig provider that just injects a null pointer?)

fehguy commented 11 years ago

It's even easier than that. You just need to create a BeanConfig object and set the values in your code. I don't recall how you set the @Provider in embedded mode, you'll need to make sure to add them as well. But it should work without any fancy coding.

4lejandrito commented 11 years ago

Hi, as you both suggest, it seems to be some weird problem in my local environment. I will leave it for now and use the sonatype snapshot. Thank you all.

fehguy commented 11 years ago

please reopen if issues persist

dhalperi commented 11 years ago

Once I get the embedded version working, I'll make a sample and submit a pull request into develop-1.3?

dhalperi commented 11 years ago

Hi @fehguy,

I'm having trouble understanding how the BeanConfig object interacts with the Jersey code in the sample app. It's declared but never referenced in the applicationContext.xml file. Is this a Spring-specific mechanism that is being used? If so, do you have any ideas for Jersey-generic integration of Swagger?

Here's my app so far. It serves the pet store resources fine, it just doesn't set up the /api resource. https://github.com/dhalperi/swagger-java-embedded-sample

Thanks, Dan

dhalperi commented 11 years ago

Okay, sorry, the answer is actually in the README. This example is indeed using a Tomcat-specific (not Spring, that's the other example) mechanism to employ BeanConfig. i'll keep digging :)

dhalperi commented 11 years ago

Finally, it looks like all the swagger-core JAX-RS code assumes that there is a Servlet lurking somewhere underneath the system (even if ServletConfig is not necessarily required to be available). For applications that embed Jersey Applications, however, it's much more difficult to use servlets than to simply use a web server. I think this means I'm screwed unless I really get into the Swagger JAX-RS code.

(E.g., the DefaultJaxRsConfig object, which underlies both examples you point out above, extends HttpServlet.)

fehguy commented 11 years ago

Can you tell me how to run your sample?

dhalperi commented 11 years ago

I use Eclipse; the project and classpath were created by mvn eclipse:eclipse. I will try and see how to get it to run from the command-line with mvn

dhalperi commented 11 years ago

This will do it: mvn exec:java -Dexec.mainClass="com.wordnik.swagger.sample.jersey.SampleJaxRsApplication"

fehguy commented 11 years ago

easy peasy. Edit this file:

SampleJaxRsApplication.java

and configure the BeanConfig during server startup:

import com.wordnik.swagger.jaxrs.config.*;

...

    public static void main(String[] args) throws Exception {
        final int port = 8002;

        BeanConfig config = new BeanConfig();
        config.setResourcePackage("com.wordnik.swagger.sample.resource");
        config.setVersion("1.0.0");
        config.setBasePath("http://localhost:8002/api");

        SampleJaxRsWebserver server = new SampleJaxRsWebserver(port);
        server.start();
        System.in.read();
        server.stop();
    }

That should do it.

dhalperi commented 11 years ago

Whoa---I did that before; didn't do much that I could see! I just now realized that it did in fact partially-work; the ApiListing is now available at localhost:8002/api-docs.

There are some issues, which I am still working on:

See the response:

curl -i -XGET -H"Accept: application/json" -H"Content-type: application/json" localhost:8002/api-docs

HTTP/1.1 200 OK
Content-Type: application/json
Date: Wed, 03 Jul 2013 03:01:30 GMT
Content-Length: 219

{"apiVersion":"1.3.5","swaggerVersion":null,"apis":[{"path":"/pet","description":"Operations about pets"},{"path":"/user","description":"Operations about user"},{"path":"/store","description":"Operations about store"}]

Still working on these, but letting you know :).

Also, assuming this will be a useful sample to add to the main project, please throw in any suggested style/etc. fixes.

Thanks! Dan

dhalperi commented 11 years ago

Conversely, if BeanConfig isn't there, I get this response at GET /api-docs

{"apiVersion":"0.0","swaggerVersion":"1.2"}
fehguy commented 11 years ago

I'm no grizzly expert (I do know one of the authors, though), but it appears this:

SampleJaxRsWebserver.java

URI baseUri = UriBuilder.fromUri("http://localhost/api/").port(port).build();

will mount it at /api just fine.

The swaggerVersion should be automatically set, I've fixed that and will push it.

For your issue with swagger-ui, I believe you need to add some CORS support. Without a HttpServlet filter, I'm guessing this isn't simple. See here as an example:

https://github.com/wordnik/swagger-core/blob/develop-1.3/samples/java-jaxrs/src/main/java/com/wordnik/swagger/sample/util/ApiOriginFilter.java#L24

Are you sure you don't want to support HttpServlet? It is definitely the path of least resistance...

dhalperi commented 11 years ago

Yes, but if I do that then the other resources are mounted at '/api/pet', for instance.

I've tried about three different times over 3 weeks to get the HttpServlet version to work, but I never seem to get it to work right with the package scanning. (I tried again earlier to day; will try again tonight) :)

dhalperi commented 11 years ago

Oh, it probably is the CORS stuff. Okay good call. Thanks.

fehguy commented 11 years ago

yes, the idea is that the swagger documentation is mounted at ${base-path}/api-docs. Is that bad for you? It's not hard to override, you'd simply make your own ApiListingResourceJSON.java which extends ApiListingResource, and sets the @Path value.

dhalperi commented 11 years ago

no, it's fine--it just seems different from the sample app. Maybe I just misinterpreted the sample app entirely, though!

dhalperi commented 11 years ago

Hey Tony,

Thanks for all your help! It sure looks like I have a complete working version now in my example repository. It was pretty easy to add the CORS filter.

Would you like it as a sample application?

I think it's ready to be copied directly into the develop-1.3 branch as a sample application, or I can fork swagger-core and submit a pull request instead.

If you have any changes to suggest I can implement those as well.

Thanks! Dan