6

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.

3 Answers 3

20

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Could you please look into this, I am having similar issue stackoverflow.com/questions/37713335/…
What is specialisationResource?
Specialisation is just an entity, SpecialisationResource just extends from Spring Resource.
The point here is, do not use resource.getFile(), use resource.getInputStream
2

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.

1 Comment

I wish there was a way to do this in Spring natively without relying on another outside library.
1

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.