referencing data files in jars - java

My Java program references a lot of data files. I put them in a top level directory called data/, along with src/ and bin/. In Eclipse, references to data/ and ../data/ both seem to work. When I run it from the command line, only ../data/ works.
When I put the bin/ and data/ directories in a jar and correctly set the entry point, it doesn't seem to know that I want it to access the data/ directory inside the jar. The only way I've been able to get it to work is by setting the references to ../data/ and placing the jar in the bin directory. This obviously doesn't do me any good, because it's not self-contained.
What do I need to do to the references to make them work?
Thanks

I'd recommend you access the files in a classpath-relative way, whether in Eclipse or in a compiled JAR.
There are a few ways you might go about it, but a basic JDK-only approach would be to use Class's getResourceAsStream() method, giving you access to an InputStream object that you can read in.

If your resources are in a jar file, consider using this method to read them:
class Class {
InputStream getResourceAsStream(String name)
}
This looks for the resource relative to a class (which may be in a jar), rather than relative to the working directory.

Thanks for pointing me down this path, guys. I ended up doing a really hacked up workaround because I'm not very good with IO yet. I used getClass() to construct a URL:
http://forums.sun.com/thread.jspa?threadID=5258488
Then made a new File object from this string (new File(file)):
String file = url.toString().replaceFirst("file:", "");
This allowed me to keep the same code that referenced the file objects.

Related

Absolute path of a file inside a project package

I've created a file inside a project package using this code:
File xmlFile = new File("src/com/company/project/xml/tags.xml");
I am able to read the file while running from eclipse. However, after creating .jar, I'm unable to read the file. So I want to put absolute path while reading the file from the project package. How it can be done? Help and suggestions are appreciated.
In most cases, IDE's will include no Java files in the resulting Jar. Most IDE's will also include the src directory in the classpath when you run/debug the program from within them.
As a general rule of thumb, never include src in any path, src will simply not exist once the program is built.
Instead you need to make use of Class#getResource or Class#getResourceAsStream, depending on your needs. You should remember, you should never treat an "embedded" resource as a File, as in most cases it won't be, it'll be a stream of bytes in a zip file.
Something like...
URL xmlFile = getClass().getResource("/com/company/project/xml/tags.xml");
will return a URL reference to the resource. Remember, if you need a InputStream, you'll have to Class#getResourceAsStream.
If you want the resource to be writable, then you will need to find a different location to store it, as embedded resources are read only
Try with getClass().getResource()
new File(getClass().getResource("src/com/company/project/xml/tags.xml").toURI());

Getting resources works from NetBeans Execution Context but not working when jar is executed from comand line

I have a code where i create Java Actions and try to associate Icons with them. One snapshot of code is
FileOpenCommand fileOpen = new FileOpenCommand(this);
fileOpen.putValue("ImageOnly", false);
fileOpen.putValue(Action.NAME, "Open");
fileOpen.putValue(Action.SMALL_ICON, new ImageIcon(getClass().getResource("../resources/File-Open-icon24x24.png")));
fileOpen.putValue(Action.SHORT_DESCRIPTION, "Opens the existing file.");
fileOpen.putValue("Group", "File");
fileOpen.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK));
this.commands.put("FileOpen", fileOpen);
The accent is on the line where I try to set the Action.SMALL_ICON property to the action. This works when executed in NetBeans environment either in debug or release mode. But when I've tried to execute jar file from the command line, it fails with exception.
Any idea? Anything to do with classpath? Resources folder is put as the package inside the main package.
Thanks in Advance!
I'm not entirely sure what exception is being thrown in your case, although assuming it is a NullPointerException, IOException, or IllegalArgumentException deriving from
fileOpen.putValue(Action.SMALL_ICON, new ImageIcon(getClass().getResource("../resources/File-Open-icon24x24.png")));
Your issue should be resolved simply by adding getClassLoader() between the getClass() and getResource(), like so:
fileOpen.putValue(Action.SMALL_ICON, new ImageIcon(getClass().getClassLoader().getResource("../resources/File-Open-icon24x24.png")));
Additionally, you must be exact in your filenames, specifically the extension. In this case, you are accessing File-Open-icon24x24.png, which will work perfectly fine regardless of whether the actual file is extended by png or PNG within Netbeans, but once exported the extension case matters.
Lastly, if neither of those changes resolve your problem, I would check your filepath, as there is most likely a logical error somewhere down the line.
When using embedded resources in Netbeans, you should have a resources folder containing additional folder or whatever data you need, which you seem to have, but this folder should be located inside the Netbeans project's src folder. getClass().getResource() returns the directory at the top of the package line, meaning if your class package is com.example.code, then the compiler will look for files/folder on the same level as com. Opening the Netbeans src folder you should see the initial com folder. Your resource folder should be placed directly next to that folder, as then it will be properly embedded in the jar file export.
In your code your path is ../resources/File-Open-icon24x24.png, which confuses me as to why you begin with ... I cannot see your folder structure so I cannot give a precise answer on this note, but you may be accessing the wrong location, although I feel like you are not as you said your project runs correctly within Netbeans. However, your resource files may not be correctly encoding into the jar file due to placement as mentioned. To test what your jar file actually contains, make a copy of it (for safety reasons) and change the file extension from jar to zip. You can then look through its contents in Windows Explorer, and see its directory structure. Another debugging trick for folder structures is to create a text file at the URL you are trying to access to see where it is placed.

How do I access the resources directory for a calling program in Java

I have a library which is used by a program. This library loads a special directory in the resources folder.
in the library I have the method
public class DataRegistry{
public static File getSpecialDirectory(){
String resourceName = Thread.currentThread().getContextClassLoader().getResource("data").getFile().replace("%20", " ");
File file = new File(resourceName);
return file;
}
}
in my program I have the main method
public static void main(String args[]){
System.out.println(Data.getSpecialDirectory());
}
When I execute the getSpecialDirectory() in a junit test within the data program, the resource is fetched and all is well.
When I execute the getSpecialDirectory() in the main method outside the data program (imported jar) I get the jars data directory and not the directory the program executing the thread is expecting.
I figured getting the parent class loader would have solved this issue... I believe I may have a fundamental issue in my understanding here.
For clarity:
(library)
Line 15 of this file: https://gist.github.com/AnthonyClink/11275442
(Usage)
Line 31 of this file:
https://gist.github.com/AnthonyClink/11275661
My poms may have something to do with it, so sharing them is probably important:\
(Library)
https://github.com/Clinkworks/Neptical/blob/master/pom.xml
(Usage)
https://github.com/Clinkworks/Neptical-Maven-Plugin/blob/master/pom.xml
Maven uses the target/classes and target/test-classes directories to compose the classpath used to run unit tests. The content of your src/main/resources and src/test/resources gets copied to these locations together with the compiled java sources (the .class files).
java.lang.ClassLoader.getResource returns a java.net.URL.
In the unit test situation, ClassLoader.getResource returns a file:// schemed URL, and your code works as expected.
When your code is executed when packaged in a jar file, ClassLoader.getResource no longer returns a file:// schemed URL. It can't because the resource is no longer a separate entity in a file system - it's buried inside the jar file. What you actually get is a jar:file:// schemed URL.
jar: URLs cannot be accessed through a [java.io.File] object.
If you only need to read the content of the resource you should use java.lang.ClassLoader.getResourceAsStream(...) and read the content from that.
Note that it is not possible to update the content of class loader resources.
Do you want the directory where the Java program was loaded from ? Can try
File f = new File(".").getCanconicalFile();
Or if you want all the places could try parsing the classpath from System.env or use the default class loader
If you want a directory relative to the jar from where your classes were loaded: Quoting from http://www.nakov.com/blog/2008/06/11/finding-the-directory-where-your-java-class-file-is-loaded-from/
URL classesRootDir = getClass().getProtectionDomain().getCodeSource().getLocation();
The above returns the base directory used when loading your .class
files. It does not contain the package. It contains the classpath
directory only. For example, in a console application the above
returns someting like this:
file:/C:/Documents%20and%20Settings/Administrator/workspace/TestPrj/bin/
In a Web application it returns someting like this:
file:/C:/Tomcat/webapps/MyApp/WEB-INF/classes/
FYI This might apply if the user id running program cannot read all folders: http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getProtectionDomain%28%29
or pass a .class object of the calling class as a parameter and get its classloader
There is no guarantee that you have a directory for your resources as far as I know. Java provide methods to get a resource as a InputStream, but nothing that can easily get the File for a resource, although it is possible to get the URL for a given resource.
If you need a special working directory in your application, I recommend having that passed as a system property, a program parameter, a web application parameter, or something else that lets you know where your application files are. You can even populate this directory with a ZIP file that you include as a resource in your application, as the ZIP libraries in Java do accept input streams as parameters.
While user #tgkprog is right and his answer correct, I can hardly imagine that you really want what you seem to have asked for and what he correctly answered in (3). Please edit your question and explain
what the output should look like if resourceName would be printed to the console,
if you really want the JAR file's location (in my test case that would be something like file:/C:/Users/Alexander/.m2/repository/com/clinkworks/neptical/0.0.1-SNAPSHOT/data, which does not make sense because you do not want to read from or even write to the local Maven cache),
or if you maybe rather want to read a resource directly from the JAR file or any other location such as the current working directory etc.
Otherwise I am afraid there is no good way to answer the question.

Why does getResourceAsStream() work in the IDE but not the JAR?

I just want to read a file into my program. The file is located one directory above the working directory at "../f.fsh". So the following code runs correctly when I run it in the IDE
String name="../f.fsh";
InputStream is = getClass().getResourceAsStream(name);
InputStreamReader isreader=new InputStreamReader(is);//CRASHES HERE WITH NULL POINTER EXCEPTION
BufferedReader br = new BufferedReader(isreader);
but when I create a JAR file that has f.fsh zipped inside of it and run it, it crashes when creating the InputStreamReader, because the InputStream is null.
I've read a bunch of answers to questions about input streams and JAR files, and what I got out of it is that I should be using relative paths, but I am already doing that. From what I understand getResourceAsStream() can find files relative to the root of the project, that is what I want. Why does it not work in the JAR? What is going wrong, how can I fix it?
Does it have to do with the classpath? I thought that was only for including files external to the jar being run.
I have also tried, but still fail, when putting a slash in:
InputStream is = getClass().getResourceAsStream("\\"+name);
I looked at: How to get a path to a resource in a Java JAR file andfound that contents of a JAR may not necesarily be accesible as a file. So I tried it with copying the file relative to the jar (one directory up from the jar), and that still fails. In any case I'd like to leave my files in the jar and be able to read them there. I don't know what's going wrong.
You can't use .. with Class.getResourceAsStream().
To load a resource f.fsh in the same package as the class, use SomeClass.class.getResourceAsStream("f.fsh")
To load a resource f.fsh in a sub-package foo.bar of the package of the class, use SomeClass.class.getResourceAsStream("foo/bar/f.fsh")
To load a resource f.fsh in any package com.company.foo.bar, use SomeClass.class.getResourceAsStream("/com/company/foo/bar/f.fsh")
This is described in the javadoc of the getResource() method, although it lacks examples.
If .. works in Class.getResourceAsStream() while running from Eclipse, it's a bug in Eclipse. Eclipse and other IDEs implement custom class loaders to fetch resources from the project at runtime. It looks like the class loader implementation in Eclipse isn't performing all the necessary validations on input to getResourceAsStream() method. In this case the bug is in your favor, but you will still need to rethink how you structure your resources for your code to work in all cases.
it's mandatory that the name of the file is CASE SENSITIVE
it's mandatory to refresh (F5) the project explorer if the file is moved or copied outside Exclipse

ClassLoader loading the wrong instance of a file

So I see there has already been a post very similar to this issue, however I am in a situation where I have no power to specify the location of this file within my jar and so am hoping someone is aware of a solution to get around this.
So I currently use the following snippet to obtain a file as an input stream, the file 'plugin.xml' is located at the root of the jar and I cannot change this location as another piece of software (dynatrace) creates this file and determines its location.
the standard snippet:
InputStream is = JmxPlugin.class.getResourceAsStream("/plugin.xml");
Now I am aware that the issue is that the ClassLoader is picking up the first file which matches the name 'Plugin.xml' along the classpath (one which isn't in my jar, yay).
Can anyone think of a way to ensure I pick up the correct file without having to move it? The relative path of my class in the jar is com/something/jmx/JmxPlugin.class.
(Id rather not have to unpack the jar in memory).
Many thanks for any contributions,
I'm not absolutely sure, but seems like Thread.currentThread().getContextClassLoader().getResourceAsStream("/plugin.xml") may solve your issue. If not, you'll have to create your own ClassLoader and resolve the issue there.
The simplies way is to move your jar in classpath to be the first containing Plugin.xml,
Another approach is to use getResource() to locate your jar file:
URL myJar=JmxPlugin.class.getResource("/"+JmxPlugin.class.getName().replace(".","/")+".class");
then use this URL to open jar file and extract Plugin.xml.

Categories

Resources