Java : getClass().getResource().toURI() Vs getClass().getResourceAsStream() - java

I have a java multiple modules sbt project and some of them contains a resources folder.
module-1
resources
template.xls
module-2
resources
other.xls
After packaging I got :
lib/module-1.jar
lib/module-2.jar
And I run my program like any other java application : java -cp "lib/*" MainClass.
My problem is accessing the template.xls from module-2.jar.
At first, I've tried the lines below to get my template :
URI template = getClass().getResource("/template.xls").toURI();
Files.newInputStream(Paths.get(template), StandardOpenOption.READ);
In development mode it works. But not on the server (after deployment), it cannot find the resource.
java.nio.file.FileSystemNotFoundException: null
[jar:file:/.../lib/module-1.jar!/template.xls]
After some research I modified my accessing code like the following to get it works in both modes (development and deployed) :
InputStream templateIS = getClass().getResourceAsStream("/template.xls");
I cannot understand why !
What is the difference between the two methods ?

Files.newInputStream, as the name suggests, can open files. It cannot open anything else. It's just for files.
The concept of an InputStream is much more abstract. When you open a file for reading, you get an inputstream, yup. But you also get inputstreams for many other things: Reading from a network connection; reading the unpacked contents of zip files. Reading the output of a decryption operation. The name says it all really: It's input, and it's a stream of data. Which applies to so much more than 'file on a filesystem'.
Paths.get(template) produces a path object, which represents a file on the file system. If template is derived from a URI, this does not work unless the URI that you have so happens to be a URI to a file object; most URIs are not to file objects.
Putting it all together, in your first sample, you find a resource on the classpath (which can be files, but don't have to be. For example, they could be entries in a jar file), you then ask its URI, feed it to the Paths API to turn it into a Path object, and then ask the Files API to turn this into an InputStream, which only works if the URI represents a file.
In the second snippet, you just ask the classloader system to get you an inputstream. It knows how to do that (after all, java has to load those class files!). If the resource you're asking for so happens to be represented by a file, it's going to do, internally, more or less the same thing as your first snippet: Use the Files API to open the file for reading. But if it's anything else, it knows how to do that too – it also knows how to get resources across a network, from inside jar files, generated on-the-fly – the concept of class loading (which is what class.getResource lets you access) is abstracted away.
NB: You're using it wrong. The proper way is ClassYouAreWritingIn.class.getResource and ClassYouAreWritingIn.class.getResourceAsStream; getClass().getResource is not correct; that breaks when subclassing, whereas the correct form doesn't.

From docs,
The method getResource() returns a URL for the resource. The URL (and
its representation) is specific to the implementation and the JVM
(that is, the URL obtained in one runtime instance may not work in
another). Its protocol is usually specific to the ClassLoader loading
the resource. If the resource does not exist or is not visible due to
security considerations, the methods return null.
If the client code wants to read the contents of the resource as an
InputStream, it can apply the openStream() method on the URL. This is
common enough to justify adding getResourceAsStream() to Class and
ClassLoader. getResourceAsStream() the same as calling
getResource().openStream(), except that getResourceAsStream() catches
IO exceptions returns a null InputStream.
So, getResourceAsStream() the same as calling getResource().openStream(), except that getResourceAsStream() catches IO exceptions returns a null InputStream.

Your first scenario doesn't work because the template.xls is packaged within a jar file. It's not a file on the filesystem (whereas it likely is in your development env prior to packaging as a file). As such the Files API can't find it.
Class.getResourceAsStream() makes use of the classloading mechanism, and the class (obviously) is loaded from the .jar file.

Related

Get RandomAccessFile from JAR archive

Summary:
I have a program I want to ship as a single jar file.
It depends on three big resource files (700MB each) in a binary format. The file content can easily be accessed via indexing, my parser therefore reads these files as RandomAccessFile-objects.
So my goal is to access resource files from a jar via File objects.
My problem:
When accessing the resource files from my file system, there is no issue, but I aim to pack them into the jar file of the program, so the user does not need to handle these files themselves.
The only way I found so far to access a file packed in a jar is via InputStream (generated by class.getResourceAsStream()), which is totally useless for my application as it would be much too slow reading these files from start to end instead of using RandomAccessFile.
Copying the file content into a file, reading it and deleting it in runtime is no option eigher for the same reason.
Can someone confirm that there is no way to achieve my goal or provide a solution (or a hint so I can work it out myself)?
What I found so far:
I found this answer and if I understand the answer it says that there is no way to solve my problem:
Resources in a .jar file are not files in the sense that the OS can access them directly via normal file access APIs.
And since java.io.File represents exactly that kind of file (i.e. a thing that looks like a file to the OS), it can't be used to refer to anything in a .jar file.
A possible workaround is to extract the resource to a temporary file and refer to that with a File.
I think I can follow the reasoning behind it, but it is over eight years old now and while I am not very educated when it comes to file systems and archives, I know that the Java language has evolved quite much since then, so maybe there is hope? :)
Probably useless background information:
The files are genomes in the 2bit format and I use the TwoBitParser from biojava via the wrapper class TwoBitFacade?. The Javadocs can be found here and here.
Resources are not files, and they live in a JAR file, which is not a random access medium.

How to properly load .properties on distributable jar file? [duplicate]

I need to store data into files inside .jar file and read it again.
I know that I can use Class.getResourceAsStream() method but it returns an InputStream that I can read from. But I look for a way to write.
I need to store data into files inside .jar file and read it again
No you don't.
Instead store the 'default' file inside the Jar. If it is changed, store the altered file in another place. One common place is a sub-directory of user.home. When checking for the file, first check the existence of an altered file on the file system, and if it does not exist, load the default file.
Note that it is generally better to describe the goal, rather than the strategy. 'Store changed file in Jar' is a strategy, whereas 'Save preferences between runs' might be the goal.
Related: What is the XY problem?
This is not really supported. In principle, you could operate on the jar file, but there's no guarantee the new contents would be correctly loaded. Let your build tools manage the jar file -- and choose something else for persistent storage managed by your program itself. Like a file.
It is unlikely that you can change a loaded jar safely. Changing it while it is being used is not a good idea.
If you want to do this, use a plain file system directory and add/remove files from it. Even this may not work as you expect.
You can manipulate any jar file using the package java.util.jar (or indeed just java.util.zip). As files inside a jar will be compressed, this isn't the most time efficient way for you to store data.
You should probably use a directory somewhere else (e.g. System.getProperty("user.home") + "/.myProgram") or see java.util.prefs.
Class.getResource() returns a URL. Theoretically, you can use this URL to create your InputStream and OutputStream. But in most cases, the generated JAR is a read-only file (or archive). So your application might trip when trying to use it.

Reading a local folder from Jar file [duplicate]

I need to store data into files inside .jar file and read it again.
I know that I can use Class.getResourceAsStream() method but it returns an InputStream that I can read from. But I look for a way to write.
I need to store data into files inside .jar file and read it again
No you don't.
Instead store the 'default' file inside the Jar. If it is changed, store the altered file in another place. One common place is a sub-directory of user.home. When checking for the file, first check the existence of an altered file on the file system, and if it does not exist, load the default file.
Note that it is generally better to describe the goal, rather than the strategy. 'Store changed file in Jar' is a strategy, whereas 'Save preferences between runs' might be the goal.
Related: What is the XY problem?
This is not really supported. In principle, you could operate on the jar file, but there's no guarantee the new contents would be correctly loaded. Let your build tools manage the jar file -- and choose something else for persistent storage managed by your program itself. Like a file.
It is unlikely that you can change a loaded jar safely. Changing it while it is being used is not a good idea.
If you want to do this, use a plain file system directory and add/remove files from it. Even this may not work as you expect.
You can manipulate any jar file using the package java.util.jar (or indeed just java.util.zip). As files inside a jar will be compressed, this isn't the most time efficient way for you to store data.
You should probably use a directory somewhere else (e.g. System.getProperty("user.home") + "/.myProgram") or see java.util.prefs.
Class.getResource() returns a URL. Theoretically, you can use this URL to create your InputStream and OutputStream. But in most cases, the generated JAR is a read-only file (or archive). So your application might trip when trying to use it.

Accessing a file in a different package

I feel like this is a very stupid question, but i've looked for 20 minutes now and i can't for the life of me figure it out, since a lot of online solutions to it seem to not be supported anymore.
How do i get a File object of a file that is in a different package?
I need it to be a File object because that's what a need to pass on to another method.
I want to test a method that gets an argument of the type File. The file i want to use to test it is in a different package: puu.sh/8pJAg/b177243091.png (testdata). I can't seem to access it, i keep getting a filenotfoundexception when i do new File("testdata/A000100")
Packages don't contain files, they contain classes and resources. This is because a package doesn't necessarily exist on the filesystem as a directory (it could be part of a .jar file, or it could be downloaded from a network location via any of several protocols, or it could be generated on the fly by a custom classloader -- Java doesn't actually care). Your options seem to be:
Change the method to accept a stream, and use Class.getResourceAsStream(),
Copy the stream to a temporary file, call your method, then delete the temporary file once it is finished, or
Stop storing the data in a package, and make a directory for it somewhere where you can guarantee it will be present.

Referencing a file in Eclipse without hard-coding the path?

I'm creating a dynamic web project in Eclipse where I frequently have write and read to and from an XML file. The file is in my project workspace in a folder called xml. I was wondering if Java provided some way to access the file without hard coding the file path. I've been looking around for a while for a solution but I haven't really founding anything that's really clear. Thanks!
You could just drop it in the classpath as suggested by others, but you won't be able to write to it.
Rather supply the absolute path as a VM argument or environment variable so that you don't need to hardcode it.
E.g.
-Dconfig.location=/path/to/config/file
with
File xmlFile = new File(System.getProperty("config.location"), "some.xml");
// ...
As a completely different alternative, you could consider a database.
You can get the proper path using the following from your Servlet:
String filename = getServletContext().getRealPath("/xml/config.xml");
NOTE:
getRealPath may return null if the file is inside a WAR file. In that case, if your file is in WEB_INF/classes, then you could use ServletContext.getResourceAsStream("/config.xml").
See this link:
I don't think the Servlet API gives you anything that would result in a reliable, writable, path to put work files in all containers. If your container runs the WebApp right out of the WAR, getRealPath() couldn't possibly point to something you can actually write to. I think that your only option here that is supported regardless of container is to hard code some path in the web.xml. Do it as a Context Parameter and you may be able to change it at deployment time. At the end of the day, you must declare a fully qualified path in either code or configuration to get the effect you seek.
Alternatively, do you really need to know the name of the file? In some Servlet apps I've managed to get the effect of dynamically writable storage through plain-jane java.io.file.createTempFile: http://docs.oracle.com/javase/1.4.2/docs/api/java/io/File.html#createTempFile(java.lang.String, java.lang.String)

Categories