Accessing files under .jar in a swing application? - java

Referring to my previous question, I want to ask that how can I read a file in different
folder into the similar .jar.
The folder hierarchy and details are provided with that question.
My main problem is with a line of code:
JTextPane textPane = ... //general initialization
textPane.setPage("path/file.html");
The problem is I have to keep that file.html in my .jar.
Moreover I would like to know suggestions on this topic.

For loading resources in a JAR, you can use Class.getResource(). This works equally well when loading from an ordinary folder structure not in a JAR:
textPane.setPage(ThisClass.class.getResource("relative/path/to/file.html"));
The path here is from whatever folder ThisClass.class is in. It is generally better to explicitly name the class rather than using this.getClass(), which may end up looking in a different (and unintended) location if this method is called for a subclass.

Related

How to correctly setup resources in a exportable library

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")

Getting location of file as string in resource folder

I have completed a program in eclipse and now I would like to export it as a single runnable jar file. The program contains a resource folder with images and text files in it. This is located beneath the source folder.
The res file is not added to the build path however when I run the program in Eclipse it still works.
The thing that is confusing me is that the res file is being saved into the runnable jar file when I export it as I can open the Jar file with WinRar and I see the folder is there with all the objects in it. But when I run the problem it stops at the point that the resource folder is referenced. To add to my confusion when I manually copy and paste the res folder next to where the runnable jar file is saved and run the program it works exactly as it should do.
Now I know this is something to do with how I reference the files in my code. At the moment I have it like this
reader = new LineNumberReader(new FileReader("res/usernames.txt"));
This works exactly how I want and accesses the res folder without any exceptions - in Eclipse and when I move the resource folder next to the Jar file.
I would like it to work normally but without having a folder outside of the Jar file I would like it all encapsulated in one Jar file.
I did a lot of research and what seems to be a common fix - may I add I don't really know how it works but everyone seems to mention it - is to somewhere use:
myClass().getResource()
When I create a new FileReader it needs a String input however when I use myClass().getResource() it returns a resource and not a string. I also don't have a clue how it is meant to reference the resource folder. Should I move the resource folder into the source folder?
Does anyone know how I can reference the resource folder from within the runnable jar file?
Sorry for rambling question I know what I want for my final product but I'm getting confused by the build paths and referencing from within classes and I have searched online for a long time trying to figure it out.
Resources, when you deploy your software, are not deployed as files in a folder. They are packaged as part of the jar of your application. And you access them by retrieving them from inside the jar. The class loader knows how to retrieve stuff from the jar, and therefore you use your class's class loader to get the information.
This means you cannot use things like FileReader on them, or anything else that expects a file. The resources are not files anymore. They are bundles of bytes sitting inside the jar which only the class loader knows how to retrieve.
If the resources are things like images etc., that can be used by java classes that know how to access resource URLs (that is, get the data from the jar when they are given its location in the jar), you can use the ClassLoader.getResource(String) method to get the URL and pass it to the class that handles them.
If you have anything you want to do directly with the data in the resource, which you would usually do by reading it from a file, you can instead use the method ClassLoader.getResourceAsStream(String).
This method returns an InputStream, which you can use like any other InputStream - apply a Reader to it or something like that.
So you can change your code to something like:
InputStream is = myClass().getResourceAsStream("res/usernames.txt");
reader = new LineNumberReader( new InputStreamReader(is) );
Note that I used the getResourceAsStream() method from Class rather than ClassLoader. There is a little difference in the way the two versions look for the resource inside the jar. Please read the relevant documentation for Class and ClassLoader.

Why does an image path no longer work once I compile my java files into a jar file?

I have a relatively basic java program which uses a system tray icon. The path I was using while writing the code is as follows "../images/logo.png". However, when I compile it into a jar file, the image does not show up in the system tray. Instead, if I change the path to "./images/logo.png", then the image shows up in the system tray when it's in the jar file form, but not while I'm testing.
It's not a major issue. However, I am curious to know why this inconsistency occurs.
When you package your program into a .jar file, your build is most likely copying the image into the same directory as the .jar file. However, when debugging in your ide, your image file lies one directory below.
Another possibility is that you are simply setting your Working Directly differently in the two scenarios.
Incidentally, you might be interested in embedding the image in your jar file, see:
https://stackoverflow.com/a/1096491/24954
This answer depends on two things. If the image file is embedded or not.
Embedded Resource
Once you have Jar'ed your application and the images are emebbed inside the application, normal file access methods will no longer work.
Trying to do something like...
new ImageIcon("../images/logo.png");
or
new File("../images/logo.png");
Won't work. This is because the resource is no longer a file within the context of the file system (it's actually a Zip entry in the Jar).
Instead, you need to use Class#getResource which will return a URL to the embedded resource.
new ImageIcon(getClass().getResource("../images/logo.png"));
Will work better. In general though, it is recommended to use an absolute path to the resources new ImageIcon(getClass().getResource("/images/logo.png")); as it's generally more difficult to break (IMHO)
External Resource
The path to the image is relative to the execution point of the application.
In development, you may have had to move up a directory (out of the src folder presumably) to find the image resource. This will mean that you will need to store you Jar file in a folder that would require it step up one level before it could find the image resource.
If you can, it's generally better to embedded the resource within the Jar where possible. It makes it easier to deploy as you reduce the number of files you need to package and makes it (a little) harder for the user to mess with it ;)

Why is my BufferedImage receiving a null value from ImageIO.read()

BufferedImage = ImageIO.read(getClass().getResourceAsStream("/Images/player.gif"));
First of all, yes I did add the image folder to my classpath.
For this I receive the error java.lang.IllegalArgumentException: input == null!
I don't understand why the above code doesn't work. From everything I read, I don't see why it wouldn't. I've been told I should be using FileInputStream instead of GetResourceAsStream, but, as I just said, I don't see why. I've read documentation on the methods and various guides and this seems like it would work.
Edit: Okay, trying to clear some things up with regards to what I have in the classpath.
This is a project created in Eclipse. Everything is in the project folder DreamGame, including the "Images" folder. DreamGame is, of course, in the classpath. I know this works because I'm reading a text file in /Images with info on the gif earlier on in the code.
So I have: /DreamGame/Images/player.gif
Edit 2: The line that's currently in the original post is all that's being passed; no /DreamGame/Images/player.gif, just /Images/player.gif. This is from a method in the class ImagesLoader which is called when an object from PlayerSprite is created. The main class is DreamGame. I'm running the code right from Eclipse using the Run option with no special parameters
Trying to figure out how to find which class loader is loading the class. Sorry, compared to most people I'm pretty new at this.
Okay, this is what getClassLoader() gets me: sun.misc.Launcher$AppClassLoader#4ba778
getClass().getResource(getClass().getName() + ".class") returns /home/gixugif/Documents/projects/DreamGame/bin/ImagesLoader.class
The image file is being put in bin as well. To double check I deleted the file from bin, cleaned the project, and ran it. Still having the same problem, and the image file is back in bin
Basically, Class.getResourceAsStream doesn't do what you think it does.
It tries to get a resource relative to that class's classloader - so unless you have a classloader with your filesystem root directory as its root, that won't find the file you're after.
It sounds like you should quite possibly really have something like:
BufferedImage = ImageIO.read(getClass().getResourceAsStream("/Images/player.gif"))
(EDIT: The original code shown was different, and had a full file system path.)
and you make sure that the images are copied into an appropriate place for the classloader of the current class to pick up the Images directory. When you package it into a jar file, you'd want the Images directory in there too.
EDIT: This bit may be the problem:
First of all, yes I did add the image folder to my classpath.
The images folder shouldn't be in the classpath - the parent of the Images folder should be, so that then when the classloader looks for an Images directory, it will find it under its root.
If you use resourceAsStream "/" referes to the root of the classpath entry, not to the root of the file system. looking at the path you are using this might be the reason.
If you load something from some home path you probably should use a FileInputStream. getResourceAsStream is for stuff that you deploy with your app.

Does Java getResource method only work with particular extensions?

The following piece of code works fine
getClass().getResource("/index.xml");
However when I do a full refactor to
getClass().getResource("/index.html");
The above line throws NullPointerException. I know I have refactored correctly because I rename the file using IDE smart refactor i.e. the file index.html definitely exists in the same directory. As soon as I switch back to
getClass().getResource("/index.xml");
Everything is fine again. Is there any reason why only the .xml extension works?
As #a_horse_with_no_name mentions, using getResourceAsStream( ) should work fine with any file and any extension.
I'd be inclined to believe (based on the information presented) that your IDE hasn't properly refreshed its file hierarchy after the refactor. I'd suggest running a full clean and build of your project, see if that helps the situation.
So, most of the other answers, the class/class loader shouldn't be looking at file extension You could write a ClassLoader which did that, but it would be odd.
I'm going to take a stab at what your problem is. I am guessing using some IDE (you don't specify which) that is copying certain files from your source folder into the destination (either a jar or a directory of classes and resources). For Java code, you want the compiled .class object files there and not the .java sources. So the IDE will be configured, with some reasonable default [magic], to copy files with only certain extensions. HTML files were used for old package JavaDocs (package-info.html rather than package-info.java which can include package-wide annotations), so are arguably reasonable to exclude by default.
Therefore, you should investigate what the project is doing in this area, and change any configurations accordingly.
Using getResourceAsStream() should work with any file extension (at least it does for me)
Recognised Java Resources are either a class extending ResourceBundle or .property file.
You can write your own extenstions to enable Resource to be gathered from other extensions.
I'm unsure why .xml files are a viable extension. Are you using Java 7?
From the JavaDoc guide:
The name of a resource is independent of the Java implementation; in
particular, the path separator is always a slash (/). However, the
Java implementation controls the details of how the contents of the
resource are mapped into a file, database, or other object containing
the actual resource.
A resource is identified by a string consisting of a sequence of
substrings, delimited by slashes (/), followed by a resource name.
Each substring must be a valid Java identifier. The resource name is
of the form shortName or shortName.extension. Both shortName and
extension must be Java identifiers.
And read this doc which tell us : an absolute resource name is constructed from the given resource name using this algorithm.
getResource() will work regardless of the type of resource. All it does it return a URL. This example works fine for me.
public class Example {
public static void main(String... args) {
System.out.println(Example.class.getResource("jaxb.properties"));
System.out.println(Example.class.getResource("test.xml"));
System.out.println(Example.class.getResource("foo.html"));
System.out.println(Example.class.getResource("danger.exe"));
}
}

Categories