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.