Is writing into class resources a good way to save files? - java

When we want to load a static file e.g. a picture, a sound file, a file containing information about a game map,... we can store them as resources in jar file and use getClass.getResource("images/splash.png") (also getResourceAsStream) to load and use them. But when we want to read and write into a file like settings file, I don't think using resources is a good way, because i think resources are designed to store read/only files that are not supposed to change, like splash screen image or a game's background music; These are my reasons to think this way:
That is why return value of getResourceAsStream is an instance of InputStream and we don't have a similar function which gives us an OutputStream, because we're not supposed to alter resource files.
Writing into resources changes program .jar file and i guess it's not a good thing at all; Because if we do so: we can't use check-sums to verify file, if we are a limited user and system administrator doesn't give us write permission we can't make changes into main .jar file, user-specific preferences are hard or impossible to implement,...
So, my questions are:
Which parts of my thoughts and assumptions are right or wrong?
If they're right what is the best(I mean short and portable between OSs and Computers) way to store files like that? (Application setting/preferences, A game save file, ...)
(#Some user who may wants to mark this as duplicate: I don't think my question is a duplicate, i searched in the site, I admit it has some common parts with some questions but it's not duplicate!)

Your three observations in #2 above are valid reasons not to store settings in a resource file, regardless of the APIs provided.
There are a variety of ways to save settings in Java, including:
The Java system property "user.home" provides the user's home directory, to which the user should have write access. You can create an application-specific subdirectory underneath it.
Java provides a Preferences API. This may store settings in a directory or (on Windows) in the registry.
OSGI provides a preferences API.
If you're using the Eclipse RCP, you can write to the configuration directory using a ConfigurationScope. See the Eclipse FAQ "What is a preference scope").

Related

Where to typically save external data from Java application?

Ok, the question is quite simple.
I have a Java program, which I am extracting some save-files to an external location.
Right now, I use C:/ApplicationName, however, that is a very bad way to do that.
I know alot of locations i could use, for instance:
%Appdata%
C:\Program Files (x86)
C:\Users\Users\Documents (Ive seen some indie games use this.)
Other locations?
But I can't figure out, when to use the proper one.
And if i want to support Linux and OSX, is there a libary, which supports that, or do i manually have to wrap them into an if/else with System.getProperties("os.name")?
Keep in mind that sometimes when games save to a specific directory, they aren't built to be cross-platform.
There are two ways you could do this. The first (and personally my preferred version) is to save in the directory of the application. This also has the advantage of being portable. However, the saves are linked to the application, so if the user deletes the application and reinstalls later they'll lose their data (which may or may not be a good thing).
Another option is to use System.getProperty().
Specifically, you can use:
System.getProperty("user.dir");
System.getProperty("user.home");
System.getProperty("user.name");
To figure out where to put your files.
This will be a lot more complicated, so the first method is preferred.
If it's data the user does not need direct access to, such as stored application/game state data, then it's appropriate to store it in the user's application data directory. Try this:
1) Determine the OS.
2) Get the user home directory.
System.getProperty("user.home");
3) Append OS-specific application directory:
Mac: /Library/Application Support/MyApp
Windows: \\Application Data\\MyApp
Linux: MyApp (there's no convention here that I know of)
If the data needs to be exported, such as saving a document from your application, then ask the user where to store the file via a file dialog, defaulting to their Documents directory.

Open and edit file in .jar with Java?

How would you go about opening an .xml file that is within a .jar and edit it?
I know that you can do...
InputStream myStream = this.getClass().getResourceAsStream("xmlData.xml");
But how would you open the xmlData.xml, edit the file, and save it in the .jar? I would find this useful to know and don't want to edit a file outside of the .jar... and the application needs to stay running the entire time!
Thank you!
Jar files are just .zip files with different file suffix, and naming convention for contents. So use classes from under java.util.zip to read and/or write contents.
Modifying contents is not guaranteed (or even likely) to effect running system, as class loader may cache contents as it sees fit.
So it might be good to know more about what you are actually trying to achieve with this. Modifying contents of a jar on-the-fly sounds like complicated and error-prone approach...
If you app. has a GUI and you have access to a web site/server, JWS might be the answer. The JNLP API that is available to JWS apps. provides services such as the PersistenceService. Here is a small demo. of the PersistenceService.
The idea would be to check for the XML in the JWS persistence store. If it is not there, write it there, otherwise use the cached version. If it changes, write a new version to the store.
The demo. writes to the store at shut-down, and reads at start-up. But there is no reason it could not be called by a menu item, timer etc.

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.

Java OutputStream equivalent to getClass().getClassLoader().getResourceAsStream()

I am attempting to store the change made to my application's properties. The .properties file is located in resources package, which is different from the package that contains my UI and model.
I opened the package using:
this.getClass().getClassLoader().getResourceAsStream("resources/settings.properties")
Is there a functional equivalent of this that permits me to persist changes to the Properties Class in the same .Properties file?
In general, you cannot put stuff back into a resource you got from the classloader:
Class loader resources are often read-only; i.e. held in read-only files / read-only directories.
If you got the resource from a JAR file, JAR files are not simply updateable. (To "update" you need to extract the old JAR's contents and create a new JAR with the updated contents. It is all to do with the structure of ZIP files ...)
In some cases, the class loader resource will have been downloaded on-the-fly, and there is no way to push changes back to the place where you downloaded from.
Even if you can update a resource you got from the classloader, it is a bad idea / bad practice.
Doing this "pollutes" the clean application installation with a user's preferences. Among other things, this means that the installation cannot be shared with other users (unless you handle preferences for multiple users ...).
There are security issues with having applications installed as writeable so that embedded preferences can be updated. Think viruses! Think one user who might be inclined to trash another user's preferences!
There are administration issues with having user-specific copies of applications. And if the user has to install his own copy of an app, there are potential security issues with that as well.
There may be technical issues with file locking or caching on some platforms that either get in the way of (safe) updates or make it difficult for an application to load the updated resource without a restart.
Finally, this is NOT the way that system administrators (and educated users) expect software to behave. Java applications should deal with user preferences in the expected way:
You can use the Java Preferences API.
You can write a Properties file containing the preferences to an OS-appropriate user-writable directory.
On Windows, you could use a Windows-specific API to store the preferences in the Windows registry, except that this makes your application Windows dependent. (I can't see any real advantage in doing this, but I am not a Window expert.)
When you wrap your app up as a JAR file, your properties file will be one (possibly compressed) file within that JAR, and it would be a bad idea to try to write to your own JAR.
getResourceAsStream() is meant to open resources for reading, and these can be anywhere on the classpath. You can't write to URLs or inside JARs, you can only write to files, so it doesn't make sense to give you the same API for output.
Find yourself a directory you're allowed to write into, and write your properties there.
It may be a good idea to copy your properties from your installation classpath (possibly inside a JAR) directly out to a file if it doesn't yet exist, as a first operation upon application startup. This will give you a properties file you can write to, yet the master copy of this properties file will come from your project deliverable.
It sounds like you want to store user preferences. Consider using the Java Preferences API for that.
In addition to Carl's answer, if you're going to read and write to this file frequently, and expect that your application will expand in scope, consider whether to go one step (or several steps) further and use a file-based database like SQLite. There are a few JDBC wrappers for SQLite that would allow you to go beyond the basic string key-value lookup that the Java Properties interface provides.
even though writing the file into resources is not good practical, we still need to do it when our application only run in IDEA locally without deployment, then we can do it as below:
URL resource = Thread.currentThread().getContextClassLoader().getResource("settings.properties");
String path= resource.getPath();
OutputStream outputStream = new FileOutputStream(path);
//outputStream write

Eclipse RCP: Making use of configuration directory

My Eclipse RCP application requires a configuration file that contains some information to connect to a remote database. Where is the best location to store this configuration file?
Can I use the default configuration directory (where 'config.ini' is usually stored) for this purpose? If so, how can I get a File instance to this location programmatically? I also note that this directory does not exist in my Eclipse IDE.
Thanks.
You have, as always, a number of options, depending on your requirements.
use the Runtime Preferences to store in a PreferenceStore with a suitable PreferenceInitializer. Quite a large and extensive API with quite a lot of thought gone into it. The preferences aren't exposed to the user or admin by default, so you'd need to do some work to expose a preference page, or write to a properties file.
For less advanced/less work, especially if you don't have access to the eclipse preferences (e.g. server side OSGi):
set as a system property, in the RCP.ini. Not user-changeable after launch, requires access to the RCP.ini (eclipse.ini) file which may be possible especially if you're not contributing the the IDE.
set as a system property, as an argument in the shortcut. Depends on the user using the shortcut. Specialized shortcut needs to be generated at installation time.
If accessibility from the filesystem is really important, then I would consider using one of the methods above to set an etc directory, and the let your bundles generate default properties files in the etc directory if they don't exist on first use. This is essentially rolling your own preference store, so if you do have access preferences bundle, you may be better off doing that. This rather old User Settings FAQ may also be helpful.
I do recall an Erich Gamma (as in Gang of Four, and JDT technical lead) interview in which he says that there are about seven different preference mechanisms, and he never knew which one to use.
As already pointed out, the Preferences API is something to look at. There is also the Secure Preferences API which is suitable to store user names and passwords encrypted on disc.
Another option is to use the 'org.eclipse.osgi.service.datalocation.Location' OSGi service. This provides access to the different locations available.
A third option is to define a system property in 'config.ini' which points to file with your connection information using placeholders: 'my.connection.settings=#config.dir/mysettings.ini'. '#config.dir' is a placeholder which gets replaced with the actual path to the configuration directory.
Take a look at the resources plugin - might give you what you're looking for:
http://help.eclipse.org/help33/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/resInt_filesystem.htm
Usually, I like to hide the config files in a "bin" directory, or somewhere not in the root directory. You should probably keep it in a sub-directory of your project so you don't clutter up some random location on the system. If you need to get a handle to the File, you can just do:
File configFile = new File("./bin/remoteDbConfig.ini");
Then if its a true ini file, you can use Properties.load() to load and use the values from the ini file.
You could also use the Preferences API to store the data you need for the remote connection.
To get the file location of the Configuration directory, run:
new org.eclipse.core.runtime.preferences.ConfigurationScope().getLocation().toFile();

Categories