I have a Spring application that's trying to connect to a database. In order to connect, an SSH tunnel must first be established (using Jsch). How do I delay HibernateJpaAutoConfiguration until after the bean that's establishing the Jsch SSH session has returned? Currently the application is failing to start because the tunnel hasn't been opened yet. When I try to exclude this autoconfiguration class, and then instantiate it explicitly predicated on the session bean already having been created, I get the following error:
Caused by: java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
I don't understand why I suddenly have to provide this myself when, if I rely on the auto-configuration, I don't have to provide it. If someone can show me a way to achieve this, that would be great.
POM:
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Spring data JPA, default tomcat pool, exclude it -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
...
Main application:
#SpringBootApplication(exclude = HibernateJpaAutoConfiguration.class)
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
#Configuration
static class SshTunnelConfiguration {
#Bean
public com.jcraft.jsch.Session sshTunnel() {
...
}
}
#Configuration
#ConditionalOnBean(com.jcraft.jsch.Session.class)
static class DelayedJpaConfiguration extends HibernateJpaAutoConfiguration {
public JpaConfiguration(DataSource dataSource, JpaProperties jpaProperties, ObjectProvider<JtaTransactionManager> jtaTransactionManager, ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
super(dataSource, jpaProperties, jtaTransactionManager, transactionManagerCustomizers);
}
}
}
You would have to implement your own data source (by extending the one you use) and implementing the InitializingBean interface and in the 'afterPropertiesSet' method initialize your jsch tunnel.
Please refer to this: Spring Data JPA with ssh tunnel to a remote MySQL server
I am using the following starter for Graphql integration
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>6.0.1</version>
</dependency>
I am not sure how to override the SimpleDataFetcherExceptionHandler with my own CustomExceptionHandler. The library already autowires a lot of stuff. Do I need to create a separate configuration for graphQl object? The documentation is not much helpful.
I also tried to integrate #ControllerAdvice to my Graphql java spring boot application but the errors are not matched by the exception handlers inside it. They are handled by GraphQl error handler. How does error propagates inside Graphql?
How can I change this behaviour?
I have this and it's working for me. Pretty much regular spring stuff for exception handling, but there is no #ControllerAdvice (it's kotlin btw, if you can't understand - let me know, but I think it's pretty clear even without kotlin knowledge).
import org.springframework.web.bind.annotation.ExceptionHandler
import com.oembedler.moon.graphql.boot.error.ThrowableGraphQLError
internal class GraphQLExceptionHandler {
#ExceptionHandler(AppException::class)
fun handleGenericException(ex: AppException): ThrowableGraphQLError {
return ThrowableGraphQLError(ex)
}
#ExceptionHandler(Exception::class)
fun handleGenericException(ex: Exception): ThrowableGraphQLError {
return ThrowableGraphQLError(TechnicalException())
}
}
#Configuration
internal open class GraphQLConfiguration {
#Bean
open fun graphQLExceptionHandler(): GraphQLExceptionHandler {
return GraphQLExceptionHandler()
}
}
I have quite a few dependencies tho, in case they matter here:
<graphql.version>5.4.1</graphql.version>
<graphql-datetime-spring-boot-starter.version>1.4.0</graphql-datetime-spring-boot-starter.version>
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>${graphql.version}</version>
</dependency>
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphiql-spring-boot-starter</artifactId>
<version>${graphql.version}</version>
</dependency>
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>voyager-spring-boot-starter</artifactId>
<version>${graphql.version}</version>
</dependency>
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-java-tools</artifactId>
<version>${graphql.version}</version>
</dependency>
<dependency>
<groupId>com.zhokhov.graphql</groupId>
<artifactId>graphql-datetime-spring-boot-starter</artifactId>
<version>${graphql-datetime-spring-boot-starter.version}</version>
</dependency>
I am using "Spring Boot - 2.1.5.RELEASE" and "Spring framework - 5.1.7.RELEASE".
JMS listener annotation is not picking a message from a MQ queue. No error logs are rolling in my IntelliJ IDEA as well.
About my project - I am exposing a REST service which sends and receives a message from MQ (used JMSTemplate annotation). Till now it was working. Now I have to add another class which should listen for a queue. Hence added #JMSListener annotation which is not picking any message. Any insight will be really helpful.
The pom has below specified dependencies.
<dependency>
<groupId>com.ibm.mq</groupId>
<artifactId>mq-jms-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.ibm.mq</groupId>
<artifactId>com.ibm.mq.allclient</artifactId>
<version>9.1.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
The class has below specified method.
import org.springframework.jms.annotation.JmsListener;
#Component
public class PickMyMessage {
#JmsListener(destination = "IN",containerFactory = "myFactory")
public void pullMyMessaage(String message){
System.out.println("Message is pulled..");
}
}
My main application has #EnableJMS annotation. In my application.yml file I have given below information.
ibm:
mq:
channel: MY.APP.SVRCONN
connName: 192.168.0.1(1415)
password: Pswd
queueManager: QM01
user: appsrv
My Bean class configs are
My JMS connection factory as below.
#Bean
public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
}
Not sure what else I am missing here. Any insight will be really helpful.
Instead of my existing project, if i create any simple demo project it is working as expected.
The class PickMyMessage is not a Spring bean, that's why Spring ignores it. Declare it as a #Bean or as a #Component.
May be there are further problems. But this is the first one that needs to be resolved.
I'm trying to integrate my Spring Boot version 2.0.1.RELEASE with Swagger.
From this blog post it seemed like it will be easy by just adding two Maven dependencies and everything should work.
So I added the following dependencies to the pom:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
And created the SwaggerConfig bean:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
return docket;
}
}
And in the properties file I ended up with these 3 entries during the attempts to make it work:
spring.application.name=cat-service
management.server.servlet.context-path=/cat-service
server.servlet.contextPath=/cat-service
But at the end, when accessing
http://localhost:8080/cat-service/api/v2/api-docs
or the UI page at
http://localhost:8080/cat-service/swagger-ui.html
I get a page not found error.
I found this issues in the swagger github page and this question in stackoverflow but I was not able to change my 404 error.
I was able to make it work with Spring boot version 2.0.4.RELEASE and this blog post:
I added these dependencies:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
And this configuration file:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
#Configuration
#EnableSwagger2
public class SpringFoxConfig {
#Bean
public Docket apiDocket() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
And it worked.
The Swagger UI can be reached at /swagger-ui.html#
Please check the reference: https://springfox.github.io/springfox/docs/current/
"2.1.3. Migrating from existing 2.x version"
You can remove springfox-swagger2 and springfox-swagger-ui from your pom.xml and add springfox-boot-starter instead (for example version 3.0.0). Also you can remove the #EnableSwagger2 annotations
And: "swagger-ui location has moved from https://host/context-path/swagger-ui.html to https://host/context-path/swagger-ui/index.html OR https://host/context-path/swagger-ui/ for short. This makes it work much better with pulling it as a web jar and turning it off using configuration properties if not needed."
First add SwaggerConfig.java file at the same package of your springboot file like the following example.
#Configuration
#EnableSwagger2
#EnableWebMvc
public class SwaggerConfig extends WebMvcConfigurerAdapter {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
#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/");
}
}
try this
http://localhost:8080/spring-security-rest/api/swagger-ui.html
or
http://localhost:8080/spring-security-rest/swagger-ui.html
If that does not work, try to change the path in application.properties
Add this to application.properties:
server.servlet-path=/loop-service
and try the following urls:
http://localhost:8080/loop-service/swagger-ui.html (UI Docs)
http://localhost:8080/loop-service/v2/api-docs (JSON Docs)
Result :
OpenAPI
Just use springdoc-openapi-ui instead.
The Maven dependency:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.11</version>
</dependency>
Or Gradle:
implementation 'org.springdoc:springdoc-openapi-ui:1.6.11'
Links
UI: http://localhost:8080/swagger-ui.html
Json: http://localhost:8080/v3/api-docs
Yaml: http://localhost:8080/v3/api-docs.yaml
That's really all there is to it... No annotations / configuration needed. Cheers!
For Spring Security
If you are using spring security, make sure you can reach these paths for it to work:
"/swagger-ui.html" <-- for UI
"/swagger-ui/**" <-- for UI redirects
"/v3/api-docs/**" <-- for json docs and openapi configuration
"/v3/api-docs.yaml" <-- for yaml docs
For more information:
https://www.baeldung.com/spring-rest-openapi-documentation
I also had the same issue (404 Not Found with springfox 3.0.0). By setting the logging level to "DEBUG", I was able to see endpoints for /v3/api-docs and they worked, but there was nothing about "swagger-ui".
I finally found https://github.com/springfox/springfox/issues/3285, which indicates that:
the new url in 3.0.0 is /swagger-ui/index.html or /swagger-ui/ rather than /swagger-ui.html"
Could they not have added a debug log to indicate where the swagger UI is available?
Springfox swagger UI opens at path /swagger-ui/index.html.
Make sure you are allowing correct ResourceLocations and path excluded from interceptors
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/swagger-ui.html");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
Note: i'm using Springfox swagger version 3.0.0 and Springboot version 2.5.3
This worked for me, I used the WebMvcConfigurer instead of the WebMvcConfigurerAdapter because that class is already deprecated.
#Configuration
#EnableSwagger2
public class SwaggerConfig implements WebMvcConfigurer {
#Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.illary.controller"))
.paths(PathSelectors.any())
.build()
.apiInfo(metaData());
}
private ApiInfo metaData() {
return new ApiInfoBuilder()
.title("Spring Boot Swagger App")
.description("\"Spring Boot Swagger Server App\"")
.version("1.0.0")
.license("Apache License Version 2.0")
.licenseUrl("https://www.apache.org/licenses/LICENSE-2.0\"")
.build();
}
public ApiInfo apiInfo() {
final ApiInfoBuilder builder = new ApiInfoBuilder();
builder.title("Swagger Test App").version("1.0").license("(C) Copyright Test")
.description("The API provides a platform to query build test swagger api");
return builder.build();
}
#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/");
}
}
For Spring Boot, just use the dependency below, it's all it needs to work on the URL /swagger-ui/ (the trailing slash is mandatory).
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
Before trying that I was trying to use the classic dependencies of swagger2 and swagger-ui, and none of the suggested URLs were working.
Another possibility is the location of your Swagger config file; you need to place it at the same package or a subpackage of the spring boot file's.
Like the above picture:
Don't forget to change server.contextPath to server.servlet.contextPath if you upgrade Spring Boot to 2+.
Solution: You just need to remove #EnableWebMvc from the configuration classes.
Description: #EnableWebMvc turn on the class org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport. In spring-boot, there is an autoconfiguration class org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration, which has the annotation #ConditionalOnMissingBean(WebMvcConfigurationSupport.class).
What we get in the end: By adding #EnableWebMvc to the project, we ourselves become responsible for everything, since we turn off spring-boot auto-configuration.
After adding below line in application.properties, it started working
spring.web.resources.static-locations: classpath:/webapp/
But I'm not sure why do we have to add this.
Adding the code, which I think could be relevant. Dependencies are as below:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!-- Swagger Dependencies -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
And main class as
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
#SpringBootApplication
#EnableSwagger2
public class ProxyApplication {
public static void main(String[] args) {
SpringApplication.run(ProxyApplication.class, args);
}
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ghsatpute.proxy"))
.paths(PathSelectors.any())
.build();
}
}
Adding this if it can help somebody. This URL worked for me
http://localhost:8003/v2/api-docs
and for swagger-ui this url worked for me out of the box:
http://localhost:8003/swagger-ui.html
Check if you are using right configured url for swagger specs. The urls like http://localhost:8080/spring-security-rest/api/swagger-ui/
didn't work , and I had been getting 404.
Below are the steps to enable swagger in spring boot application:
Add springfox-swagger2 and springfox-swagger-ui dependencies.
To enable the Swagger2 in your project you should use #EnableSwagger2
Define a docket bean ad below
#Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2).groupName("group-name").apiInfo(apiInfo()).select().paths(predicate()).build();
}
private Predicate<String> predicate() {
return or(regex("/api/v1.*"), regex("/api/v2.*"));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("The title of the your choice")
.contact("your#email.com").license("Licence name ").version("1.0").build();
}
Swagger UI url pattern:
localhost:<server.port>/<server.servlet.context-path>/swagger-ui.html
For anyone still looking for this, using OpenAPI 3.0, not Springfox, I got it running using WebMvcConfigurationSupport without the need of #EnableWebMvc.
The only Dependency needed:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.8</version>
</dependency>
I initially just got the JSON and a 404 on the UI.
What ultimately worked was adding the right resource handlers to my WebConfig and configuring my SecurityConfig to allow unrestricted access for everyone.
The resource handlers in my WebConfig:
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/4.10.3/swagger-initializer.js")
.addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/4.10.3/index.html")
.addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/4.10.3/swagger-ui.css")
.addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/4.10.3/index.css")
.addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/4.10.3/swagger-ui-bundle.js")
.addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/4.10.3/swagger-ui-standalone-preset.js");
}
Important was to add each one specifically (with version and everything, ** didn't work). Otherwise it wouldn't run in the cloud.
I also needed to add the paths "/v3/api-docs/**", "/swagger-ui/**" my SecurityConfig to allow access to these resources. So in my case I added those here:
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.addFilterBefore(...
.authorizeRequests().antMatchers("/v3/api-docs/**", "/swagger-ui/**", "...")...
}
and:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.GET, "/v3/api-docs/**", "/swagger-ui/**", "...");
}
It became working for me after removing #EnableWebMvc
Faced same issue just resolved with change in dependency
Refer https://www.vojtechruzicka.com/documenting-spring-boot-rest-api-swagger-springfox/
compile "io.springfox:springfox-swagger2:2.9.2"
Previously was using - Not working
compile group: 'io.springfox', name: 'springfox-swagger2', version: '3.0.0'
I was stuck for a day with this issue. My issue was incorrect port configuration. Check your application.yml/application.properties file for port configuration. There I had mistakenly added both
server.port , management.server.port with different values 🤣 .
I am trying to develop a simple JAX-RS based web service using Spring Boot version 1.4.1.RELEASE. However getting this exception -
java.lang.IllegalStateException: No generator was provided and there is no default generator registered
at org.glassfish.hk2.internal.ServiceLocatorFactoryImpl.internalCreate(ServiceLocatorFactoryImpl.java:308) ~[hk2-api-2.5.0-b05.jar:na]
at org.glassfish.hk2.internal.ServiceLocatorFactoryImpl.create(ServiceLocatorFactoryImpl.java:268) ~[hk2-api-2.5.0-b05.jar:na]
at org.glassfish.jersey.internal.inject.Injections._createLocator(Injections.java:138) ~[jersey-common-2.23.2.jar:na]
at org.glassfish.jersey.internal.inject.Injections.createLocator(Injections.java:123) ~[jersey-common-2.23.2.jar:na]
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:330) ~[jersey-server-2.23.2.jar:na]
at org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:392) ~[jersey-container-servlet-core-2.23.2.jar:na]
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:177) ~[jersey-container-servlet-core-2.23.2.jar:na]
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:369) ~[jersey-container-servlet-core-2.23.2.jar:na]
Here are my program details -
Dependencies included in POM.xml -
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
And here is JerseyConfig file -
package com.test.main;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;
import com.test.resources.TutorialResource;
#Component
public class JerseyConfig extends ResourceConfig{
public JerseyConfig() {
register(TutorialResource.class);
packages("com.test.resources");
}
}
Important: Looks like this issue is not present in most recent versions of Spring Boot. However the content of this answer can still be used as a guide when you want to create an application with Spring Boot and Jersey.
The layout of the JAR has changed in Spring Boot 1.4.1
The layout of executable jars has changed in Spring Boot 1.4.1: application’s dependencies are now packaged in BOOT-INF/lib rather than lib, and application’s own classes are now packaged in BOOT-INF/classes rather than the root of the jar. And it affects Jersey:
Jersey classpath scanning limitations
The change to the layout of executable jars means that a limitation in Jersey’s classpath scanning now affects executable jar files as well as executable war files. To work around the problem, classes that you wish to be scanned by Jersey should be packaged in a jar and included as a dependency in BOOT-INF/lib. The Spring Boot launcher should then be configured to unpack those jars on start up so that Jersey can scan their contents.
I've found that registering classes instead of packages works. See below the steps to create an application with Spring Boot and Jersey.
Creating a web application with Spring Boot and Jersey
Ensure your pom.xml file declares spring-boot-starter-parent as the parent project:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
</parent>
You also need the following dependencies:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
And the Spring Boot Maven plugin:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
For example purposes, create a Jersey resource class annotated with #Path and define a resource method to handle GET requests, producing text/plain:
#Path("/greetings")
public class GreetingResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
public Response getGreeting() {
return Response.ok("Hello, World!").build();
}
}
Then create a class that extends ResourceConfig or Application to register the Jersey resources and annotated it with #ApplicationPath. Registering classes instead of registering packages works with Spring Boot 1.4.1:
#Component
#ApplicationPath("api")
public class JerseyConfig extends ResourceConfig {
#PostConstruct
private void init() {
registerClasses(GreetingResource.class);
}
}
And finally create a Spring Boot class to execute the application:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
If you want to test this web service, you can use the JAX-RS Client API:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class GreetingResourceTest {
#LocalServerPort
private int port;
private URI uri;
#Before
public void setUp() throws Exception {
this.uri = new URI("http://localhost:" + port);
}
#Test
public void testGreeting() {
Client client = ClientBuilder.newClient();
Response response = client.target(uri).path("api").path("greetings")
.request(MediaType.TEXT_PLAIN).get();
String entity = response.readEntity(String.class);
assertEquals("Hello, World!", entity);
}
}
To compile and run the application, follow these steps:
Open a command line window or terminal.
Navigate to the root directory of the project, where the pom.xml resides.
Compile the project: mvn clean compile.
Package the application: mvn package.
Look in the target directory. You should see a file with the following or a similar name: spring-jersey-1.0-SNAPSHOT.jar.
Change into the target directory.
Execute the JAR: java -jar spring-jersey-1.0-SNAPSHOT.jar.
The application should be available at http://localhost:8080/api/greetings.
Note 1: Have a look at the Spring Boot documentation. There's a section dedicated to Jersey.
Note 2: When producing JSON, ensure you have a JSON provider registered. ResourceConfig should take care of that though (just ensure that the dependencies are on the classpath).
Although Jersey cannot scan your classes inside the new version of the fat boot jar, you can achieve the same effect using Spring classpath scanning facilities. This way you can scan a package similarly to ResourceConfig.packages():
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(Provider.class));
scanner.addIncludeFilter(new AnnotationTypeFilter(Path.class));
config.registerClasses(scanner.findCandidateComponents("your.package.to.scan").stream()
.map(beanDefinition -> ClassUtils.resolveClassName(beanDefinition.getBeanClassName(), config.getClassLoader()))
.collect(Collectors.toSet()));
Note: please have a look at the source of org.glassfish.jersey.server.internal.scanning.AnnotationAcceptingListener. This is the stock solution and you can see that it does the same: it scans for classes annotated with #Path or #Provider (but doesn't manage to find anything because of the broken scanning mechanism).
Update:
I had a custom config which didn't extend ResourceConfig but returned an instance of it as a bean.
If you look at the official Spring example, you can insert the code above into the JerseyConfig() constructor (instead of the two register(...) calls). The only difference is that instead of calling config.registerClasses(...) you simply call registerClasses(...) in the constructor.
I think you should annotate your JerseyConfig with #Configuration and not #Component.