I'm writing a Java application that needs to access several resources in a .jar file that is run over JNLP.
While the application works fine in my development environment (Eclipse) it doesn't work when executed through JNLP, apparently because it can't find the resource file in the jar. I've checked the resource file and the resources are most definitely there.
I'm currently using code like:
someclass.getResourceAsStream("/resources/somefile.png");
What is the correct way to access a resource file in a .jar that will work with JNLP?
use : this.getClass().getClassLoader().getResourceAsStream(name)
example: myClass.getClass().getClassLoader().getResourceAsStream("resources/somefile.png")
two tips:
1 - use your own class that is in jar file. if used another class - for example Object - fails
2 - name i.e. resource must be without leading '/'
I got stuck for a while on a similar issue and the comment from #Devon_C_Miller saved me (once I saw it, after some time!), so I thought I'd recopy it here:
When you get a resource via a Class, the path is resolved relative to the class, unless you start it with a '/'. When you get it via a ClassLoader, it is always resolved as an absolute path and must not begin with a '/'.
In my case, I use the following syntax, for a file located in the JAR: /properties/config.properties:
//NO LEADING `/` EVEN IF IT IS AN ABSOLUTE PATH
private final static String CONFIG_FILE = "properties/config.properties";
InputStream resource = Configuration.class.getClassLoader().getResourceAsStream(CONFIG_FILE);
Related
In a library I made, I have the following line to retrieve my resources:
private static final File fragment = new File(DefaultShader.class.getClassLoader().getResource("file.txt").getFile());
But when I export this library to a JAR and attempt to use it in another application, when this same line is read internally I get an I/O error because it's trying to access the JAR.
java.io.FileNotFoundException: file:\C:\Users\me\test\libs\lib.jar!\file.txt
Nodejs would make this easier by providing methods that returns the runtime location path of a script, apparently there is not an equivalent in Java or just couldn't find it. How can I get around this issue?
Never call the getFile method of URL. It does not return a valid file name. It only returns the path portion of a URL.
A resource embedded in a .jar file is not a File and you cannot refer to it as a File.
Fortunately, you don’t need a File object. You can read it directly using getResourceAsStream:
DefaultShader.class.getResourceAsStream("/file.txt")
Obviously, you should not store an InputStream in a static final field. But you can easily make a method instead:
private static InputStream readFileData() {
return DefaultShader.class.getResourceAsStream("/file.txt");
}
Be aware that resources should be placed in packages, just like classes. Placing a resource in the root of a .jar is like writing a file to a drive’s root directory or the user’s home directory: if any other program chooses to use the same filename, the results for both programs will be problematic, to say the least.
Similarly, if your resource is in the root of your .jar, and another library also happens to store a resource with the same name in the root of its own .jar, there will be a conflict, and it may or may not be your resource which gets loaded by the getResource* methods, depending on the current classpath definition. (This concern doesn’t apply to Java 9+ modular programs, but it’s still a good idea to keep resources in packages.)
The practice of putting a resource in the same package as the class that uses it is considered a good practice by Java SE: the getResource and getResourceAsStream methods are designed to expect it in the same package as the class by default. If the string argument does not start with a slash, those methods assume it’s in the same package.
This looks for file.txt in the same package as the DefaultShader class:
DefaultShader.class.getResourceAsStream("file.txt")
Whereas this will look for file.txt in the root of every .jar in the classpath:
DefaultShader.class.getResourceAsStream("/file.txt")
I am trying to read a Properties file in a maven nature project using the Properties.load(); I am specifying a path as a string ex. "./someFolder/file.properties",
but when I try to use my project as dependency in other projects I am forced to copy those files to the other project , simply because the "." means current directory.
Is there a way to specify a path so it will always be valid despite where I am calling it from ? ,
I have tried using the MyClass.class.getClassLoader().getResourceAsStream() but I am having trouble using it , it worked sometimes and failed other times.
There are lots of misconceptions in your question.
"." means classPath
No. When used inside a filesystem path (i.e. a path passed to the constructor of a File, or FileReader, or FileInputStream), "." means the current directory.
When used in a resource path (i.e. passed to Class[Loader].getResource[AsStream]()), it's invalid.
The trick is to carefully read the documentation.
getResourceAsStream() expects a /-separated path.
When using ClassLoader.getResource[AsStream](), this path always starts from the root of the classpath. So you would pass a path looking exactly like a fully qualified class name, except the dots would be replaced by slashes. So, com/foo/bar.properties looks for a resource named bar.properties, in the package com.foo.
When using SomeClass.class.getResource[AsStream](), either the path starts with a /, and the path starts from the root of the classpath, or it doesn't, and it starts from the package of SomeClass. So, if SomeClass is in the package com.foo, using /com/foo/bar.properties is equivalent to using bar.properties.
It's hard to tell what you're doing wrong, since you're not providing any detail. But you really need to understand the difference between opening a file on the file system, and reading a resource loaded by the class loader. Sometimes, the resources just happen to be loaded by the class loader from the filesystem, because the classpath happens to contain directories, and not just jar files.
I noticed that my problem was that I had my properties files in the project path itself, and that the ClassLoader.getResource[AsStream](); looks is the target/classes folder, and that I didn't have the resources folder in my project.
I solved it my adding the resources folder to my build path and adding my files in the src/main/resources as the following src/main/resources/foo/bar.properties and loading it by SomeClass.class.getClassLoader().loadResourceAsStream("foo/bar.properties");.
I have a web application running under tomcat 7, and in one of the class, Im trying to read a file in one of the jar under WEB-INF/lib folder.
URL resourceURL = MyClass.class.getClassLoader().getResource("xml/xslt/master.xsl");
File xslfile = new File(resourceURL.getPath());
AssertUtil.assertTrue(xslfile.exists(),"xsl file not found");
Both MyClass and master.xsl resides in the same jar and there is no issue with packaging. But above snippet fails in the assertion statement as xslfile.exists returns false. The URL correctly resolves to the location of the file inside the jar as given below
file:/<MY_WEBAPP_LOCATION>/MyApp/WEB-INF/lib/MyComponent.jar!/xml/xslt/master.xsl
where MY_WEBAPP_LOCATION corresponds to the absolute path to my tomcat servers webapp directory.
But if I rewrite the code as below to read as inputstream, it works fine.
InputStream xslFile = MyClass.class.getClassLoader().getResourceAsStream("xml/xslt/master.xsl");
Can anyone explain what is preventing the creation of File from the jar resource, whereas the inputstream creation is working perfectly fine. Is there any additional permission settings needed from tomcat side, to read a file inside jar ?
EDIT: One more observation, if the file is placed under WEB-INF/classes, creation of File with above code works fine. Issue is only when it is placed in a jar under WEB-INF/lib
Be careful it seems that ClassLoader.getResource does not handle relative path.
See this.
GetResourceAsStream happens to take the path relative to the ClassLoader (and not the class !!). I think you're lucky enough that there are the same here.
If it is a Desktop application getResource() will work
But as this is a web application the resource needs to be extracted from Context , hence getResoruceAsStream()
It is not a permission problem, but the use of java.io.File API - in particular constructor http://docs.oracle.com/javase/7/docs/api/java/io/File.html#File%28java.lang.String%29
When you are constructing File object using
File xslfile = new File(resourceURL.getPath());
you are using java.io.File#File(String) method which expects an "abstract pathname". What is an acceptable/valid pathname is described by javadoc of the File class: http://docs.oracle.com/javase/7/docs/api/java/io/File.html
String value that your are getting from getPath() method:
file:/<MY_WEBAPP_LOCATION>/MyApp/WEB-INF/lib/MyComponent.jar!/xml/xslt/master.xsl
simply does not constitute a valid "abstract pathname" - it is a URL that is converted to a java.lang.String (and IMHO should be returned with URL scheme of "jar" and not "file"). Therefore a call to
isExist()
returns false as there is no file with such name on your disk.
On the other hand if the resource is outside of a jar (e.g. under WEB-INF/classes directory) resourceURL.getPath() will return a value that presents a valid abstract pathname as the resource in question is indeed a simple file.
When you use java.lang.ClassLoader#getResourceAsStream(java.lang.String) the method streams out the resource directly into a java.lang.InputStream and might not even use File class in its implementation.
This is the well known problem of loading resources from a jar file. This is not the first time I've tried to do this, but now it doesn't work the way I expect it to.
Normally I try to load the Resources with this.getClass.getResource("foo.png"), or getResourceAsStream()and it works. Now however it does not. The Resource is always null.
If I let System.out.println(this.getClass.getResource("")) print me the path (from eclipse) it shows /path/to/eclipseproject/package/structure/. Running this from a jar it just shows rsrc:package/structure
If I recall correctly this should print the path to the jar. Furthermore I thought this would print the package structure in both cases. Am I doing something wrong?
Here is the thing...
When Extracting the file from the Jar use:
this.getClass.getResource("/foo.png")
When running from a runnable Jar use, to reference an external file in the Jar folder path:
this.getClass.getResource("foo.png")
// When running this from Eclipse, it would refer to files in project root!
I have a code in the lower level determining where I'm running from to determine the correct path.
Doe this get the path you need?
this.getClass().getClassLoader().getResource("<your class name>.class").getPath();
See also this question for more on this issue.
Unless you prepend the path to the resources with '/', Class.getResource() will search for the resource in class package. E.g.: tld.domain.Foo.class.getResource("Bar.txt") will search for tld/domain/Bar.txt
Check the URLClassLoader for all the gory details, but it really depends on whether you are trying to access a ressource in the jar,
using a class loaded inside the same jar, in this case your file 'root' is the root of the jar
using a class loaded outside the jar (your eclipse case) where the root is your 'working directory'
To access resources inside a jar from outside, you should use something like
URL url = new URL( "jar", "", "file:" + jar.getCanonicalPath( ) + "!/" + localPathResource );
url.openStream(...)
This answer provides an explanation of how to load class resources from JAR files, even when the class is not in the JAR file and not in the Class-Path specified in the JAR file's manifest. There are also links to code.
I'm working with a project that is setup using the standard Maven directory structure so I have a folder called "resources" and within this I have made a folder called "fonts" and then put a file in it. I need to pass in the full String file path (of a file that is located, within my project structure, at resources/fonts/somefont.ttf) to an object I am using, from a 3rd party library, as below, I have searched on this for a while but have become a bit confused as to the proper way to do this. I have tried as below but it isn't able to find it. I looked at using ResourceBundle but that seemed to involve making an actual File object when I just need the path to pass into a method like the one below (don't have the actual method call in front of me so just giving an example from my memory):
FontFactory.somemethod("resources/fonts/somefont.ttf");
I had thought there was a way, with a project with standard Maven directory structure to get a file from the resource folder without having to use the full relative path from the class / package. Any advice on this is greatly appreciated.
I don't want to use a hard-coded path since different developers who work on the project have different setups and I want to include this as part of the project so that they get it directly when they checkout the project source.
This is for a web application (Struts 1.3 app) and when I look into the exploded WAR file (which I am running the project off of through Tomcat), the file is at:
<Exploded war dir>/resources/fonts/somefont.ttf
Code:
import java.io.File;
import org.springframework.core.io.*;
public String getFontFilePath(String classpathRelativePath) {
Resource rsrc = new ClassPathResource(classpathRelativePath);
return rsrc.getFile().getAbsolutePath();
}
In your case, classpathRelativePath would be something like "/resources/fonts/somefont.ttf".
You can use the below mentioned to get the path of the file:
String fileName = "/filename.extension"; //use forward slash to recognize your file
String path = this.getClass().getResource(fileName).toString();
use/pass the path to your methods.
If your resources directory is in the root of your war, that means resources/fonts/somefont.ttf would be a "virtual path" where that file is available. You can get the "real path"--the absolute file system path--from the ServletContext. Note (in the docs) that this only works if the WAR is exploded. If your container runs the app from the war file without expanding it, this method won't work.
You can look up the answer to the question on similar lines which I had
Loading XML Files during Maven Test run
The answer given by BobG should work. Though you need to keep in mind that path for the resource file is relative to path of the current class. Both resources and java source files are in classpath