1

Iam using spring boot org.springframework.boot.loader.JarLauncher to run my self executable spring boot app jar.

Now, I have my self executable jar packed like this (For brevity Iam just adding the files only that are needed to show the problem):

Main.jar   
      ---META-INF
      ---lib
           ---a.jar
              ---com
                  ---comp
                     ---Abc.class
              ---folder1
                 ---1.txt
                 ---2.txt
           ---b.jar

and so on.

In my Abc.class Iam trying to load the resource 1.txt which is working fine when I run it in eclipse; but the story starts when I run using command line as self executable jar. I was not able to read that resource and ends up with null pointer error.

After reading few threads, I learnt that my IDEs does some magic on Class Loaders and so it was working fine, which will not be the case when I run it as executable jar

This is how I was Loading the files and the all the possible options I have tried to no luck.

Option 1:

Abc.class.getResourceAsStream("\folder1\1.txt")

Option 2:

Abc.class.getClassLoader().getResourceAsStream("folder1\1.txt")

Option 3: Reading thread, I have tried considered the current thread class loader context as below too, But to no luck again

Thread.currentThread().getContextClassLoader().getResourceAsStream("folder1\1.txt")

Can any one advise. How should I write my code to be able to read my resource that is in the jar and by the class that is in the same jar ?

PS: Spring boot is not adding class-path entry to MANIFEST.MF and if I have to do something around that, how do I do that ? In-fact I tried -cp when running my jar setting it to current directory, but that have not worked either

Community
  • 1
  • 1
Shiv
  • 521
  • 2
  • 11
  • 27

2 Answers2

3

In Spring, the best way to access a Resource is via Resource APIs. For a Classpath resource what you should use a ClassPathResource and it would look something like this:

Resource resource = new ClassPathResource("/my/resource.json", this.getClass());

After obtaining a Resource you can either get a File reference via getFile() or get an InputStream straight off by calling getInputStream().

Spring provides quite a few different implementations of the Resource interface, take a look at the list of known implementations in the docs

Alexej Kubarev
  • 853
  • 8
  • 14
  • This worked like Charm. But Iam double confused now, as the class path resource is also doing as below, which is same as what I have been trying InputStream is; if (this.clazz != null) { is = this.clazz.getResourceAsStream(this.path); } else { is = this.classLoader.getResourceAsStream(this.path); } if (is == null) { throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist"); } return is; What made is different and caused it work ? any clues ? – Shiv Jan 14 '16 at 21:06
  • 1
    Even ClassPathResource doco says: Supports resolution as java.io.File if the class path resource resides in the file system, but not for resources in a JAR. Always supports resolution as URL. – Shiv Jan 14 '16 at 21:11
  • 1
    The ClassPathResource.getFile() method will work only if the resource points to a file system resource. If you look ac ClassPathResource implementation, in constructor it does some path cleanups as well as a few other things. Later, in the getInpufStream() method it would actually do quite a few checks to cover different cases as well. – Alexej Kubarev Jan 15 '16 at 22:07
  • Iam using the direct Constructor with resource and class and calling the getInputStream , I can see getFile being called at all. http://grepcode.com/file/repo1.maven.org/maven2/org.springframework/spring-core/3.2.14.RELEASE/org/springframework/core/io/ClassPathResource.java#ClassPathResource.getInputStream%28%29 – Shiv Jan 16 '16 at 22:27
  • Thank you. Unable to access the ClassPathResource as file and but able to access it as InputStream – DecKno Mar 04 '17 at 09:25
1

Use Spring's class ClassPathResource to load file from classpath.

For example you can read file into String like this from classpath:

String fileContent = FileUtils.readFileToString(
       new ClassPathResource("folder1" + File.separator + "1.txt").getFile());

It doesn't matter what kind of build tool or project structure you are using as long as that file is on classpath.

luboskrnac
  • 23,973
  • 10
  • 81
  • 92