others-How to solve class path resource cannot be opened because it does not exist exception when trying to read file from classpath with java

1. Purpose

In this post, I would demo how to solve class path resource cannot be opened because it does not exist problem when using java to load file from classpath:

at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:180)
at com.abc.tweb.service.impls.TestDefaultSourceParser.test1(TestDefaultSourceParser.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

The directory structure is as follows:

➜  test git:(master) ✗ tree -N
.
├── java
│   └── com
│       └── abc
│           └── tweb
│               └── service
│                   └── impls
│                       └── TestDefaultSourceParser.java
└── testfiles
		└── test1.txt

8 directories, 2 files

The code we are using to load the test1.txt are as follows:

package com.abc.tweb.service.impls;


import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.InputStream;

import static org.junit.Assert.assertTrue;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class TestDefaultSourceParser {
    @Autowired
    private DefaultSourceParser sourceParser;

    @Test
    public void test1() throws Exception{
        try (InputStream stream = new ClassPathResource("/testfiles/test1").getInputStream()) {
            String source = IOUtils.toString(stream);
            log.debug("source:"+source);
            assertTrue(source!=null&&source.length()>0);
        }
    }
}

2. Environment

  • Spring boot 2.x
  • Java 8

3. The solution

We should put the file at the right location :

➜  tweb git:(master) ✗ tree -N .
.
├── java
│   └── com
│       └── abc
│           └── tweb
│               └── service
│                   └── impls
│                       └── TestDefaultSourceParser.java
└── resources
    └── testfiles
        └── test1.txt

8 directories, 2 files

As you can see, the file testfiles/test1.txt should be put into the location: ./resources, either the src/main/resources or the src/test/resources directory.

Then run the below code:

package com.abc.tweb.service.impls;


import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.InputStream;

import static org.junit.Assert.assertTrue;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class TestDefaultSourceParser {
    @Autowired
    private DefaultSourceParser sourceParser;

    @Test
    public void test1() throws Exception{
        try (InputStream stream = new ClassPathResource("/testfiles/test1.txt").getInputStream()) {
            String source = IOUtils.toString(stream);
            log.debug("source:"+source);
            assertTrue(source!=null&&source.length()>0);
        }
    }
}


We get this result:

source: xxxxx
Test success.

4. Why did this work?

As the above code shown, we are using ClassPathResource to load the resouce file from classpath, let’s view the details of the class:

public class ClassPathResource extends AbstractFileResolvingResource

Resource implementation for class path resources. Uses either a given ClassLoader or a given Class for loading resources.

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.

So the leading slash is not required ,so the following line would work too:

InputStream stream = new ClassPathResource("testfiles/test1.txt").getInputStream()

So, what is a Resource in Spring framework?

There will be a lot of configuration files in project development using spring as a container. These configuration files are all loaded through Spring’s Resource interface.

It’s an interface for resource descriptors abstracted from actual types of underlying resources (such as files or classpath resources).

Let’s see the Resource interface definition:

public interface Resource extends InputStreamSource {
 
  boolean exists();
  
  default boolean isReadable() {
		return true;
	}
  
  default boolean isOpen() {
		return false;
	}
  
  default boolean isFile() {
		return false;
	}
  
  URL getURL() throws IOException;
  
  URI getURI() throws IOException;
  
  File getFile() throws IOException;
  
  default ReadableByteChannel readableChannel() throws IOException {
		return Channels.newChannel(getInputStream());
	}
  
  long contentLength() throws IOException;
  
  long lastModified() throws IOException;
  
  Resource createRelative(String relativePath) throws IOException;
  
	String getFilename();
  
  String getDescription();
}

Here is the explanation of the above methods:

  • getInputStream(): Find and open the resource, and return an InputStream to read from the resource. It is expected that each call will return a new InputStream(), and the caller is responsible for closing each stream
  • exists(): returns a boolean value indicating whether a resource exists in physical form
  • isOpen: Returns a boolean value indicating whether this resource has an open stream handle. If true, InputStream cannot be read multiple times, it can only be read once and closed in time to avoid memory leaks. For all regular resource implementations, false is returned, except for InputStreamResource.
  • getDescription(): Returns the description of the resource, which is used to output the error log. This is usually the fully qualified file name or the actual URL of the resource.
  • isReadable(): Indicates whether the resource directory is read through getInputStream().
  • isFile(): Indicates whether this resource represents a file in a file system.
  • getURL(): returns a URL handle, if the resource cannot be parsed as a URL, an IOException will be thrown
  • getURI(): Returns the URI handle of a resource
  • getFile(): Returns a file, if the resource cannot be parsed as an absolute path, FileNotFoundException will be thrown
  • lastModified(): The timestamp of the last modification of the resource
  • createRelative(): Create related resources of this resource
  • getFilename(): What is the file name of the resource For example: the last part of the file name myfile.txt

You can see that the ClassPathResource is proficient to do the job.

5. Summary

In this post, I demonstrated how to solve the filenotfound exception when using java to load file in classpath, the key point is to put the file at right place.