How can I organize resources in Java?
For example, when I will use pictures to be embedded in my application, in what folder can I store it? Because when I make my program in an executable .jar file all the resources like pictures, text file, it goes outside the folder where I stored it.
A common practice is to reserve a package to resources. You can place it anywhere, depending on the classes needing to access those resources (there's no rule, it's just a matter of organization logic). For example, your class project.gui.Main needs to load some images, you can then create a project.gui.data package and store your resources in it. To load these resources from Main, use the following piece of code :
Main.class.getResource("data/img.png");
This way, you can access the resource with a local path. Never use absolute paths if the application si expected to be packed in a jar.
Related
I've been trying to make jar application that can read a csv file in the same directory as it. This is, however, proving difficult as my means for accessing the file currently is:
InputStream is = getClass().getClassLoader().getResourceAsStream(filename);
Which works for my program running in the IDE and for my tests but doesn't work when I run the program from the compiled jar file. I have no idea how to get it to work for both. I seriously can't understand this path stuff, it seems like there are a million ways to do it and only one of them work for only one specific scenario.
I've been trying to make jar application that can read a csv file in the same directory as it.
Ah, there's your problem. That just isn't a thing.
There are only 2 types of files:
Application Resources
These are read only, and are as much part of your app as your class files are. It is not in any way relevant to think about 'editing' them - that's not the kind of thing they are. It is reasonable to assume that if this resource is somehow missing, the app is as corrupt / misinstalled as it would be if class files are missing.
For this, you use .getResource and .getResourceAsStream. And note that getClass().getClassLoader() is wrong, you want MyClass.class.getResource and then add a slash if you want to go from root (because getClass() potentially breaks when you subclass, and going via classloader is [A] just typing for no reason, and [B] breaks in bootload scenarios. MyOwnClassName.class.getResource never breaks, so, always use that).
This asks java to look in the same place class files are and nowhere else. Your class files are inside the jar files, and not next to them, therefore, it won't find a text file that is sitting next to jar files.
it does not make sense that it does work during development: That means you shoved a file inside the resources folder, which is equivalent to having a CSV file inside the jar file. You must have gone out of your way to tell your build system to do weird things. Don't do that.
If that CSV file is not intended to be user editable it should be inside the jar file and not next to it: That makes it an application resource. Examples of application resources:
You have a GUI, and you need to store the icon files and splash screen art and such someplace.
You ship static data with your app, such as a table of all US states along with the zipcodes they use (could be a text or csv file for example).
Templates of config files. Not config files themselves.
DLLs and the like that you need to unpack (because windows/linux/mac isn't going to look inside jars for them).
You're a webapp and you want to ship the HTML static files along with your webapp.
If this is what your CSV file is, the fix is to put it in the jar, not next to it, then load it with MyClass.class.getResource(name).
Config files and project files
For example:
For a rich text editor (like, say, LibreOffice Writer), the .odt files representing your writings.
Save games for a game.
A config file, which can be edited by the user, or is edited by your own app in a 'preferences' dialog. This stores for example whether to open the app full screen or not, or authentication info for a third party API you're using.
These should not be in the jar, should not be loaded with .getResource at all, and should not be in src/main/resources in the first place.
They also should not be next to your jar! That's an outdated and insecure model (the idea that editable files sit in the same place the app itself sits): A proper OS configuration means that an app cannot write to itself which is most easily accomplished by having it be incapable of writing to its directory. Some OSes (notably, windows) did this wrong for a while.
For example on windows, your app lives in C:\Program Files\MakorisAwesomeApp\makori.jar, and the data files for it live somewhere in C:\Users\UserThatInstalledIt\Documents\MakorisAwesomeApp.
oh linux, your app might be /usr/bin/makori and the data lives somewhere in the home dir. Config data might live in /etc/.
You don't "ship" your config files, you instead make installers that create them. You can do this part in-app by detecting that the relevant config file does not exist, load in a template (that is a resource, shipped inside your jar, loaded with getResource), and write it out, and tell the user to go look at it and edit it.
I really want a CSV file next to my jars!
Well, that's wrong, so, there are no libraries that make this easy. When you want to do silly things its good that APIs don't make that easy, right?
There are really hacky ways to do this. You can use .getResource to get a URL and then 'parse' this. This breaks the classloader abstraction concept (because in java, you can write your own classloaders and they can load from anywhere, not just files or entries in jars), but you can ask for 'yourself' (MyClass.class.getResource("MyClass.class")), pull the URL apart and figure out what's happening - does it start with file://? Then it is a file, so turn it into a j.i.File object, and go from there. Does it start with jar://? find the !, substring out the jar part, and now you know the jar. Make that a java.io.File, ask for the parent dir, and look there for the CSV.
You have to write all this. It's complicated code that is hard to test. You should not do this.
I've mostly only created application for personal use and the rare occasions where I have distributed my code have been in the form of uploading my source code on GitHub. I'm currently finishing up a project and plan on using launch4j to package it up as an exe. However, my application has a handful of png files that I coded in with the unique filepath of my computer. Obviously if my code were to run on any other computer in the world, those files would not be found.
I'm vaguely aware that java does not require the full filepath for a file (ie C:\Users...\file_name.ext) but I've never gotten a program to run correctly unless I write out the filepath like that, so that's been my default up until this point.
The resource system. Think about it: What's the difference between the many class files that comprise your application, and those png files, from an application distribution perspective?
The answer is, essentially, nothing. They are file-like concepts, they might prefer to be shipped in a packaged-up file (a jar file) instead of separately. They must be found at some point halfway through your app's existence (java does not pre-load all classes. It just loads your main class, and then loads whatever is needed the first time you mention any class).
You don't have to hardcode the absolute path to those class files in your app, so they clearly don't suffer from this 'coding filepaths' issue.
Thus, the answer is somewhat obvious: Simply stick those PNG files in the exact same place as your class files, and ask the VM to provide you with the data in them using whatever mechanism it is using itself, as it is doing that exact same job (find resource, obtain data in the resource) all the time, on your class files.
But, how?
You have 2 different methods, and these 2 methods take the same kind of argument, which comes in 2 forms: A grand total of 4 'modes' to choose from.
Pick a method
If the API you have that needs an image file so happens to have an overload that accepts a URL, this is very simple (ImageIcon is one such resource, that's probably what you're passing these PNG files to, so that's great):
URL loadIcon = ContextClass.class.getResource("/icons/load.png");
new ImageIcon(loadIcon);
Quite simple. Sometimes you want to read it yourself directly, and a URL object is rather unwieldy. Sometimes, you want to pass it to an API which does not have a URL overload, but it does have an InputStream overload. Then, you can fetch an InputStream. Given that this is a resource, like all resources, you must safely close it, thus, let's use try-with:
byte[] pngData;
try (var in = ContextClass.class.getResourceAsStream("/icons/load.png")) {
pngData = in.readAllBytes();
}
ContextClass.class is a somewhat exotic java syntax feature: It is an expression that resolves to the java.lang.Class instance of the so-named class. For example, Class<?> c = String.class; is legal java and gives you the class object that represents the class concept of all java.lang.String objects. The class object itself has these getResource methods. Thus, substitute some relevant class that you wrote as context here. Presumably, if you want to load an image in source file MyStatusWindow.java, you'd just use that class: MyStatusWindow.class.getResource.
These methods will look in the same location that the class itself was loaded from. If ContextClass is loaded from a jar, then the system will fetch PNGs from within that jar. If it's loaded from a build dir during development/debug, the png is loaded from there. If you've got some fancypants module system that is loading classes straight from the network, then the PNG will also be loaded from there.
resourceKey
A resourcekey is simply a path. It's not really a path, just - a string with slashes. You can't use .., for example, it's not really a path. You also, weirdly, can't use filenames that include more than a single dot in the name, for historic (read: silly) reasons.
You have 2 variants - classpackage relative and absolute.
.getResource("/icons/load.png") is absolute. .getResource("icons/load.png") is relative. The leading slash is the difference.
If you have:
package com.foo;
public class MyStatusWindow {
...
MyStatusWindow.class.getResource("icons/load.png");
}
And this is all in a jar file (i.e. /com/foo/MyStatusWindow.class is one of the entries listed if you execute jar tvf myapp.jar on the command line), then the above would look in that jar for /com/foo/icons/load.png - the relative form takes the context-class's package and sticks it in front. The absolute form would just look in /icons/load.png, still in the jar (so it's never C:\ - never the root of your disk - it's the root of the classpath entry).
Build systems
Maven, Gradle, and just about every other build system has a proscribed directory structure. The above example should go in src/main/java/com/foo/MyStatusWindow.java, relative to some 'root project dir'. Only java source files are supposed to go there. There's also a resources: src/main/resources/com/foo/icons/load.png, that's where your icon file would go. Then MyStatusWindow.getResource("icons/load.png") will just work, in your build system, and in your IDE, and when you ship it all as a jar file. If it doesn't, you've misconfigured your IDE or have a broken build configuration - and you should fix that. Out of the box, this just works.
How do i access a file that is in the same directory as my source file? I have seen it done in a tutorial and it was extremely simple, but any searches I conduct on the subject are too broad. any help? i.e.
doSomething("file.xml")
How do I access a file relative to the source file i am working with? i haven't seen how to do this, but since it would be an acceptable solution for the first question, here it is: i.e.
doSomething("src/com.package.file.xml")
I really just want a platform independent way to access files in my project. I know its probably a duplicate but please don't hate me.
Generally, you shouldn't
Files stored in the src directory (especially in Eclipse) won't be accessible at when the application is build and deployed.
Netbeans will package these files as part of your Jar when you build it, Eclipse requires you to these files stored in a separate "resources" directory within the project.
At this point, they become known as "embedded resources" and can no longer be accessed like a normal file, but instead, need to be loaded via the resources functionality available in your class.
For example.
To access the resource in com/package/file.xml, you would typically use some thing like...
getClass().getResource("/com/package/file.xml");
This will return a URL which represents the reference to the resource. If it's more confidnent, you can also gain an InputStream directly to the resource using something like...
getClass().getResourceAsStream("/com/package/file.xml");
Which will return an InputStream to the named resource...
This all of course, assumes that the resource can be found ;)
Right now I am trying to accomplish a project on Eclipse Juno and Tomcat 7 that requires to have a "virtual folder" to hold multimedia files (like images, other sub-pages,etc.). I already have some methods to give out the file path in a URI based syntax (lets say I want to access images in /Content/Image) and I want to map that URI to C:\Users\MyUser\Content\image (I am aware that I am binding the project to Windows systems but I will workaround later on in this issue).
Currently my project is called pj, and Eclipse created a context called pj inside the eclipse's tomcat instance (and thats makes a lot of sense). When i test my project with
> http://localhost:8080/pj
it works fine (and it's supposed to).
But there is a problem here: until now I haven't found a way to create a URI in tomcat to actually go to the Content/Image path to grab content to add to my pages (read somewhere that is unhealthy to keep content on WEB-INF folder, so i'm trying to actually get it done the right way). Also read somewhere that to accomplish this objective, I have to do something like this in the contexts:
<context docbase="d:/images" path="/Content/Images"></context>
Also read there that in tomcat, to resolve URIs you have to use contexts to achieve that goal (giving a bridge between the meaning of he URI and it's location in the file system).
Still, as from tomcat 4 (if not mistaken) it is not supposed to fiddle around server.xml, so in ANOTHER attempt to make this right, i try to actually add a context in META-INF inside context.xml with the code shown before. But there is here ANOTHER problem! It seems that adding the path tag makes tomcat go nuts, as said here: http://tomcat.apache.org/tomcat-7.0-doc/config/context.html .
So I am really in a bind here.... What I want to ask is:
What is the best way to actually add an external folder in a web project to fetch multimedia content and
How it is supposed to make it work inside Eclipse?
PS: I am asking this because in one of my methods inside my project I am using the getLoader method to return the InputStream (java.io InputStream NOT Corba) and it return nulls (which means it doesnt find it).
EDIT: Tried to actually fiddle around server.xml by inserting the conext by hand but didn't work, inserting the relative URI doesn't work on the server (local:8080/Content/Image with valid files inside) or going inside my main project and do getstream doesnt work too
After some fiddling around, tweaking, etc. I came up with a workaround for this situation. Like I stated, it IS possible to actually have an outside folder hold all the multimedia and/or pages as you wish. One of the references to that solution is here: http://harkiran-howtos.blogspot.pt/2009/08/map-external-directory-into-your.html .
Still, for some reason, this is not quite possible to make it work inside Eclipse (or I have failed something and wasn´t unable to make it work). But there is an alternate solution for this. It is also feasible to actually have a folder for that purpose INSIDE the web app but OUTSIDE the WEB-INF and META-INF folder. In other words, a folder that is located in the ROOT of the web app.To access those files in that folder you can use something called ServletContext. That context has actually inside all possible references to the folder structure of your web app. To access those files with the context give, you have to use getResourceAsStream from the Servlet context (or use getRealPath if it is necesary and/or you can guarantee that the web app is exploded inside Tomcat). So in other words, to access folders inside the web app but outside the WEB-INF and META-INF you have to use ServletContext and their given methods to get files/streams.
PS: Ty wds for pointing out ServletContext
I made the harkiran's solution work but it's not very good solution.
People discourage use of getRealPath. Mapping external folder is good thing to do for many reasons.
But to do it in Eclipse, you need to go to deployment folder.
In my case it's hidden folder inside Eclipse workspace.
workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/
Inside that folder you shoud make directory structure and file from harkiran's solution. I works until you delete and recreate server in Eclipse.
After that you need to make it again.
I wanted to know where should I keep my resources like I am displaying some images, playing some media files while making executable jar? Should I include it in executable jar or I should keep outside the executable jar?
If I keep the resources outside the jar what URL location URL should I pass in my program to access the images?
Actually the problem is I want to make distributable copy of my jar file.
If I give location of my local system for accessing the images and media files it will work in my system but what when I distribute it in other systems?
It depends on your application.
You can pack resources into the jar. Typically it is good for resources that are never changed: company logo, icons etc. You can read them using getClass().getResourceAsStream().
Other solution is to download the files from server (e.g. over HTTP). This solution is good for media that you do not want to pack together with application. For example video clip you want to play to user. Or, probably localized icons from the previous example or localized messages for multi-lingual applications.
The resources that you are downloading can be cached. You can use User's temporary directory (System.getProperty("java.io.tmpdir")) or sometimes using preferences API.
This isn't a radical answer but you could, in essence, create a resources project for images, properties and the like (media even).
You can then add your resources project (henceforth referred to as "MyProjectWsResources") as a child to your code project (henceforth referred to as "MyProjectWsClient"). Since MyProjectWsResources is in MyProjectWsClient's build path, it makes referencing your resources easier. If you're unclear of what putting something in your project's build path entails, it's saying anything in MyProjectWsResources's src folder is in MyProjectWsClient when it goes to referencing for information (as long as you're not using absolute paths :))
Why go for the approach of multiple projects? IMO, it separates code from resources so you download resources and code separately and your clients need not download the resources projects repeatedly when there are updates to your code only (I feel updates to code are far more frequent as compared to updates to resources). Lesser server bandwidth (important if you're using Java WS or any other packaging/system which I now realize you probably aren't).. still, hope this helps :)
I want to make distributable copy of my jar file.
Fist I will address the only sentence in your question that was not a question.
A great way to distribute a Swing desktop application to multiple users from the click of a link on the net, is Java Web Start.
Deployment with JWS would mean the resources would need to be in a Jar. For best results with the 'auto updating' nature of JWS, the Jar(s) for media would be:
referenced from a separate, sand-boxed, extension so they can be shared with other applications, and loaded/updated separately and lazily (as needed).
Compression:
uncompressed, for video, sound and image
compressed for textual information (HTML, RTF, CSV..)
Put in a path in the Jar that is known to the application. (e.g. /resources/video/vidNNN.mp4)
Resources in Jars are an embedded-resource and must be accessed by URL (or InputStream as mentioned by Alex, but URL is more robust). Quoting the info. page.
URL urlToResource = this.getClass().getResource("/path/to/the.resource");
During development, it is generally best to arrange a build that assembles the resources in the same way the end user will get them - to build the app. each run.
In other cases you might want to leave the resources at a public location on the server and access them as needed, but this effectively makes the server necessary for running the media related parts of the app. It seems your resources are both static (user does not change them) and an 'application resource'.