A 'simple' way to implement Swagger in a Spring MVC application - spring

I have a ReSTFul API written in simple Spring (no Spring Boot, no fancy stuff!). I need to implement Swagger into this. So far, EVERY page on the internet has only driven me crazy with confusing configurations and bloated code that I did not find portable at all.
Does anyone have a sample project (or a set of detailed steps) that can help me accomplish this? In particular, I am looking for a good sample that uses swagger-springmvc. I know it has 'samples', but at best, the esoteric code is discouraging.
I must clarify that I am not looking for "why Swagger is simply the best". I am not using (and for my current task will not use) Spring Boot or such.

Springfox (Swagger spec 2.0, current)
Springfox has replaced Swagger-SpringMVC, and now supports both Swagger specs 1.2 and 2.0. The implementation classes have changed, allowing for some deeper customization, but with some work. The documentation has improved, but still needs some details added for advanced configuration. The old answer for the 1.2 implementation can still be found below.
Maven dependency
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.5.0</version>
</dependency>
The bare-minimum implementation looks more-or-less the same, but now uses the Docket class instead of the SwaggerSpringMvcPlugin class:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api(){
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.regex("/api/.*"))
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("TITLE")
.description("DESCRIPTION")
.version("VERSION")
.termsOfServiceUrl("http://terms-of-services.url")
.license("LICENSE")
.licenseUrl("http://url-to-license.com")
.build();
}
}
Your Swagger 2.0 API documentation will now be available at http://myapp/v2/api-docs.
Note : If you are not using Spring boot then you should add jackson-databind dependency. Since springfox uses jackson for databinding.
Adding Swagger UI support is even easier now. If you are using Maven, add the following dependency for the Swagger UI webjar:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.5.0</version>
</dependency>
If you are using Spring Boot, then your web app should automatically pick up the necessary files and show the UI at http://myapp/swagger-ui.html (formerly: http://myapp/springfox). If you are not using Spring Boot, then as yuriy-tumakha mentions in the answer below, you will need to register a resource handler for the files. The Java configuration looks like this:
#Configuration
#EnableWebMvc
public class WebAppConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
The new static documentation generation feature also looks quite nice, though I have not tried it out myself.
Swagger-SpringMVC (Swagger spec 1.2, older)
The documentation for Swagger-SpringMVC can be a little bit confusing, but it is actually incredibly easy to set up. The simplest configuration requires creating a SpringSwaggerConfig bean and enabling annotation-based configuration (which you probably already do in your Spring MVC project):
<mvc:annotation-driven/>
<bean class="com.mangofactory.swagger.configuration.SpringSwaggerConfig" />
However, I think it is well worth it to take the extra step of defining a custom Swagger configuration using the SwaggerSpringMvcPlugin, instead of the previous XML-defined bean:
#Configuration
#EnableSwagger
#EnableWebMvc
public class SwaggerConfig {
private SpringSwaggerConfig springSwaggerConfig;
#SuppressWarnings("SpringJavaAutowiringInspection")
#Autowired
public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) {
this.springSwaggerConfig = springSwaggerConfig;
}
#Bean
public SwaggerSpringMvcPlugin customImplementation(){
return new SwaggerSpringMvcPlugin(this.springSwaggerConfig)
.apiInfo(apiInfo())
.includePatterns(".*api.*"); // assuming the API lives at something like http://myapp/api
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("TITLE")
.description("DESCRIPTION")
.version("VERSION")
.termsOfServiceUrl("http://terms-of-services.url")
.license("LICENSE")
.licenseUrl("http://url-to-license.com")
.build();
}
}
When you run your application, you should now see your API spec created at http://myapp/api-docs. To get the fancy Swagger UI set up, you need to clone the static files from the GitHub project and put them in your project. Make sure your project is configured to serve the static HTML files:
<mvc:resources mapping="*.html" location="/" />
Then edit the index.html file at the top level of the Swagger UI dist directory. Towards the top of the file, you'll see some JavaScript that refers to the api-docs URL of another project. Edit this to point to your project's Swagger documentation:
if (url && url.length > 1) {
url = url[1];
} else {
url = "http://myapp/api-docs";
}
Now when you navigate to http://myapp/path/to/swagger/index.html, you should see the Swagger UI instance for your project.

Springfox Swagger UI works for me after adding WebJar dependency and resource mappings.
http://www.webjars.org/documentation#springmvc
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.5</version>
</dependency>
spring-servlet.xml:
<mvc:resources mapping="swagger-ui.html" location="classpath:/META-INF/resources/"/>
<mvc:resources mapping="/webjars/**" location="classpath:/META-INF/resources/webjars/"/>
or Spring Annotation
https://github.com/springfox/springfox-demos/blob/master/spring-java-swagger/src/main/java/springfoxdemo/java/swagger/SpringConfig.java
Swagger2 should be enabled
#EnableSwagger2
public class SwaggerConfiguration {
}

You can also consider using swagger-maven-plugin to generate swagger.json and copy it to yours static swagger-ui.
Please check simple sample of working plugin with Spring MVC annotations on this repo:
https://github.com/khipis/swagger-maven-example
or for JAX-RS
https://github.com/kongchen/swagger-maven-example

Related

Springfox 3.0 not working in Spring framework

I have a Spring application. I am trying to migrate it to Springfox 3.0 from Springfox 2.9.2.
It is already working on old Springfox dependencies, but with 3.0, I can't access any of the documentation at all. As far as I know, all I just need to do is add the Springfox dependecy below:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
And remove all Springfox 2.9.2 configurations/beans. And it should work alright.
I tried accessing the Swagger UI docs via
/swagger-ui
/swagger-ui/
/swagger-ui/#/
/swagger-ui/index.html
Still, its not accessible. The microservice compiles successfully with mvn clean install. What do I do to make Springfox 3.0 work?
You still need to keep the following basic configuration:
#Configuration
public class SpringFoxConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
You may want to check the migration guide here.
I strongly suggest you move to springdoc, which is a more recent library, way less buggy and much easier to work with. Take a look at:
https://springdoc.org/
https://springdoc.org/#migrating-from-springfox

Is there any special configuration to use SpringRunner with junit5? [duplicate]

This question already has answers here:
Should SpringRunner be used in Spring Boot with Junit 5
(3 answers)
Closed last year.
My micro-service project based on spring-boot framework and all my unit test running with spring runner.
#RunWith(SpringRunner.class)
adding this annotations, imports the following library:
import org.springframework.test.context.junit4.SpringRunner;
How can I set my test classes to run with junit5 ?
Using JUnit Jupiter (aka JUnit 5) no longer requires ˋ #RunWith(SpringRunner.class)ˋ since this is a JUnit 4 mechanism. With recent versions of Spring/Spring Boot JUnit 5 support comes out of the box eg through using ˋspring-boot-starter-testˋ.
I recommend to exclude dependencies on JUnit 4 in your Maven/Gradle file to make confusing JUnit 4 and 5 features less likely.
Here’s an article that shows the basics: https://howtodoinjava.com/spring-boot2/testing/junit5-with-spring-boot2/
Remove JUnit4 from your build Path.
For example :
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#TestPropertySource(locations = "classpath:application-local.properties")
public class MyTest {
#Before
public void setUp() {
...
}
#Test
public void testMethod() {
Assert.assertTrue(...);
}
}
will become
#SpringBootTest(classes = Application.class)
#TestPropertySource(locations = "classpath:application-local.properties")
public class MyTest {
#BeforeEach
public void setUp() {
...
}
#Test
public void testMethod() {
Assertions.assertTrue(...);
}
}
Spring 2.4 seems to include JUnit 5 and make it the default out of the box.
Besides updating #RunWith(SpringJUnit4ClassRunner.class) to #ExtendWith(SpringExtension.class) I had to add the following to build.gradle for the tests to actually run:
test {
useJUnitPlatform {}
}
This last step may have been due to JUnit 4 being a dependency of one of my dependencies, but every other thing I read didn't suggest this was needed.
The first annotation #RunWith(SpringRunner.class) is used to provide a bridge between Spring Boot test features and JUnit. SpringRunner.class enables full support of spring context loading and dependency injection of the beans in the tests. #SpringBootTest create ApplicationContext tests through SpringApplication that will be utilized in our tests. It bootstraps the entire container since the embedded server and creates a web environment.
In our test, we can mimic the real web environment setting it as RANDOM_PORT that also loads WebServerApplicationContext. The embedded server is started and listen to on a random port.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {YourPackage.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class YourClassTest {
#LocalServerPort
private int port;
#Autowired
TestRestTemplate restTemplate;
HttpHeaders headers = new HttpHeaders();
#ParameterizedTest
#JsonFileSource(resources = "/param.json")
void createBusinessEntity(JsonObject object){
....
}
}
#LocalServerPort annotation provides us the injected HTTP port that got allocated at runtime. It is a convenient alternative for #Value("${local.server.port}").
To access a third-party REST service inside a Spring application we use the Spring RestTemplate or TestRestTemplate the convenient alternative that is suitable for integration tests by injecting it in our test class. With spring-boot-starter-test dependency in our project, we can access to "TestRestTemplate" class in runtime.
In our test method, we are using the junit-json-params , a Junit 5 library that provides annotations to load data from JSON Strings or files in parameterized tests. We also annotated the method with #ParameterizedTest annotation to complement the library bellow. It is used to signal the annotated method is a parameterized test method. That method must not be private or static. They also must specify at least one ArgumentsProvider via #ArgumentsSource or a corresponding composed annotation.
Our #ArgumentsSource a JSON file #JsonFileSource(resources = "param.json") we put inside the test.resources package. #JsonFileSource lets you use JSON files from the classpath. It supports single objects, arrays of objects and JSON primitives.
The JSON object retrieved from the file is bound to the method params "object" that it is converted to a POJO object, in this case, our entity model.
In the Pom.xml we must import these libraries...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.joshka</groupId>
<artifactId>junit-json-params</artifactId>
<version>5.5.1-r0</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit-jupiter.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
Take a look at these articles that a post on DZone and my blog where you can access a complete sample and explanation step by step how to test spring boot microservice using Junit 5.
https://dzone.com/articles/microservices-in-publish-subscribe-communication-u
https://www.jeevora.com/2019/11/18/publish-subscribe-messaging-systems/

How to generate OpenApi 3.0 spec from existing Spring Boot App?

I have a project (Spring Boot App + Kotlin) that I would like to have an Open API 3.0 spec for (preferably in YAML). The Springfox libraries are nice but they generate Swagger 2.0 JSON. What is the best way to generate an Open Api 3.0 spec from the annotations in my controllers? Is writing it from scratch the only way?
We have used springdoc-openapi library in our kotlin project, and it meets our need for automating the generation of API documentation using spring boot projects.
It automatically deploys swagger-ui to a spring-boot application
The Swagger UI page should then be available at:
- http://server:port/context-path/swagger-ui.html
The OpenAPI description will be available at the following url for json format:
- http://server:port/context-path/v3/api-docs
Add the library to the list of your project dependencies (No additional configuration is needed)
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.2.32</version>
</dependency>
You could look at spring-restdocs and restdocs-api-spec.
spring-restdocs takes a test-driven approach to API documentation which has many advantages over the introspection-driven approach spring-fox uses. restdocs-api-spec is an extension for spring-restdocs that adds API specification support. Currently it supports OpenAPI2 OpenAPI3 and Postman.
I decided to implement my own generator https://github.com/jrcodeza/spring-openapi maybe you can check it out too. It's based on reflection and supports javax and spring annotations. It also generates inheritance model (with discriminators) based on Jackson annotations. Besides you can define your own interceptors if you want to alter generation process (e.g. when you have your own annotations and need to adjust generated sections of schema). You can use it in runtime mode or as a maven plugin. There is also OpenAPI3 to java client generator, which generates the model from openapi3 spec. Again it generates also Javax annotations and Jackson annotations for correct inheritance.
If you're using jax-rs this tutorial helps. It uses the Apache CXF implementation. I couldn't find any other implementation of jaxrs that uses Spring Boot AND generate Open API 3.0 spec.
You'll need these depedencies:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-service-description-openapi-v3</artifactId>
<version>3.2.4</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>swagger-ui</artifactId>
<version>3.13.6</version>
</dependency>
Here is the general configuration, more detail is in the link:
#Configuration
#EnableAutoConfiguration
#ComponentScan(basePackageClasses = PeopleRestService.class)
public class AppConfig {
#Autowired private PeopleRestService peopleRestService;
#Bean(destroyMethod = "destroy")
public Server jaxRsServer(Bus bus) {
final JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
factory.setApplication(new JaxRsApiApplication());
factory.setServiceBean(peopleRestService);
factory.setProvider(new JacksonJsonProvider());
factory.setFeatures(Arrays.asList(new OpenApiFeature()));
factory.setBus(bus);
factory.setAddress("/");
return factory.create();
}
#Bean
public ServletRegistrationBean cxfServlet() {
final ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new CXFServlet(), "/api/*");
servletRegistrationBean.setLoadOnStartup(1);
return servletRegistrationBean;
}
}
https://dzone.com/articles/moving-with-the-times-towards-openapi-v300-adoptio
You can also refer to
https://www.baeldung.com/spring-rest-openapi-documentation
which provides a tutorial on implementing OpenAPI 3.0 with a SpringBoot 1.x or 2.x application using springdoc-openapi.
To summarize, you just add the maven dependency for springdoc-openapi into your application and when you bootRun, go to path
http://server:port/v3/api-docs.yaml/ and you will download an Open API 3.0 spec file in yaml, generated from your application's code.
You can do some other stuff with springdoc-openapi, by accessing the following when your SpringBoot application is running:
http://server:port/v3/api-docs: Gives your spec file in Json format.
http://server:port/swagger-ui.html: Access this in your browser and you will see the swagger documentation.

Spring boot metrics with Jersey2

I have an application running spring-boot, jersey2 and spring metrics:
below is maven snippet:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Jersey used to work well until introducing actuator dependency.
Then following bean has been created to make Jersey working as filter:
#Bean
public FilterRegistrationBean jerseyFilterRegistration() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setName("jerseyFilter");
bean.setFilter(new ServletContainer(resourceConfig()));
bean.setOrder(Ordered.LOWEST_PRECEDENCE);
bean.addInitParameter("com.sun.jersey.config.property.WebPageContentRegex", managementContextRegex);
return bean;
}
Metrics are mapped to /admin path. With this configuration I cannot make metrics working. However by adding management.port (different than main app port) both Jersey resource and metrics are available.
What I'm missing here to make both metrics and Jersey resource start working on the same port?
"com.sun.jersey.config.property.WebPageContentRegex"
This is the wrong property. That's for Jersey 1.x. For 2.x, it should be
"jersey.config.servlet.filter.staticContentRegex"
See ServletProperties.FILTER_STATIC_CONTENT_REGEX
As an aside you can avoid having to define your own FilterRegistrationBean by simply setting a couple configuration properties. In your application.properties, you could use the following
spring.jersey.type=filter
spring.jersey.init.jersey.config.servlet.filter.staticContentRegex=<your-regex>
Or you can configure the regex in your ResourceConfig subclass
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
property(ServletProperties.FILTER_STATIC_CONTENT_REGEX, "<your-regex>");
}
}
As another side, just an FYI, the cause of the problem is the default /* url-mapping used for Jersey. If you can change it, doing so would solve the problem. For instance /api. You can configure that in the properties with spring.jersey.applicationPath=/api or with #ApplicationPath("/api") on the ResourceConfig subclass.
And the final aside, there is also a property
ServletProperties.FILTER_FORWARD_ON_404
"jersey.config.servlet.filter.forwardOn404"
I'm not exactly sure how the staticContenRegex property works, I never really dug into to source code. So I don't know if it just does some file IO to get the static file or it forwards the request, but if it does some file IO, then I don't think the property will work for your use case, as the endpoints are not files. In which case the forwardOn404 should work.

drools-6 (kie) auto scanning (from spring) of modules and sessions from kie workbench deployed artifacts

I am trying to build a web (spring-mvc) application with kie (drools 6) integrated via injection. I have used kie workbench to create a workflow, complied and deployed. I have added reference of this artifact in my project's pom.xml and added the local kie-workbench repository as per this blog post and it's working fine (pulling in the artifact as dependency in my maven/spring project). What I am trying to do is inject the kiesession in one of my service as dependency with following snippet -
#Service
public class TniServiceImpl implements TniService {
#Inject
#KSession("tniSession")
private KieSession tniSession;
...
}
In my root-context.xml, I have added the kie namespace as well along with reference to xsd. I have added org.kie.spring.KModuleBeanFactoryPostProcessor as well as per drools documentation. I am trying to make CDI injection work for KSession scanning and injection (it's already working for my other components in same project, using #Inject). So far I am always getting "No qualifying bean of type [org.kie.api.runtime.KieSession] found for dependency" error. Looks like spring is not able to scan the available kie modules and sessions therein. Need help on following -
Is CDI inject really supported with spring? Do I have to configure kmodules and kession explicitly as mentioned here?
Am I missing something here which should make this scanning and injection work?
My environment is following -
spring 3.2.6-RELEASE (including webmvc and other components)
kie-api-6.0.1.FINAL
kie-spring-6.0.1.FINAL
kie-internal-6.0.1.FINAL
I have already gone through following links but no luck (mostly they are not trying to do what I am) -
Loading Drools/KIE Workbench artifacts directly from the repository
why does loading Drools 6 KIE JAR into code fail?
I'll appreciate if anybody can guide me on what could be the missing piece here or if there's no option but to explicitly define all kmodules/ksessions in spring config file.
I had the same problem and found a solution here: http://drools.46999.n3.nabble.com/Spring-4-0-amp-Drools-6-0-1-Integration-issue-td4028052.html
Basically you will need to inject ApplicationContext instead of kieSession and get xml bean manually.
TniServiceImpl.java
#Service
public class TniServiceImpl implements TniService {
#Inject
ApplicationContext context;
KieSession kieSession;
#PostConstruct
public void postConstruct(){
kieSession = (KieSession) context.getBean("ksession1");
}
...
}
root-context.xml
<kie:kmodule id="kmodule1">
<kie:kbase name="kbase1">
<kie:ksession name="ksession1" />
</kie:kbase>
</kie:kmodule>
<bean id="kiePostProcessor" class="org.kie.spring.KModuleBeanFactoryPostProcessor" />
Hope this helps.
UPDATE:
Another way to achieve this is to keep xml identical and instead of trying to inject KieSession, inject KieBase. Then, with the instance of KieBase, create new KieSessions.
#Service
public class TniServiceImpl implements TniService {
#Autowired
private KieBase kbase;
/* inside some method */
#RequestMapping(method=RequestMethod.GET)
public #ResponseBody Data getData() {
KieSession ksession = kbase.newKieSession();
...
}
}
The above answer doesn't work with spring mvc. I found that this is a bug in the existing drools and they are fixing it in the next version. I am stuck at this point since I am using DROOLS in batch mode but I want it to be used in a REST Service hosted on websphere.
The above solution works perfectly within a batch program.
This is what I have working with the latest Spring MVC (Spring Boot)
#SpringBootApplication
public class DroolDemoApplication {
public static void main(String[] args) {
SpringApplication.run(DroolDemoApplication.class, args);
}
#Bean
public KieContainer kieContainer() {
return KieServices.Factory.get().getKieClasspathContainer();
}
#Bean
public KieSession kieSession() throws IOException {
return kieContainer().newKieSession("DroolDemoSession");
}
}
and below is the kmodule.xml
<kbase name="DroolDemoKbase" packages="rules">
<ksession name="DroolDemoSession" />
</kbase>
finally all you do in your controller is
#Autowired
private KieSession kieSession;
kieSession.fireAllRules();
hope this helps those folks still having issues
I had similar issues with the rules not being triggered, and I solved it by using the 6.2.0.Final version of the kie-ci and kie-spring. I tried versions: 7.7.0, 7.2.0, 6.5.0 and 6.4.0, but none of them worked.
...
<properties>
<kie.version>6.2.0.Final</kie.version>
</properties>
...
<dependencies>
...
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-ci</artifactId>
<version>${kie.version}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>${kie.version}</version>
</dependency>
...
</dependencies>
What also helped was running mvn dependency:tree and seeing which versions of which artefacts/projects are being used.

Resources