I created a Spring Boot Maven project, however my RequestMapping, as well as localhost:8080 return a 404 error page. I think the issue is with how my packages are setup, but I've tried solutions in multiple questions, and I still cant get around the error page. Could you guys point me in the right direction as to how to resolve this issue? Perhaps I need to add the Component annotation above my Main class? But I've tried this solution, and the error still persists.
Here is my package structure:
/src/main/java
ControllerLayer
UsersController.java
DataAccessLayer
UsersDAL.java
ServiceLayer
UsersService.java
Main
Main.java
Main.java:
#SpringBootApplication(scanBasePackages = {
"/src/main/java/ControllerLayer", "/src/main/java/DataAccessLayer",
"/src/main/java/ServiceLayer" })
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
UsersController.java:
import Entities.Users;
import ServiceLayer.UsersService;
#RestController
#RequestMapping("/users")
public class UsersController {
#Autowired
private UsersService usersService;
#RequestMapping(value =
"/create/{userId}/{userPassword}/{userAge}/{userEmail}"
+ "/{userFirstName}/{userlastName}", method =
RequestMethod.POST)
public void createUser(#PathVariable("userId")String userId,
#PathVariable("userPassword")String userPassword,
#PathVariable("userAge")int userAge,
#PathVariable("userEmail")String userEmail,
#PathVariable("userFirstName")String userFirstName,
#PathVariable("userLastName")String userLastName) {
usersService.createUser(new Users(userId, userPassword,
userAge, userEmail, userFirstName, userLastName));
}
}
UserService.java
import DataAccessLayer.UsersDAL;
import Entities.Users;
#Service
public class UsersService {
#Autowired
private UsersDAL usersDAL;
public void createUser(Users user) {
usersDAL.createUser(user);
}
}
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>xProjectAlpha</groupId>
<artifactId>org.htech.xProjectAlpha</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.9.Final</version><!--$NO-MVN-MAN-VER$-->
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
When a request is sent, then a response shall be returned. In your case, you didn't send any content with the response and that's why you get 404 error (page not found).
In main.java, try:
#SpringBootApplication(scanBasePackages = {
"ControllerLayer", "DataAccessLayer",
"ServiceLayer" })
Your package names shouldn't include the root path in the project.
It is advisable to have spring boot Application class in root package and have all other classes in package structure below that package .You don't have to worry about component scan as an example
package com.igt.customer;
import java.util.Arrays;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class CustomerApplication {
public static void main(String[] args) {
SpringApplication.run(CustomerApplication.class, args);
}
#Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
};
}
}
Controller class
package com.igt.customer.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class EmployeeController {
#RequestMapping("/employee")
public String employee() {
return "Greetings from Sam!";
}
}
running the application (go to the directory of your application on cmd )
E:\MongoDb\New folder\customer>mvn install -U -e
you should see this in the end if its fine
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10.686 s
[INFO] Finished at: 2017-08-16T16:39:57+05:30
[INFO] Final Memory: 21M/219M
[INFO] ------------------------------------------------------------------------
run the jar file
E:\MongoDb\New folder\customer\target>java -jar customer-0.0.1-SNAPSHOT.jar
accessing the application
http://localhost:8080/employee
Notice application name is not required in URL
P.S i have written extra detail here as i have experienced if you are new to spring boot building and running the application is a challenge , in my application i had created a rest controller in the same package as the Application class with RequestMapping "/" as i was getting 404 error , Please see the link below as a reference
spring boot application
This issue will be simply solved if you remove the main package of the main.java class.
The new structure will be:
/src/main/java
Main.java
ControllerLayer
UsersController.java
DataAccessLayer
UsersDAL.java
ServiceLayer
UsersService.java
In my Spring boot application, there is no need to scan the base packages manually because all the configurations are embedded in a single annotation #SpringBootApplication. Please refer to this link.
I don't understand how the base packages are initially configured. Can someone please explain this?
For example, if your base package looks like:
com.example.myapp.SpringApplication
... it means your application takes base packages as com.example.myapp. So if you can create all Controllers, Service, Repository under com.example.myapp in the sense it will load your Controllers, Service, Repository easily or else it can't able to load. This is because springbootapplication intially sets the base packages and loads whatever java classes are inside the base package. So because of this you get a 404 error in the browser as well as in postman. So try to match with base package.
Related
I have a Spring REST project that is redirecting all requests to error page, even if they are mapped in the controller.
I reduced the code to the smallest possible version that produces the error:
Here is the project structure:
Here is the Application class (The imports are removed to make the thread easier to read):
package com.example.demo;
#Controller
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#GetMapping("/greeting")
#ResponseBody
public String greeting() {
return "greeting";
}
}
Originally I hade a sperate controller from the App class, but moved the controller code to the app class to make sure that this is not a project structure problem
Here is the controller code (Tried with and without it, and received the same error):
#Controller
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#GetMapping("/hello")
#ResponseBody
public String greeting() {
return "greeting";
}
}
(Both http://localhost:8080/greeting as well as well http://localhost:8080/hello return the same error page)
Dependencies and plugins from the pom file:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
(Tried with and without tomcat as dependency and nothing changed)
And lastly here is the error message I receive in the browser when I visit the links (http://localhost:8080/greeting and http://localhost:8080/hello):
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Nov 27 00:16:08 CET 2022
There was an unexpected error (type=Not Found, status=404).
Edit:
After setting debug to true in project.properties, here is the error message I see in console (Worth mentioning that the project ran with no issues when I tried it on another system (Same OS)):
GET "/greeting", parameters={}
Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
Resource not found
Completed 404 NOT_FOUND
"ERROR" dispatch for GET "/error", parameters={}
(Timestamps are removed to make reading easier)
Did you tried making call to the endpoint via postman ? If so, can you try again after removing #ResponseBody annotation.
Instead of #Controller use #RestController
I'm trying to connect to my http://localhost: 8080/spring-boot-test/ui, but unfortunately I fail because I have errors on Eclips. WildFly 23 theoretically worked, because I normally get their localhost
An internal error occurred during: "Updating Deployment Scanners for Server: WildFly 23".
Could not initialize class org.wildfly.security.auth.client.DefaultAuthenticationContextProvider
An internal error occurred during: "Checking Deployment Scanners for server".
Could not initialize class org.wildfly.security.auth.client.DefaultAuthenticationContextProvider
When I try to redirect the directory in standalone.xml to a target with META-INF and WEB-INF, I come across two ERRORs
ERROR [org.jboss.as.server.deployment.scanner] (DeploymentScanner-threads - 1) WFLYDS0011: The deployment scanner found a directory named META-INF that was not inside a directory whose name ends with .ear, .jar, .rar, .sar or .war. This is likely the result of unzipping an archive directly inside the C:\Users\adame\eclipse-workspace\spring-boot-test\target directory, which is a user error. The META-INF directory will not be scanned for deployments, but it is possible that the scanner may find other files from the unzipped archive and attempt to deploy them, leading to errors.
ERROR [org.jboss.as.server.deployment.scanner] (DeploymentScanner-threads - 1) WFLYDS0011: The deployment scanner found a directory named WEB-INF that was not inside a directory whose name ends with .ear, .jar, .rar, .sar or .war. This is likely the result of unzipping an archive directly inside the C:\Users\adame\eclipse-workspace\spring-boot-test\target\ directory, which is a user error. The WEB-INF directory will not be scanned for deployments, but it is possible that the scanner may find other files from the unzipped archive and attempt to deploy them, leading to errors.
Pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.adamkaim.spring</groupId>
<artifactId>spring-boot-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<properties>
<java.version>16</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
<packaging>war</packaging>
</project>
App.java
package com.adamkaim.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Address.java
package com.adamkaim.spring;
import org.springframework.stereotype.Component;
#Component
public class Address {
private String address="Wall Street 34";
public String getAddress() {
return this.address;
}
}
Student.java
package com.adamkaim.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class Student {
#Autowired
private Address address;
public String showInfo(){
return this.address.getAddress();
}
}
MainView.java
package com.adamkaim.spring;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Title;
import com.vaadin.server.VaadinRequest;
import com.vaadin.spring.annotation.SpringUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
#SuppressWarnings("serial")
#SpringUI(path="/ui")
#Title("Titlett")
#Theme("valo")
public class MainView extends UI{
#Override
protected void init(VaadinRequest request) {
final VerticalLayout verticalLayout = new VerticalLayout();
verticalLayout.addComponent(new Label("Welcome"));
Button button = new Button("Click me");
verticalLayout.addComponent(button);
button.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
verticalLayout.addComponent(new Label("Button is clicked.."));
}
});
setContent(verticalLayout);
}
}
I was having the same error when trying to start a Wildfly 19.1.0 container from Eclipse (2021-09). The container seemed to start successful, but this message was driving me crazy.
After a while I came across this message on the Wildfly Google Groups, and this solved my problems!
Adding --add-opens=java.base/java.security=ALL-UNNAMED to eclipse.ini
fixed the issue on my side
Thanks to the original author, Rahim Alizada, in https://groups.google.com/g/wildfly/c/_OuPrpsF2pY/m/xLt6u-IfBgAJ.
The option --add-opens opens up the informed modules (all types and members) at runtime, allowing deep reflection from the target modules (in this case, everyone else - ALL-UNNAMED).
More about this option in the JEP 261.
I tried running Eclipse with only Java 8 in my system, if I remember it well I got it working, but some modules on these newer Eclipse versions require Java 11 to load properly.
I came across this error during the deployment of wildfly 23 server in eclipse
An internal error occurred during: "Updating Deployment Scanners for Server: WildFly 23". Could not initialize class org.wildfly.security.auth.client.DefaultAuthenticationContextProvider
This solved my issue too
Adding --add-opens=java.base/java.security=ALL-UNNAMED to eclipse.ini
TL;DR: I have a Spring MVC Hello, World! application that works on Tomcat 9. The same application on Tomcat 10 gives a 404 error for web request mappings.
The Problem
When deploying a Spring MVC 5 Hello, World! application to Tomcat 10, the application gives a 404 error for web request mappings. The same Hello, World! application works on Tomcat 9. It displays the Hello, World! message on Tomcat 9.
What I Expected
I expected the application to display the Hello, World! message on Tomcat 10.
Environment
MS Windows 10
Tomcat 10.0.2
Spring MVC 5.3.3
Research I Performed
I researched in the Spring Reference Manual, Section on Web Servlet. I also tested Spring MVC Tutorials online. These tutorials worked on Tomcat 9. However, the same tutorials failed on Tomcat 10. I also performed Google search on Tomcat 10. I saw references to Jakarta EE, but I am not sure if this is the source of the issue. Java EE 8 and Jakarta EE 8 are backwards compatible.
How to Reproduce
I created a very basic Hello, World! project to test this out. Here is the code that I'm using for the project.
File pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.spring</groupId>
<artifactId>example-spring</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<properties>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
</plugin>
</plugins>
</build>
</project>
File ProjectInitializer.java
package com.example;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class ProjectInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { PureJavaConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
}
File PureJavaConfig.java
package com.example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableWebMvc
#ComponentScan("com.example")
public class PureJavaConfig {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setSuffix(".jsp");
resolver.setPrefix("/WEB-INF/jsp/");
return resolver;
}
}
File TutorialController.java
package com.example;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class TutorialController {
#GetMapping("/")
public String home() {
return "home";
}
}
File home.jsp
<html>
<body>Hello, World! of Spring! <%= java.time.LocalDateTime.now() %></body>
</html>
This project runs okay on Tomcat 9. It displays the Hello, World! message. However, when I run on Tomcat 10, I get a 404 error message.
TL;DR: Spring MVC 5 does not run on Tomcat 10 because of the package renaming from javax.* to jakarta.*.
After further research, I was able to find the answer to my question. Spring MVC 5 does not work on Tomcat 10. This is because Tomcat 10 is based on Jakarta EE 9 where package names for APIs have changed from javax.* to jakarta.*.
Tomcat 10 mentioned this on the download webpage:
Users of Tomcat 10 onwards should be aware that, as a result of the
move from Java EE to Jakarta EE as part of the transfer of Java EE to
the Eclipse Foundation, the primary package for all implemented APIs
has changed from javax.* to jakarta.*. This will almost certainly
require code changes to enable applications to migrate from Tomcat 9
and earlier to Tomcat 10 and later.
For Spring MVC 5, the Spring MVC DispatcherServlet has a dependency on the javax.servlet.* package namespace. This is using the Java EE 8 javax package naming. Since Tomcat 10 is based on Jakarta EE 9, the packages for javax naming are not supported. This explains why Spring MVC 5 does not work on Tomcat 10.
There are GitHub issues filed against the Spring Framework regarding this:
Spring core 5 is not starting on Tomcat 10
Support for Jakarta EE 9 (annotations and interfaces in jakarta.* namespace)
In my case, instead of migrating to Tomcat 10, I will stay on Tomcat 9 until the Spring framework is upgraded to Jakarta EE 9.
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.
We are creating a spring-boot jersey application.
Now we want to create executable war file.
The problem is the application runs fine when I run it with
mvn spring-boot:run
But when I try to package it to war and run it with java -jar ABD.war
its giving the following error
Caused by: java.io.FileNotFoundException: /Users/ABC/ABD-0.0.1-SNAPSHOT.war!/WEB-INF/classes (No such file or directory)
Caused by: org.glassfish.jersey.server.internal.scanning.ResourceFinderException:
Here are the part of pom.xml I'm using ,
<packaging>war</packaging>
.
.
.
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<org.slf4j.version>1.7.7</org.slf4j.version>
<maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
<java.version>1.8</java.version>
</properties>
.
.
.
.
.
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Although when I unpack the war file I can see the WEB-INF/classes folder is there.
OK found the solution.
I have a jersery config class, where I added all of controllers class with packages().
When I commented it out and change it to register("controller.class") It started to work!
#Configuration
#ApplicationPath("/path")
#Controller
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(MultiPartFeature.class);
register(OneController.class);
//packages("com.controllers");
}
}
#
Update
#
private static final Logger logger = LoggerFactory.getLogger(OneController.class);
public JerseyConfig() {
scan("com.somepackages");
}
public void scan(String... packages) {
for (String pack : packages) {
Reflections reflections = new Reflections(pack);
reflections.getTypesAnnotatedWith(Path.class)
.parallelStream()
.forEach((clazz) -> {
logger.info("New resource registered: " + clazz.getName());
register(clazz);
});
}
}
#
With this solution you can get all controllers in jersey register through package scan.