Spring-boot jersey maven failed to run war file - java

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.

Related

How do I include src/test/java files to run TestNG tests?

I'm just learning Java and could use your help. I'm using Eclipse, and created a Maven project using the org.openjfx archetype. Everything seems to work fine except when I try to write tests in src/test/java, which causes an error.
An error occurred while instantiating class
starcraft.warcraft.test.TestClass: Unable to make public
starcraft.warcraft.test.TestClass() accessible: module
starcraft.warcraft does not "exports starcraft.warcraft.test" to
module org.testng
This is how I created the project with default settings in Eclipse:
Project Setup with Maven Archetype Selection
Now, when Eclipse creates the project, it doesn't have a src/test/java folder, so I create that manually. Then I create a class called "TestClass" inside a package "starcraft.warcraft.test" inside src/test/java, and I add a simple method to test inside the App class called "adder". You can see the project structure
Project Structure
package starcraft.warcraft;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage stage) {
var javaVersion = SystemInfo.javaVersion();
var javafxVersion = SystemInfo.javafxVersion();
var label = new Label("Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".");
var scene = new Scene(new StackPane(label), 640, 480);
stage.setScene(scene);
stage.show();
}
// WILL TEST THIS METHOD
public static int adder(int digit1, int digit2) {
return digit1 + digit2;
}
public static void main(String[] args) {
launch();
}
}
Now I want to use TestNG for the tests, and so I include it in my POM which is
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>starcraft</groupId>
<artifactId>warcraft</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.testng/testng -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.5</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.6</version>
<executions>
<execution>
<!-- Default configuration for running -->
<!-- Usage: mvn clean javafx:run -->
<id>default-cli</id>
<configuration>
<mainClass>starcraft.warcraft.App</mainClass>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
This is the default POM created by the Maven archetype except for the TestNG dependency I added. When I try to use TestNG, Eclipse makes me add it to the module path like so:
Maven saying I need to add the TestNG library
And here is my module-info:
module starcraft.warcraft {
requires javafx.controls;
requires org.testng;
exports starcraft.warcraft;
}
OK, all good so far, but now when I try to run my test inside TestClass:
package starcraft.warcraft.test;
import org.testng.annotations.Test;
import starcraft.warcraft.App;
public class TestClass {
#Test
public void testAdder() {
int sum = App.adder(1, 2);
System.out.println(sum);
}
}
I get the error, which again is
An error occurred while instantiating class
starcraft.warcraft.test.TestClass: Unable to make public
starcraft.warcraft.test.TestClass() accessible: module
starcraft.warcraft does not "exports starcraft.warcraft.test" to
module org.testng
I can't figure out how to do the export. When I try making the entry in module-info, it doesn't give me the option of adding the package in src/test/java, the only packages it allows me to choose from are in src/main/java.
I don't understand modules well. How can I get the program to let me run tests from src/test/java?
Thanks everyone who looked at this. I solved it by following these steps:
Delete module-info.java. This turns it into a nonmodular project which is fine for me. I hesitated to do this because the Maven JavaFX archetype included it, but as it says here somewhere
https://openjfx.io/openjfx-docs/#IDE-Eclipse
you can just delete it after its created.
The problem then if you try to run the project is it will give you a warning that JavaFX isn't included as a module. It will still run, but its best to get rid of this incase of problems down the road. So you need to download the JavaFX libraries, place them in your hard drive, and then include them in your project via VM arguments in Eclipse:
right click project -> Run configuration -> Arguments tab -> add in the VM arguments area something like:
--module-path [fully qualified path to lib folder containing downloaded JavaFX] --add-modules javafx.controls
path would be like "C:\javafx\lib" or wherever you placed the downloaded JavaFX.
Then it should run, and project will still build using Maven, but I'm not sure if its using the JavaFX which is still in the Maven POM or the one I specified on the C drive. But it works. Any help on whats happening there would be appreciated. Thanks

An internal error occurred during: "Updating Deployment Scanners for Server: WildFly 23"

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

Spring Boot throws ClassNotFoundException with maven dependency for another project

I have Spring Boot project with simple EnvironmentPostProcessor implementation:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
public class DevProfilerResolverEnvironmentPostProcessor implements EnvironmentPostProcessor {
#Override
public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {
if (configurableEnvironment.getActiveProfiles().length == 0) {
if (System.getenv().get("OS").contains("Windows")) {
configurableEnvironment.addActiveProfile("DEV");
}
}
}
}
Also, I registered this class to sprig.factories:
org.springframework.boot.env.EnvironmentPostProcessor = com.example.demo.DevProfilerResolverEnvironmentPostProcessor
Now structure looks like:
Snippet from pom file:
<?xml version="1.0" encoding="UTF-8"?>
<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</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
I executed with Maven:
mvn install
Now I want to use this EnvironmentPostProcessor implementation on another spring boot project. Thus I added it to dependency section for the new project:
<dependency>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
And I wrote simple service usage:
#Service
#Profile("DEV")
public class DeveloperService {
#Scheduled(cron = "1/1 * * * * ?")
public void doWork() {
System.out.println("Developers.... ");
}
}
and enabled scheduling for main class:
#SpringBootApplication
#EnableScheduling
public class LvivBootApplication {
public static void main(String[] args) {
SpringApplication.run(LvivBootApplication.class, args);
}
}
However, I got following exception after main execution:
14:56:09.822 [main] ERROR org.springframework.boot.SpringApplication - Application startup failed
java.lang.IllegalArgumentException: Unable to instantiate factory class: org.springframework.boot.env.EnvironmentPostProcessor
Caused by: java.lang.ClassNotFoundException: com.example.demo.DevProfilerResolverEnvironmentPostProcessor
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
and dependency is added to the new project:
I am running it exactly on Windows environment.
The idea is when OS is Windows add DEV profile for the first project.
Second has service which prints to console dummy info when the profile is DEV and scheduled this printing for every second.
I can't find what is missed at this example?
SOLUTION:
For making from first project library jar pom should be fixed like following:
<!--<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
</plugins>
</build>-->
Or simple to eliminate this plugin from pom file.
Your first jar is not a library jar. It's a repackaged boot jar, created by the spring boot plugin, containing the first spring boot application and all its dependencies, intended to be executed, and not to be used as a library.
If you want to use that as a library, you need to use the non-repackaged jar file, containing only the classes and resources of the project.
It's quite bizarre to have an application depend on another application, though. You should create a library project, only containing common common classes and resources, and use that as a dependency to your two spring boot applications.
I am posting because I had a similar error in similar circumstances - I searched for hours and the solution was very easy. I am using Eclipse for debugging - Eclipse doesn't use the produced jar from Maven build - it uses its own set of build paths with, as far as i understood, exploded classes etc.
My Maven project, the one that produced a JAR that I was including into my main project POM, didn't declare in any way some kind of dependency to the main project, e.g. via a common parent or whatever.
Eclipse seems to not understand that one of the dependencies I was using in the POM was a result of another local project - somehow, although the file (jar-with-dependencies) was in the Maven cache alright, it wasn't picking it up to copy to its own aforementioned set of classpath directories.
I needed to explicitly add it (my library project) into the main project via Project -> Properties -> Java Build Path -> Projects - adding it to the list titled "Required projects on the build path:"

Spring Boot | localhost: 8080 404 error page displayed

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.

Spring-Boot Resource Not Found when using executeable Jar

again I face a strange issue and hope someone here can help.
I have a spring boot backend module, what works in eclipse well and application is executeable when starting main in application.java. Everything fine.
My application makes import of example data to database using csv-files what is included in src/main/resources folder. As mentioned, when starting in eclipse everything works.
Now I would like to execute it as executable jar, the application begins to start and then it failed to start, because it cannot find the csv files. The path what it prints out, where it looked for the files, is correct and the csv files are in the jar included.
The Pom of the module looks like follows:
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>at.company.bbsng</groupId>
<artifactId>bbsng-import</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>bbsng-import-backend</artifactId>
<name>bbsng-import-backend</name>
<properties>
<start-class>at.company.bbsng.dataimport.Application</start-class>
</properties>
<dependencies>
<!-- SPRING ... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
<!-- EXCLUDE LOGBACK AND USE LOG4J -->
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- COMMONS ... -->
...
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Path to csv-files are configured in propery files as follows:
# EXAMPLE PATH
csv.path=config/csv/
The part of java config file is as follows:
...
#Value("${csv.path}")
private String csvExamplePath;
#Bean
public Resource addressResource() {
return new ClassPathResource(csvExamplePath + CSV_ADDRESS);
}
...
In the jar the files are located at path
\config\csv\
Stacktrace:
Caused by: java.io.FileNotFoundException: class path resource [config/csv/Company.csv] cannot be resolved to absolute file path because it does not reside in th
e file system: jar:file:/C:/Development/Projekte/bbsng/trunk/import/backend/target/bbsng-import-backend-0.1.0-SNAPSHOT.jar!/config/csv/Company.csv
at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:207)
at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:52)
at at.compax.bbsng.dataimport.app.source.company.CompanyGenerator.init(CompanyGenerator.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java
Again, the application works as expected when starting it from eclipse, only executable jar complains about missing csv-files, what are in jar already.
Any clue would be great.
Okay, already I found the real problem and the solution.
First, the application use the correct path to the csv files, but there is another issue when using an executable jar what I found under following link. Stackoverflow-Link
Before I come to issue with executable jar I used following solution for getting CSV-File (Issue is getFile()):
final List<String> resourceLines = FileReadUtils.readLines(specialisationResource.getFile());
for (final String line : resourceLines) {
data.add(getNewTransientSpecialisation(line));
}
But in executeable jar I cant use my resource as file, I need to use it as stream, see provided link above. So I needed to change my code. If you prefer using native java, you can do follows:
final InputStream inputStream = specialisationResource.getInputStream();
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = bufferedReader.readLine()) != null) {
data.add(getNewTransientSpecialisation(line));
}
I prefer using frameworks and use apache commons like follows:
final List<String> resourceLines = IOUtils.readLines(specialisationResource.getInputStream());
for (final String line : resourceLines) {
data.add(getNewTransientSpecialisation(line));
}
So just remember, don't use File() for getting resource, always use stream do avoid that issue from beginning :-)
Hope that helps someone.
I encountered this limitation too and created this library to overcome the issue: spring-boot-jar-resources
It basically allows you to register a custom ResourceLoader with Spring Boot that extracts the classpath resources from the JAR as needed, transparently:
new SpringApplicationBuilder()
.sources(Application.class)
.resourceLoader(new JarResourceLoader())
.run(args);
With that ResourceLoader you can do resource.getFile() on any classpath resource.
Now I needed to find xmlFiles into a resource folder from a JAR and facing similar problems described here already. I would like to share my findings and how I got it work, maybe it is helpful.
In a jar I have "db-input" folder under src/main/resources with any number for xml-Files for DB-Input. My application is Spring-Based:
#Component
public class DatabaseInitializer implements InitializingBean {
#Autowired DomainConfigurationRepository repository;
#Autowired MarshallerService marshallerService;
#Autowired ApplicationContext context;
#Override
public void afterPropertiesSet() throws Exception {
final Resource[] resources = context.getResources("classpath*:db-input/*");
final Set<String> filePaths = findInputFileNames(resources);
final Set<DomainConfiguration> configurations = createConfigurations(filePaths);
repository.save(configurations);
}
private Set<DomainConfiguration> createConfigurations(final Set<String> filePaths) throws Exception {
final Set<DomainConfiguration> configurations = new HashSet<>();
for(final String path : filePaths){
final Resource resource = context.getResource(path);
final DomainConfigurationXO xoConfiguration = marshallerService.unmarshal(resource.getInputStream());
final DomainConfiguration configuration = PopulationUtils.getPopulatedConfiguration(xoConfiguration);
configurations.add(configuration);
}
return configurations;
}
public Set<String> findInputFileNames(final Resource[] inputDirectoryResources) throws IOException {
return Arrays.stream(inputDirectoryResources)
.map(resource -> extractURI(resource))
.collect(Collectors.toSet());
}
private String extractURI(Resource resource){
try {
return resource.getURI().toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

Categories

Resources