What is the best way to plug the server stubs generated by Swagger Codegen into an existing Spring MVC application?
I'm starting off by attempting to use the petstore stubs sample.
My Spring configuration is in Java and looks like this:
public class SpringConfigurationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { ApplicationContext.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebMvcContext.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
// ... onStartup etc.
}
WebMvcConfigurationSupport:
@Configuration
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
@PropertySource({ "classpath:config.properties", "file:${CONFIGDIR}/config.properties" })
@ComponentScan(useDefaultFilters = false, basePackages = { "com.yyy", "com.xxx" }, includeFilters = { @Filter(type = FilterType.ANNOTATION, value = Controller.class) })
public class WebMvcContext extends WebMvcConfigurationSupport {
// ... beans etc.
}
ApplicationContext:
@Configuration
@EnableAsync
@EnableScheduling
@EnableMBeanExport
@Import({SecurityConfig.class, GeneralDBConfiguration.class})
@ComponentScan(useDefaultFilters = true, basePackages = { "com.yyy", "com.xxx" }, excludeFilters = { @Filter(type = FilterType.ANNOTATION, value = {Controller.class, Configuration.class/*, Aspect.class*/}) })
public class ApplicationContext implements AsyncConfigurer {
// beans etc.
}
How do I go about including the config classes part of the io.swagger.configuration
package into my existing application?
Some more details:
One of the problems I'm having is that if I specify a maven dependency on the petshop stubs (which is installed locally by running mvn install:install-file ...
from the spring-mvc-j8-async
directory):
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-spring-mvc-server</artifactId>
<version>1.0.0</version>
</dependency>
Then my spring application finds two AbstractAnnotationConfigDispatcherServletInitializer
s (one from my app, and the io.swagger.configuration.WebApplication
one from swagger-spring-mvc-server
) and fails to load up - giving the following exception:
Failed to register servlet with name 'dispatcher'.Check if there is another servlet registered under the same name.
I guess another way to phrase my question would be, how do you use the server stubs generated by swagger-codegen
? It looks like I can't just depend on the maven package out of the box...
I can see this is quite an old one, but I struggled with this a lot and the info I collected might be useful to others until generator docs get improved.
This description is for generating and use spring-mvc server stub from OpenApi 3.0.0 spec using openapi-generator-maven-plugin.
Don't use swagger-codegen, use openapi generator instead: https://github.com/OpenAPITools/openapi-generator (reasoning of the fork is here: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/qna.md)
Create a separate project/module for server stuff and configure maven plugin.
<build>
<plugins>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>3.3.4</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<skipIfSpecIsUnchanged>true</skipIfSpecIsUnchanged>
<inputSpec>${engine-openapi-spec.location}</inputSpec>
<output>${project.build.directory}/generated-sources/openapi</output>
<generatorName>spring</generatorName>
<library>spring-mvc</library>
<apiPackage>eu.dorsum.swift.engine.service.api</apiPackage>
<modelPackage>eu.dorsum.swift.engine.service.model</modelPackage>
<generateApis>true</generateApis>
<generateApiDocumentation>false</generateApiDocumentation>
<generateApiTests>false</generateApiTests>
<generateModels>true</generateModels>
<generateModelDocumentation>false</generateModelDocumentation>
<generateModelTests>false</generateModelTests>
<generateSupportingFiles>true</generateSupportingFiles>
<configOptions>
<sourceFolder>src/main/java</sourceFolder>
<java8>true</java8>
<dateLibrary>java8</dateLibrary>
<useTags>true</useTags>
<configPackage>eu.dorsum.swift.engine.appconfig</configPackage>
<interfaceOnly>false</interfaceOnly>
<delegatePattern>true</delegatePattern>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
There are two pieces of configuration which are kind of tricky to figure out.
2.a: Set configOptions/configPackage
to a package where your application's root config resides. This option will add your config package as additional basePackage to component scan in OpenAPIUiConfiguration.java:
@ComponentScan(basePackages = {"eu.dorsum.swift.engine.service.api", "eu.dorsum.swift.engine.appconfig"})
. This is an inverted approach you might think of initially by having the generated mvc config start your existing stuff, but this is the only way I've found requiring no modification in generated code.
2.b: Set configOptions/delegatePattern
to true. This one I like a lot! This will generate an additional delegation interface which your server controller can implement. Generated ApiController will delegate all service calls to this interface, so you can plug in your implementation very elegantly. In my setup I have this chain: MessageApi (generated interface) -> MessageApiController implements MessageApi (generated mvc controller) -> MessageApiDelegate (generated interface) -> MessageService implements MessageApiDelegate (my implementation of service methods).
Having these two pieces of config there is no need to modify generated sources. If api spec changes delegation interface changes and you have to implement those changes in MessageService.
Below is an update to my original post to explain in more detail how the service implementation is bound to the delegate interface.
The sources generated by openapi-gen are in a separate jar module containing only swagger.yaml and pom.xml as checked-in 'sources'. The module using the generated code is a war which has a direct dependency on the generated jar. Service implementation is in the war and implements MessageApiDelegate interface which is in the generated source. Spring context is pulled up from the generated code and the packages you define as apiPackage and configPackage in openapi-gen config will be added as basePackages for @ComponentScan in the generated OpenAPIUiConfiguration.java, so your service implementation must be placed in or under apiPackages.
Here is a simplified structure to put the pieces together.
parent/
swift-engin-service/ (generated jar module)
swagger.yaml
pom.xml
target/generated-sources/openapi/src/main/java/eu/dorsum/swift/engine/
appconfig/ (generated spring webmvc config)
OpenAPIUiConfiguration.java
WebApplication.java (brings up spring context by extending AbstractAnnotationConfigDispatcherServletInitializer)
service/
MessageApiController.java (@Controller, @RequestMapping etc.)
MessageApiDelegate.java (implement this to get your service implementation plugged in the controller)
swift-engine/ (war module, your code)
pom.xml
src/main/java/eu/dorsum/swift/engine/
appconfig/
EngineConfig.java (my spring config)
service/api/ (must be the same as apiPackages property)
MessageService.java (service implementation)
Relevant part of swift-engine/pom.xml
<project>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>eu.dorsum.core.java.swift-engine</groupId>
<artifactId>swift-engine-service</artifactId>
</dependency>
</dependencies>
</project>
Relevant part of eu.dorsum.swift.engine.service.api.MessageService
@Service
public class MessageService implements MessageApiDelegate {
@Override
public ResponseEntity<List<SwiftMessage>> listMessages(List<String> sorting, Integer pageStart, Integer pageSize, String filter) {
// implementation
}
@Override
public ResponseEntity<Void> updateMessage(SwiftMessage message) {
// implementation
}
}
Relevant part of eu.dorsum.swift.engine.appconfig.OpenAPIUiConfiguration (generated)
@Configuration
@ComponentScan(basePackages = {"eu.dorsum.swift.engine.service.api", "eu.dorsum.swift.engine.appconfig"})
@EnableWebMvc
public class OpenAPIUiConfiguration extends WebMvcConfigurerAdapter {
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With