Spring-Boot Resource Not Found when using executeable Jar - java

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);
}
}
}

Related

How to import java project as module in java web project using intelijj?

I wanted to use the class from basic java project inside java servlet class which is defined in another project.
I tried importing project as module through the module dependency InteliJ menu.
At compile time ,it is not giving any error ,but after running the server(Glassfish) and calling the servlet it is giving below error.
java.lang.NoClassDefFoundError: com/practise/LogFileCreator at UserLoginValidator.dbConnectionMaker(UserLoginValidator.java:31)>
Please find below code which causing error.
below class is from web project
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import java.io.*;
import com.practise.LogFileCreator;
public class UserLoginValidator extends HttpServlet
{
public String LogFilePath="D:\\Logs";
public PrintWriter out;
String errormsg="";
//********************
LogFileCreator l ;
#Override
public void init() throws ServletException {
try {
this.l = new LogFileCreator(LogFilePath); // here i am trying to create object of my class which causing the mentioned error.
l.WriteLog("Hello");
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
}
}
below class is from normal java project
package com.practise;
import java.io.*;
public class LogFileCreator
{
private String filepath;
private StringBuffer sb = new StringBuffer("Log");
private File file;
private FileWriter fileWriter;
private BufferedWriter bufferedWriter;
public PrintWriter p;
public LogFileCreator(String filepath) throws IOException
{
this.filepath=filepath;
String filename=sb.toString().concat(java.time.LocalDate.now().toString());
this.file = new File(this.filepath,filename);
this.fileWriter= new FileWriter(file,true);
if(!file.exists())
{
file.createNewFile();
}
p= new PrintWriter(fileWriter);
}
public void WriteLog(String logMessage){
p.println(java.time.LocalDateTime.now() + " : " + logMessage);
p.flush();
}
}
Here is the image for module dependency I used .
Image
Earlier I was using LogFileCreator.java class from same web project and it was working fine
Here what i am trying to acheive is ,without writing the LogFileCreator class again in web project ,wants to reuse the class written already inside normal java project to print the logs in desired text file .
Any solution/suggestions would be appreciated.
Thank you!
[Edit 1]
pom.xml
<?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>org.example</groupId>
<artifactId>webapp</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>9.4.0.jre11</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>Logging</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<!-- String Driver= "com.microsoft.sqlserver.jdbc.SQLServerDriver";-->
<!-- String dbusername="sa";-->
<!-- String dbpassword="Admin#123";-->
<!-- String connectionString="jdbc:sqlserver://localhost:1433;databasename=Users;";-->
<!-- -->
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
</project>
Here What I did which resolved the above problem.
First I converted my first basic java project to maven project .
secondly I added maven dependency for the 2nd project (web project) and also added the jar file (of first project classes ) inside the web INF/lib directory of web project using below option from InteliJ .
Image
The server's classloader does not have a copy of LogFileCreator.class so the class is not on it's classpath.
It is probably how the deployment file (assuming war) was packaged. How are you building the file? Maven, Gradle, neither, etc.?
Edit
I wanted to give a bit more clarity for others who might stumble upon a similar issue. During compile time, the project/module in question was imported; however, maven does not know to package that library in the generated war. When it was deployed to Glassfish, the war did not contain the LogFileCreator class because it was not packaged in the war. The authors solution worked because maven is packaging the module into the war and Glassfish can now find the class.

JSP in Spring Boot application yields 404 Error no matter what I do

I'll start off by saying I've looked at and tried the solutions in every question regarding this that I can find. The biggest problem is that most of these solutions are very old, and Spring Boot has changed a lot in the last several years. To be clear, I've tried this, this, this, this, and more. I've also read numerous tutorials. Nothing works.
I have a brand new Spring Boot application and I'm trying to get JSP rendering working with it. These are my dependencies:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>[2.3.4.RELEASE,3)</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>[2.3.4.RELEASE,3)</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>[2.3.4.RELEASE,3)</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>[2.8.0,3)</version>
</dependency>
<dependency>
<groupId>de.mkammerer</groupId>
<artifactId>argon2-jvm</artifactId>
<version>[2.7,3)</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>[8.0.21,9)</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>[2.3.2,)</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>[2.3.4.RELEASE,3)</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>[9.0.38,)</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>[2.3.4.RELEASE,3)</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
My project is laid out as follows:
- source
- production
- java
- [my source code packages]
- resources
- WEB-APP
- jsp
- initialization
- begin.jsp
- [my resource packages]
- test
- java
- resources
"WEB-APP/jsp" is just the latest iteration I've tried. I've tried "WEB-INF/jsp", "META-INF/jsp", "webapp/jsp", no parent (just "jsp"), etc., all with the same results.
I know the parent directories are a bit non-standard, but it's configured correctly in Maven and I've confirmed it's not the source of my problems:
<build>
<sourceDirectory>source/production/java</sourceDirectory>
<resources>
<resource>
<directory>source/production/resources</directory>
</resource>
</resources>
...
</build>
My Application class is as follows:
#SpringBootApplication(scanBasePackages="com.my.project")
#EnableWebMvc
#EnableJpaRepositories("com.my.project.repository")
#EntityScan("com.my.project.model")
public class Application
{
private static final Logger LOGGER = LogManager.getLogger(Application.class);
public Application()
{
}
#Bean
public ViewResolver viewResolver()
{
LOGGER.info("Constructing InternalResourceViewResolver[JstlView]");
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-APP/jsp/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
resolver.setRedirectContextRelative(true);
resolver.setRedirectHttp10Compatible(false);
return resolver;
}
public static void main(final String[] args)
{
SpringApplication.run(Application.class, args);
}
}
And my Controller:
#Controller
public class InitializationController
{
private static final Logger LOGGER = LogManager.getLogger(InitializationController.class);
#GetMapping("/initialize_application")
public String beginInitialization(ModelMap model)
{
LOGGER.info("Beginning initialization");
...
LOGGER.info("Returning view");
return "initialization/begin";
}
...
}
On startup I see the "Constructing InternalResourceViewResolver" log entry (my view resolver bean is created). When I go to /initialize_application, I get the following error:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Oct 18 21:45:26 CDT 2020
There was an unexpected error (type=Not Found, status=404).
Looking in the log again, I see "Beginning initialization" and "Returning view," so I know that the 404 is for my JSP and not my controller. My controller is working.
Other things I've tried:
Initially I did not have #EnableWebMvc on my application. Without it, the log was empty except my log statements. When I added #EnableWebMvc, this is now logged with the 404: No mapping for GET /WEB-APP/jsp/initialization/begin.jsp (or whatever other directory I've tried other than "WEB-APP").
I've tried running this directly on the pure command line with mvn spring-boot:run
I've tried running this in IntelliJ IDEA with a Maven run configuration and command spring-boot:run (same result)
I've tried both <packaging>jar</packaging> and <packaging>war</packaging>, but neither make a difference, because neither a JAR nor a WAR are ever made. Maven runs the application directly out of the target/classes directory instead of creating an artifact.
When I've tried WEB-INF or META-INF instead of WEB-APP or webapp or something else, I've seen a logged warning: Path with "WEB-INF" or "META-INF": [WEB-INF/jsp/initialization/begin.jsp]
I have also confirmed that my JSPs are present in target/classes/WEB-APP/jsp (or whatever other directory I've tried other than "WEB-APP"), so they do exist.
I'm at a loss how to proceed. I'm beginning to think I need to ditch Spring Boot and stick with a traditional boilerplate Spring Web MVC application with a Servlet config and a Tomcat installation, but I was really excited about the "just runs" aspect of Spring Boot. Any help would be appreciated.
UPDATE 1
After reading this Spring documentation about JSP limitations, I now know that I have to use <packaging>war</packaging>, and I'm using that now, but it hasn't made a difference. I'm starting to suspect that the underlying problem here is that maven spring-boot:run doesn't create a WAR and run it, it just builds everything to target/classes and runs it from there.
Also, after finding this old, official Spring boot samples application, I've changed my project structure a little:
- source
- production
- java
- [my source code packages]
- resources
- [my resource packages]
- webapp
- META-INF
- WEB-INF
- jsp
- initialization
- begin.jsp
- test
- java
- resources
Updated my view resolver configuration:
resolver.setPrefix("/jsp/");
resolver.setSuffix(".jsp");
And added this to my POM:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<warSourceDirectory>source/production/webapp</warSourceDirectory>
</configuration>
</plugin>
If I run mvn package, my WAR gets created correctly (classes and JSPs all where they should be), but neither mvn spring-boot:run nor mvn package spring-boot:run work—I still get 404 errors resolving my JSPs.
The old Spring Boot sample application linked to above puts the JSPs in WEB-INF/jsp, but I can't do that, because that results in the warning Path with "WEB-INF" or "META-INF": [WEB-INF/jsp/initialization/begin.jsp] (and still 404). What's frustrating is that this sample application doesn't exist anymore, nor does any new variation of it. I can't find any updated version that works with the newest version of Spring Boot. The sample application was deleted in 2.2.x.
Can you try by changing the scope of tomcat-embed-jasper to provided as this dependency is needed to compile JSPs.
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>[9.0.38,)</version>
<scope>provided</scope>
</dependency>
Edit:
I looked for various spring-boot + jsp projects over internet. I noticed that they have they also have spring-boot-starter-tomcat with provided scope. Can you try this.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>[2.3.4.RELEASE,3)</version>
<scope>provided</scope>
</dependency>
References :
https://mkyong.com/spring-boot/spring-boot-hello-world-example-jsp/
https://dzone.com/articles/spring-boot-2-with-jsp-view
Edit-2 :
So this time i created a new springboot project. Did bare minimum setup to get jsp rendered. So basically i followed this tutorial and my project was running fine.
Then I replaced the pom.xml with yours and the i got the same error you mentioned in the question.
Then while doing trial and error i removed the <version>[9.0.38,)</version> from <artifactId>tomcat-embed-jasper</artifactId> and it started working for me.
<!--I have removed version here and it started working for me-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<!-- <version>[9.0.38,)</version>-->
<scope>provided</scope>
</dependency>
Although i have different directory structure. But as you mentioned that is not the cause of issue.
I have uploaded the project to github. Feel free to pull it run it locally.
Github
Assuming the following location for your web content (which should be outside the classpath AFAIK) source/production/webapp. Spring Boot will ignore this due to a hardcoded path in DocumentRoot for detection of directories when running from the command-line or IDE (it will work when building a war and running that).
As a workaround you can add a TomcatContextCustomizer as a bean to detect the path and set it as the correct base.
package com.my.project;
#SpringBootApplication
public class Application extends SpringBootServletInitializer
{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
{
return application.sources(DemoApplication.class);
}
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public TomcatContextCustomizer docBaseCustomizer()
{
return new TomcatContextCustomizer()
{
public void customize(Context context)
{
File root = new File("source/production/webapp");
if (root.exists() && root.isDirectory())
{
context.setDocBase(root.getAbsolutePath());
}
}
}
}
}
Now add the following to your application.properties
spring.mvc.view.prefix=/jsp/
spring.mvc.view.suffix=.jsp
NOTE: The removal of the other annotations can only be done if your #SpringBootApplication annotated class is in the com.my.project package. It will then automatically detect the other classes (like entities and repositories).

JAX-RS does not work with Spring Boot 1.4.1

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.

Spring-boot jersey maven failed to run war file

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.

Hibernate exception hibernate.cfg.xml not found

I'm trying to start project with Hibernate and Maven.
I got such exception:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Exception in thread "main" org.hibernate.HibernateException: /hibernate.cfg.xml not found
at org.hibernate.util.ConfigHelper.getResourceAsStream(ConfigHelper.java:170)
at org.hibernate.cfg.Configuration.getConfigurationInputStream(Configuration.java:2176)
at org.hibernate.cfg.Configuration.configure(Configuration.java:2157)
at org.hibernate.cfg.Configuration.configure(Configuration.java:2137)
at FirstHibernate.com.myhib.CRUDS.CrudsOps.main(CrudsOps.java:15)
Here is the screenshot of my project structure, (hibernate.cfg.xml is in src/):
http://imageshack.us/photo/my-images/692/screenshotxba.jpg/
CrudsOps.java
package FirstHibernate.com.myhib.CRUDS;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class CrudsOps {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
SessionFactory sf = new Configuration().configure().buildSessionFactory();
System.out.println("Cfg and hbm files loaded succesfully");
Session session = sf.openSession();
session.beginTransaction();
System.out.println("Transaction began");
}
}
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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>FirstHibernate</groupId>
<artifactId>com.myhib</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>com.myhib Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.6.8.Final</version>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.1-901.jdbc4</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
</dependency>
</dependencies>
<build>
<finalName>com.myhib</finalName>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
</project>
What could be a source of that exception?
As #JBNizet said, your hibernate.cfg.xml should be in src/main/resources. In src, it won't be added to your classpath for runtime.
If you are running your project within Eclipse, don't forget in the project preferences in the build path configuration to check that the src/main/resources is not excluded from your class path and is indeed a source folder.
The file should be in the runtime classpath. Maven copies to the target/classes folder the resources that are under src/main/resources. So your config file should be there.
That said, you don't show the code which loads the file, so there might be other problems.
You can load hibernate.cfg.xml from a different directory (not necessarily the classpath) using the configure(File configFile) method that takes the hibernateConfig File argument.
(note, am using hibernate 4.3.7)
Like this:
String hibernatePropsFilePath = "/etc/configs/hibernate.cfg.xml";
File hibernatePropsFile = new File(hibernatePropsFilePath);
Configuration configuration = new Configuration();
configuration.configure(hibernatePropsFile);
StandardServiceRegistryBuilder serviceRegistryBuilder = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
ServiceRegistry serviceRegistry = serviceRegistryBuilder.build();
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
For the People who are facing this issue while deploying on linux machine
You need to copy hibernate.cfg.xml to classes directory of your war project.
In Eclipse/Maven Project: (During Development)
You need to copy hibernate.cfg.xml to src directory of your Eclipse/Maven project.
If you are working in Intellij Idea then make a folder named "resources" under src\main\java. Open Module setting of your project, select "Modules" from left and in the "sources" tab select the newly created "resources" folder and mark it as "Resources".
then this should work
Configuration con = new Configuration().configure("hibernate.cfg.xml");
If you are using Netbeans, put your hibernate.cfg.xml file into the "Build > classes >" directory and it will work.

Categories

Resources