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

No API Generation with Webflux, Swagger 3.0.0-SNAPSHOT AND spring integration #2967

Closed
Numbernick opened this issue Apr 11, 2019 · 6 comments · Fixed by #2995
Closed

No API Generation with Webflux, Swagger 3.0.0-SNAPSHOT AND spring integration #2967

Numbernick opened this issue Apr 11, 2019 · 6 comments · Fixed by #2995
Labels
Milestone

Comments

@Numbernick
Copy link

I'm working with Spring Webflux, Swagger 3.0.0-SNAPSHOT and google cloud integration from spring (so Spring integration). I have several REST-Endpoints which needs to be documeted via swagger. I was following the normal implementation of swagger as described in #1773 . So far so good.

I ended with an almost empty swagger-ui page with following errors:
😱 Could not render e, see the console.
The Console Output was like:
TypeError columnNumber: 189990 fileName: "http://localhost:8080/webjars/springfox-swagger-ui/swagger-ui-standalone-preset.js?v=3.0.0-SNAPSHOT" lineNumber: 13 message: "l[this.state.selectedIndex] is undefined" stack: "value@http://localhost:8080/webjars/springfox-swagger-ui/swagger-ui-standalone-preset.js?v=3.0.0-SNAPSHOT:13:189990\nw/t.prototype.render@http://localhost:8080/webjars/springfox-swagger-ui/swagger-ui-bundle.js?v=3.0.0-SNAPSHOT:70:61143[etc.]

The api-docs page just returned a 404 Not Found.
So the reason for the 404 is just an empty documentationCache in Swagger2ControllerWebFlux. I invested some breakpoint at startup, like in the loading class for all handler resolvers in DocumentationPluginsBootstrapper and no single method got called to populate the documentationCache.

The problem is the top annotation:

@Component
@Conditional(SpringIntegrationNotPresentInClassPathCondition.class)
public class DocumentationPluginsBootstrapper

The conditional clashes with the google cloud integration setup and leeds to an empty documentation for my webflux controllers. So since there is no documentation found, the api-docs page results in an 404 Not Found error and the swagger-ui page results in an not helpful javascript implementation error.

My workarround is to create the bean at startup on ym own like (kotlin-code):

@Bean
    fun swaggerDocumentationStarter(
            documentationPluginsManager: DocumentationPluginsManager,
            handlerProviders: List<RequestHandlerProvider>,
            scanned: DocumentationCache,
            resourceListing: ApiDocumentationScanner,
            typeResolver: TypeResolver,
            defaults: Defaults,
            pathProvider: PathProvider,
            environment: Environment): DocumentationPluginsBootstrapper = DocumentationPluginsBootstrapper(documentationPluginsManager, handlerProviders, scanned, resourceListing, typeResolver, defaults, pathProvider, environment)

This results in a total working swagger-ui and api-gen.

My question / change request would be to have a more apporiate solution for applications working with WebMvc / WebFlux AND spring integration.

BTW: Adding springfox-spring-integration-webflux as library results in following Stacktrace, so this sounds not like a solution:

11-04-2019 16:16:59.221 [main] ERROR o.s.boot.SpringApplication.reportFailure - Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springIntegrationWebFluxRequestHandlerProvider': Lookup method resolution failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [springfox.documentation.spring.web.plugins.SpringIntegrationWebFluxRequestHandlerProvider] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:265)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1253)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1168)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
	at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:67)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
	at de.swr.ard.core.imageservice.service.ImageServiceApplicationKt.main(ImageServiceApplication.kt:87)
Caused by: java.lang.IllegalStateException: Failed to introspect Class [springfox.documentation.spring.web.plugins.SpringIntegrationWebFluxRequestHandlerProvider] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2]
	at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:686)
	at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:583)
	at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:568)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:248)
	... 18 common frames omitted
Caused by: java.lang.NoClassDefFoundError: org/springframework/integration/webflux/inbound/WebFluxIntegrationRequestMappingHandlerMapping
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
	at java.lang.Class.getDeclaredMethods(Class.java:1975)
	at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:668)
	... 21 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.springframework.integration.webflux.inbound.WebFluxIntegrationRequestMappingHandlerMapping
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 25 common frames omitted
@davidmelia
Copy link

I get exactly the same problem with Spring Cloud Streams (containing Spring Integration). @Numbernick thanks for the workaround.

@singleton11
Copy link

singleton11 commented May 7, 2019

We also had the same issue and invented the same workaround, @Numberick, do you have any proposal how to resolve an issue? I’d implement it

@Numbernick
Copy link
Author

@singleton11 Thank you for your comment :) Well I would see two types which should be done:

  1. The swagger-ui page should have some useful information like "No documentation found". Thats way better than a javascript error
  2. I don't know why no documentation should be generated when spring-integration is on the classpath. It seems like the springfox integration support has created this behaviour: a926d80 . So I hope @dschulten could have some educated guess how this can be done correctly?

@singleton11
Copy link

@dschulten, could you please explain how this can be done correctly?

@dschulten
Copy link
Contributor

dschulten commented May 13, 2019

@singleton11 @Numbernick Spring-integration initializes its http request handler beans later than the plain mvc or webflux handlers. In order to document spring-integration's handler classes, the DocumentationPluginsBootstrapper needs to run after spring-integration has fully initialized its beans. Therefore I prevent the plain bootstrapper from running and bootstrap later in SpringIntegrationDocumentationPluginsBootstrapper
Spring Cloud depends on spring-integration, therefore the normal bootstrapper does not run, which might not be what you want if you do not use spring-integration's http integration. While the issue should be resolvable by adding the springfox-spring-integration jar, I have introduced an incompatible change.

The solution to invert the conditional annotation, so that it disables the normal bootstrapping if springfox-spring-integration is present, sounds more appropriate, but I faintly remember that was not as easy as it sounds. I'll check again, open for suggestions.

@dschulten
Copy link
Contributor

see PR #2995
The springfox-spring-integration plugins for webflux and webmvc do not automatically pull in the spring-integration-webflux or spring-integration-http jars - that should be a conscious decision by users of the library. @Numbernick as your case proves, not everybody wants that.

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