Loading and saving resources inside a runnable jar? [duplicate] - java

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can a Java program use files inside the .jar for read and write?
I am in the process of creating a game, and I want it all to run from on runnable jar file, so all of my resources (images and text files) are going to be inside the jar file.
The problem that I'm having with this is that it is extremely difficult to deal with files inside the jar.
I am using eclipse, which can sometimes play tricks on you because it will find the files if you run it from eclipse, but if you export it won't.
I just want to know basically the proper way to dew a few things:
I need to be able to load images (which I have working, somehow. I sort of tinkered with it and did it on accident, so I have no clue how it works)
I need to be able to read from text files (I have this working too, again, by accident and hours of guessing.)
I need to be able to write to text files that exist and and are in the jar. This is what is making me think I'm doing it all wrong. All I want to do is be able to save certain settings so they work on the next load, and I have no clue how to write to the file.
In eclipse (Indigo) I made a folder names "resources" and marked it as a source folder. I put all of my images and text files in there.
I read in images like this:
public static Image ammo = new ImageIcon(TankMazeGame.class.getResource("ammo.png")).getImage();
I read text files like this:
InputStream is = TankMazeRunner.class.getClassLoader().getResourceAsStream("Settings.txt");
Scanner settingsReader = new Scanner(new InputStreamReader(is));
I am writing to my settings file like this, but it isn't really working, so that's what I need help with.
File settingsFile = new File(DisplayMenu.class.getClassLoader().getResource("Settings.txt").getFile());
try {
OutputStream os = new FileOutputStream(settingsFile, true);
PrintWriter pw = new PrintWriter(os, true);
pw.write("SIZE: " + TankMazeRunner.WIDTH + "\n");
os.flush();
pw.close();
} catch (Exception e2) {
System.out.println("Error");
}
As you can probably tell from my code, I'm lost.

For others who come across this question.
You cannot do this unless you can place constraints on the OS being used, and even then you may not be able to do this.
Here are some constraints to be aware of:
Microsoft Windows implements its own read-write lock symmantics: you cannot write to a file while there is a read handle open. The JVM will have a read handle open as the this is the file on the classpath, therefore not possible in Java (unless you want to fork a temporary process, kill yourself, let the temp process rewrite and relaunch... Which will be ugly and the anti-virus will likely stop the temp process anyway)
Most unix OS implementations will have no issue, though the JVM will have cached offsets within the JAR file, so your program will crash as soon as you are done and attempt to load a class.
only windows users tend not to use file permissions to lock down write access to executables, so it is invalid to assume you have write access on the jar.
The second point has you dead in the water for all OS impls (unless you can live with a fork, and the fork makes a lot of risky assumptions anyway)
Just write to a separate file and be done.

How about you write settings to a well known location outside of the JAR?
As alternatives for saving settings, consider one of these approaches:
java.util.prefs.Preferences, mentioned here.
JWS PersistenceService, described here.
Cookies, discussed in Accessing Cookies.

Related

How to save URI of serialized Object between sessions? [duplicate]

I know that .jar files are basically archives as well as being applications. What I'm asking is how can I store data(actual files not just strings) packed inside my program? I want to do this within my Java code.
The reason for this if your wondering is that I'm producing a server mod of a game. The server starts and creates all the level data and I want to store all these file inside my .jar app.
Yes you can do this.
Non-code resources in a JAR file on the classpath can be accessed using Class.getResourceAsStream(String). Applications routinely do this, for example, to embed internationalized messages as resource bundles.
To get your file into the JAR file (at project build time!), just copy it into the appropriate place in the input directory tree before you run the jar command. Build tools such as Maven, Gradle, etc can automate that for you.
Is there a way to add files to the archive within the app?
In theory, your application could store files inside its own JAR file, under certain circumstances:
The JAR has to be a file in the local file system; i.e. not a JAR that was fetched from a remote server.
The application has to have write access to the JAR file and its parent directory.
The application must not need to read back the file it wrote to the JAR in the current classloader; i.e. without exiting and restarting.
The JAR must not need to be be signed.
The procedure would be:
Locate the JAR file and open as a ZIP archive reader.
Create a ZIP archive writer to write a new version of JAR file.
Write the application's files to the writer.
Write all resources from the ZIP reader to the writer, excluding old versions of the applications files.
Close the reader and writer.
Rename the new version of the JAR to replace the old one.
The last step might not work if the initial JAR is locked by the JVM / OS. In that case, you need do the renaming in a wrapper script.
However, I think that most people would agree that this is a BAD IDEA. It is simpler and more robust to just write regular files.
The other answers have provided some good strategies, but I am going to suggest going in a somewhat different direction.
This game supposedly has graphics and is a desktop application. It is most easy to distribute desktop applications from a web server.
If both those things are true of your game, then look into using Java Web Start to deploy it.
JWS offers APIs not available to other apps. & one of particular interest to this problem is the PersistenceService. The PersistenceService allows for small amounts of data to be stored and restored by an app. (even when it is in a sand-box). I have made a small demo. of the PersistenceService.
The idea would be to check the PersistenceService for the application data, and if not found, use the data in the Jars. If the user/application alters the data, write the altered data to the PersistenceService.
JWS also offers other nice features like splash screens, desktop integration, automatic updates..
This is not possible. You however can look into embedded databases for your usecase. Java 6 comes with JavaDB. If you doesn't want to use it then you can find more here http://java-source.net/open-source/database-engines
I would recommend that you consider having two JARs: one to store your application's class files and another JAR to store the user data. If you do not have two separate JARs, then you will have difficulties obtaining a write lock from the Operating System (since you would be trying to overwrite the JAR containing your program while java is reading it).
To create a JAR, use the java.util.jar.JarFile class. There is also another question on stackoverflow which describes how to create/write a JAR file.
Don't do this. A jar file is a source of application classes and resources, not a file system. You wouldn't try to save files into a exe, would you?
By creating a file in the Source Packages (ex: /src/resource/file.txt) its contents can be read using Class.getResourceAsStream(String)
This is a working implementation of the following answer
InputStream is = Class.class.getResourceAsStream("/resource/file.txt");
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line;
while((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
System.out.println(sb.toString());

Can I write a program to monitor files using Java or another language? [duplicate]

This question already has answers here:
Is there a sophisticated file system monitor for Java which is freeware or open source? [closed]
(5 answers)
Closed 8 years ago.
Is there a way to monitor files using java in Linux ?
I don't mean modifying files, but any reading operations.
For example if I have a file x.txt can I know if the user printed, emailed, or copied data from this file?
If I can't do that in java, is it doable in other languages? Or is there open source program that can do this thing?
The Linux inotify API can emit an IN_ACCESS event for a watched file. Unfortunately, for some reason (Windows compatibility?), the default Java APIs don't expose this.
The third-party library https://bitbucket.org/nbargnesi/inotify-java loads a C++ .so lib that hooks into the native libraries to get around this. I haven't used the library personally, but reading around the test files a basic approach seems to be:
InotifyContext ic = new InotifyContext();
Random random = new Random(); // for generating file descriptors
ic.addPath("file/you/want/to/monitor", random.nextInt());
final InotifyEventListener i = new InotifyEventListener() {
#Override
public void filesystemEventOccurred(InotifyEvent e) {
if (e.isAccess())
// handle file being accessed
}
};
ic.addListener(i, random.nextInt());
Note: This catches any read from a file, e.g. your own program (I think, you'd need to test that), the user simply opening the file in a text editor and immediately closing it without doing anything, some filesystem crawler automatically finding the file and scanning it for viruses (although Linux, so...). You'll probably want to do some additional checking on the event before immediately yelling at the user or whatever.

Java Properties file across multiple programs

So I have to figure out how to use a single .properties file across 7 different executable jar files that all eventually call one another in some way shape or form. I'm compiling with ant to a dist folder and I will put the properties file in that folder but I want to know if once I start the first program if it is possible to access the buffered properties file from the other 6 programs?
Properties properties = new Properties();
BufferedInputStream stream = new BufferedInputStream(new FileInputStream("example.properties"));
properties.load(stream);
stream.close();
String sprache = properties.getProperty("lang");
But I'm not sure how to access that same buffer from program 1 to program 2?
Edit: I guess I im hoping there is a way to share that buffer but in theory I could just load an instance of the properties file into each of the jar files.
If the programs run in separate VMs, you cannot access objects of one program from the other, unless you use some inter-process communications (IPC) functionality (shared cache, socket communication, etc.), but implementing such functionality is much more complex (and even slower) than simply re-reading the properties file in each program.
7 executable jars will share 7 different JVM processes when running, so the normal (or is that sane?) answer is: no. Now if you are willing to run a small server app, you could use RMI to synchronize between them. However, at that point you're starting to run into the question of why they're not EJBs running in a container where the resource can be shared.

Safe file-based data persistance in Java EE

Is it possible to have a Java EE application (based on Spring Framework, running in Tomcat container) persisting its data in a file on the server?
The scenario is as follows: I have a class with an int field (read from ?? during startup). I want to save it to a file in a safe manner (as safe as possible, meaning surviving server crash would be appreciated). Is it possible (besides naive file reading/writing)
Kind regards,
q
Really the only "safe" way to do it is to rely on the underlying file system.
Simply:
public void saveThing(Serializable thing, String fileName) throws Exception {
String tempFileName = fileName + "_tmp";
File tempFile = new File(tempFileName);
FileOutputStream fos = new FileOutputStream(tempFile);
FileDescriptor fd = fos.getFD();
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(thing);
oos.flush();
fd.sync();
oos.close();
f.renameTo(fileName);
}
What's happening here is first we're writing the file to a temporary file. This ensures that the entire file write succeeds without damaging the original file (for example, if you run out of disk space, the original will be retained as this routine will not finish). However if this routine fails, the lingering temp file will remain, and will need to be cleaned up later.
Once we've written the file, we force the OS to flush any pending writes to the actual disk. Many systems buffer file system writes to ram, and "eventually" write them out to disk. This is for obvious performance reasons. However, should the system crash or lose power between when you closed the file, and the OS decides to flush the writes, you can potentially lose data. This sync is an EXPENSIVE operation.
Finally, once we are sure that we have written the file, and that it is committed to disk (as sure as we can be anyway), we then RENAME the temp file to the actual file name.
Renaming a file on the file system is an atomic operation. It's can't partially fail. It either works, or it doesn't. If the two files are on the same file system, the rename is near instantaneous since it simply updates some file system information. If the two are on separate file systems, then the new file must be copied first to the new file system, and then renamed. I ASSUME this is how it is done, I never tested this. I tend to stick to the same file system and avoid the question completely.
This process ensures that the file will be updated, under the correct name, completely, "all at once". The file (under its correct name) never only "partially exists", which is what would happen if you were to simply overwrite the existing file.
Finally, on Windows you may have a problem if there is contention for the original file, since Windows will not delete a file that is opened by something else. Unix has no problem doing this, but Windows does. So you need to ensure through some external means that you have sole access to the file before doing this rename procedure.
The short answer is yes. I actually had to do just that for a project that I did with a university a while back. I posted the code for it on my git hub: Speak To Me project. In that Web app, I persisted user data to file in plain text so it was both human readable and easy to for objects to reinitialize themselves.
So readers of this question might be wondering why I didn't use a database for these purposes. Well the university that I was working with didn't want to support one. As well, this app had really low traffic; it is a research prototype for testing search interfaces so it was only used for user studies. Finally, because of the nature of the application, persisting to file keep things really simple. In fact, the data files were later used for post study analyses. Plus it kept the option open for students who were not great coders to get their feet wet (that... never happened).
Anyhow, my recommendation is that if you are just persisting simple values, then plain text will be fine. If your data has any amount of complexity, then use JSON. XML is a bit heavyweight and really should only be used if your application is large but in that scenario, you shouldn't be persisting to file.
It may be on overkill for your situation, but you could use HSQLDB. You can configure it to persist in a file.
For a simpler solution, you can always write/read from a file. Some issues worth of consideration:
Use JNDI or a system variable to store the name and path of the file.
Make sure that the user that runs the server has read/write access to the file.
Other than that you can use standard Java File operations
You can use the serializable interface in Java to create persistent objects that you can save and reload from disk.

how to write into a text file in Java

I am doing a project in java and in that i need to add and modify my
text file at runtime,which is grouped in the jar.
I am using class.getResourceAsStream(filename) this method we
can read that file from class path.
i want to write into the same textfile.
What is the possible solution for this.
If i can't update the text file in jar what other solution is there?
Appreciate any help.
The easiest solution here is to not put the file in the jar. It sounds like you are putting files in your jar so that your user only needs to worry about one file that contains everything related to that program. This is an artificial constraint and just add headaches.
There is a simple solution that still allows you to distribute just the jar file. At start up, attempt to read the file from the file system. If you don't find it, use default values that are encoded in you program. Then when changes are made, you can write it to the file system.
In general, you can't update a file that you located using getResourceAsStream. It might be a file in a JAR/ZIP file ... and writing it would entail rewriting the entire JAR file. It might be a remote file served up by a Url classloader.
For your sanity (and good practice), you should not attempt to update files that you access via the classpath. If you need to, read the file out of the JAR file (or whatever), copy it into the regular file system, and then update the copy.
I'm not saying that it is impossible to do this in all cases. Indeed, in most normal cases you can do it with some effort. However, this is not supported, and there are no standard APIs for doing this.
Furthermore, attempts to update resources are liable to cause anomalies in the classloader. For example, I'd expect resources in JAR files to not update (from the perspective of the application) until the application restarted. But resources in exploded JAR files probably would update ... though new resources might not show up.
Finally, there are cases where updating a resource is impossible:
When the user doesn't have write access to the application's installation directory. This is typical for a properly administered UNIX / Linux machine.
When the JAR file is fetched from a remote server, you are likely not to be able to write the updates back.
When you are using an arbitrary custom classloader, you've got no way of knowing where the actual bytes of an updated resource should be stored, and no way of storing them.
All JAR rewriting techniques in Java look similar. Open the Jar file, read all of it's contents, and write a new Jar file containing the unmodified contents (and the modifications you whished to make). Such techniques are not advisable for a Jar file on the class path, much less a Jar file you're running from.
If you decide you must do it this way, Java World has a few articles:
Modifying Archives, Part 1
Modifying Archives, Part 2
A good solution that avoids the need to put your items into a Jar file is to read (if present) a properties file out of a hidden subdirectory in the user's home directory. The logic looks a bit like this:
if (the hidden directory named after my application doesn't exist) {
makeTheHiddenDirectory();
writeTheDefaultPropertiesFile();
}
Properties appProps = new Properties();
appProps.load(new FileInputStream(fileInHiddenDir));
...
... After the appProps have changed ...
...
appProps.store(new FileOutputStream(fileInHiddenDir), "Do not modify this file");
Look to java.util.Properties, and keep in mind that they have two different load and store formats (key = value based and XML based). Pick the one that suits you best.
If i can't update the text file in jar what other solution is there?
Store the information in any of:
Cookies
The server
Deploy the applet using 1.6.0_10+, launch it using JWS and use the PersistenceService to store the information. Here is my demo. of the PersistenceService.
Also, if your users will agree to a trusted applet (which seems overkill for this), you might write the information to a sub-directory of user.home.

Categories