I'm create an application that is going to be run on Windows, Mac OX and Linux. I have a properties file storing user settings which need to be read and changed on the fly.
A JAR file is compressed and is not meant to be changed on the fly which means I should write to an external file.
I'm using :
new FileInputStream("database")
new FileOutputStream("database")
How do I create a URL which is going to be consistent throughout all three operating systems. The JAR is run as an application on the desktop and I would like the file to be stored somewhere discrete.
I've tried reading from a local file in the same package as this class :
this.getClass().getResourceAsStream("database")
This works however I can't seem to create an output stream to write to the same file but this would be breaking the rule of changing a JAR file on the fly.
There are plenty of good reasons for which you should not do this, off the top of my head:
GetResourceAsStream does not necessarily get the file from the JAR itself. You coincidentally got it from there because the Jar was the first or the only element in the class path.
Writing a file in your own Jar could break the JAR signature if you are going to sign it.
The database could need to be backed up; in this case you may want to back it up separate from the code (the code could be upgraded when restoring the database).
Hope this helps.
Related
I wrote a little Java app for analyzing .csv files. Now I want to keep reading from and writing to a .txt file, which acts similar to a mini-database. For this purpose I simply added the .txt in my project and used the Files.readString(Path) and Files.write(Path path, byte[] bytes) methods.
When I do this in IntelliJ I have no problems but as soon as I build/export the file with Maven and started with the created launcher the app didn't work because the project structure / file organization isn't the same anymore.
I also tried to just add the .txt file to the exported folder afterwards but even then I couldn't manage to implement a relative path to the file.
I'm still relatively new to programming and it's just a small app so I don't think mySQL would fit my needs. I've also read that I could store the data in a property file but I don't know if that would be the right way to archive what I want. Certainly it must be possible to somehow keep a .txt for reading and writing in your exported project. Does someone have an idea?
If you use a ยด*.txt` file for storing, that file cannot be inside the jar because the jar cannot change its contents while running.
You need to put the file somewhere else, either at some dedicated location (let the user choose/configure one), or next to the jar. To figure out your own execution path, you can use
How to get the path of a running JAR file?
Maven is one tricky tool. You need to go to the pom file and add the resource.
Unable to add resources to final jar for maven project in Intellij.
-I hope this helps
Trader
I want to edit a configuration file located in the current running jar.
Is there a way that allows modify directly this file or copy it from external?
You cannot modify the jar being run at runtime like Andreas suggested in the comments. I'll just put in a possible alternative to why you might need this in here, so this question doesn't show up in unanswered page.
Why?
When a jar is running (that is, it is being used by JVM), the file (archive >>file) is locked by the Operating System. When any file is locked (or marked as locked) it cannot be changed, in other words it is as good as a read-only file.
Why you might be in need of doing this.
Reason why usually people try to write into jar is that, they want a file which they want to use with absolute/relative path (with reference to jar file).
What option do we have in this case?
So, if this is your issue, you can make a directory in the jar's location and use it.
If your jar is at C:\Users\<Something>\Desktop\testDir\, then you create a directory 'conf' (C:\Users\<Something>\Desktop\testDir\conf\) in this place. You can write/create files in this directory and reference them from your code easily (with reference to the current working directory's path).
I'm learning java and am currently trying to develop a simple application. My question is can you store data about settings, etc in a text file internal to a .jar? If so how would you go about accessing this within java? Sorry if this is a really stupid idea.
InputStream is = this.getClass().getResourceAsStream("/data/file.txt");
The resources you are getting need to be on the classpath
Yes you can, and it's not a stupid question we all need to start somewhere.
There are two parts to your question:
Adding a data/text file to a .jar - (using ant to jar it:) add "fileset dir=..." to the jar target, where dir is set equal to the directory that has the data/text file. Refer to How can I include data text files in a jar using Ant?
Accessing that data/text file from within the java code - you need to use a ClassLoader and getResourceAsStream. Refer to Loading files in JAR in Tomcat using getResourceAsStream
Also, please take a look at https://github.com/gitjonathan/turbo-sansa, I have a working version up on it.
Can you store data inside a .jar?
Read-only data can be stored inside a JAR file. You can read such data using getResourceAsStream(...) if the JAR is on the classpath, or by using the standard JAR file API class if it is not on tle classpath.
But storing update-able data in a JAR file is problematic:
In a lot of circumstances it is impossible; e.g. because the JAR file is read-only or was downloaded on the fly.
In all other cases it would be very awkward, because the standard JAR file API class does not support update in place. (You would need to create a new ZIP file, copy across the old content apart from the file you are updating, add that file, and then rename the resulting file.)
in my Java project I am using an H2 in-memory database, for which I have to load the JDBC driver when I initialize my application. I want/need to load the H2 .jar file dynamically, so I do the following:
String classname = "org.h2.Driver";
URL u = new URL("jar:file:libs/h2.jar!/");
URLClassLoader ucl = new URLClassLoader(new URL[] { u });
Driver d = (Driver) Class.forName(classname, true, ucl).newInstance();
DriverManager.registerDriver(new DriverShim(d));
When I put the H2 .jar file into a "libs" folder outside my Java source code folder (that is, in Eclipse, this "libs" directory is on the same level as the "src" folder), then this approach works fine. However, unfortunately I have to put this H2 .jar file into a folder within the source code folder tree, but below the main class folder.
For example, my Java package structure looks like this in Eclipse:
<project>/src/my/app/MyApp.java // main class of my application
<project>/src/my/app/sub/package/h2.jar // how to access this?
<project>/libs/h2.jar // loading from here works
I know this is stupid, but unfortunately I have to work with this strange setup. But what I don't know: how can I edit my Java code (listed above) in order to work with this setup?
EDIT: This has to work outside Eclipse as well, so adding the JAR file to the Java Build Path in Eclipse is no option for me.
EDIT2: I already tried to load "jar:file:my/app/sub/package/h2.jar!/", but that did not work for me.
Thanks in advance for all helpful ideas!
Kind regards, Matthias
In some frameworks referring to files inside JARs can be done using the classpath: prefix. I doubt URLClassLoader supports it natively, but it's worth a try (e.g. classpath:/my/app/sub/package/h2.jar). But since that doesn't work with URLClassLoader, here are other ways:
One way to do it would be to write your own ClassLoader which reads the JAR file from classpath (using getResourceAsStream), uncompresses it (using ZipInputStream) to memory (e.g. a map of byte arrays) and loads the classes from there.
Another, slightly easier way, is to read the JAR file from classpath and write it into a temporary file. Then you can use the plain URLClassLoader to load classes from it. This has the disadvantage that the file must be written to a file and the file probably cannot be removed until the JVM exits (unless using Java 7 or higher).
I'm using the second approach (copying to a temp file) in one project, though I'm using it to launch an external process. I would be curious to hear why you have such a requirement. If it's just a matter of having the whole application in one JAR, there are numerous simpler methods for achieving that (Maven Assembly Plugin, Maven Shade Plugin, Jar Jar Links, One-JAR to name a few).
No it's not a homework, but an online build system that uses my classes under my/app/* and several other classes (not from me) to automatically build the whole solution. Anyway, I can't give you more details on the internals of this system, as I don't know them. As said, I simply have to live with it, and that is why I am asking here...
Sounds like you are working in a WTF environment (does it have a name?), so here are some ways to start hacking around it:
Find out more about your environment, especially absolute file paths of the following: directory where the source files are saved, directory where the generated .class files are saved, and the current working directory when the program is run.
If you can get any kind of output of what your program prints during runtime, you can put into your application some debug code where you use File.listFiles() to crawl the machine's directory trees. If you can get output only from what happens when compiling, it might be possible to execute your own code during compile by creating your own annotation processor (apt is part of javac since Java 6), though I'm not sure whether the annotation processor must be compiled first separately.
The working directory can be read from the user.dir system property and the location of class files can be probably gotten from the java.class.path system property (unless custom class loaders are used). There is no guarantee that a JAR file in the source directory would be copied to the classpath, so you might need to do some looking around.
Then when you know the file path of the JAR file, then you can get an URL to it using new File("path/to/h2.jar").toURI().toURL() which you can then pass to URLClassLoader.
If nothing else works, upload the source code of the libraries and compile them together with your project.
In the long run, try to replace the WTF build environment with one that uses a standard build tool (such as Maven) and a common CI server (such as Jenkins). It's normal for projects to have lots of library dependencies, so you shouldn't need to hack around a build environment to use them.
I have created a simply login screen for a test game and it plays a .mid music tune. The .wav sounds are played when you hover over a button. When I Build+Compile the program, the both sounds play inside netbeans. When i run the .jar file outside of netbeans, it does not work. Any suggestions...
P.S. The sounds are in a folder, inside the src folder, called resources. For .mid music, I use the sequence, and for .wav I use AudioInputStream and such.
It is most likely that you are attempting to access application resources as though they were a File. An application resource would usually be inside a Jar file, and must be accessed by URL. To form the URL, use something like:
URL urlToMid = this.getClass().getResource("the.mid");
If that is not the case, then the next most likely problem is that the resource is not being included in the Jar.
Without a code example, it's hard to help you out. Do you get an exception?
My guess is that your application cannot find the music files when running outside of NetBeans. Your home directory must be set to some value while running from NetBeans (probably pointing to your src/resource folder), but you specify a different home location (or none at all) when running outside NetBeans.
If the files are included in the generated .jar file, another possible problem is case-sensitivity. If the file is load from the regular filesystem (e.g. from within NetBeans), it depends on your operating system's filesystem if the filename is case-sensitive or not. On Windows it is not.
Once the file is loaded through a classloader (e.g. using getResource()), the filename is case sensitive and Alert.wav is a different file than alert.wav.
Make sure the filename in the source code is exactly the same way as it appears in the filesystem.