I'm currently writing a replacement shell in java for windows, and my application is currently creating a config file using
File userSettings = new File("Gyroscope.properties");
However, when this is run by windows, the current directory is set to C:\windows\System32\ and the program doesn't have write permissions to this directory.
What would be a better location for the configuration file to be written to? It would be preferable to have it relative to the jar file, however I do not know a good method of doing so, and other stack overflow questions have had unclean solutions to this exact problem, so I am looking for other locations as options.
The configuration is machine specific, and should be common to all users.
Use the Preferences class, it'll make sure that the settings are written to somewhere where it's allowed (such as user's home dir or the registry).
I will describe a possible solution. There should be several way to achieve it.
Put your config file next to your jar. Put explicitly the config file in your classpath while invoking java (e.g. java -cp .../Gyroscope.jar;.../Gyroscope.properties <your_main_class>).
Now you can access your properties file as a ressource (ClassLoader.getSystemResourceAsStream("/Gyroscope.properties"). Use whatever classloader you want as soon as it is convenient and cleam for your actual implementation. Once your have an inputstream, you can load your properties into a Properties object as usual.
Hope this will help.
Related
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").
As i know it returns the application path? But what exactly the use of it.
In many environments the application user is not allowed to read any files outside of the deployment directory. This is mostly done for security purposes - for example if someone hacks your application they won't be able to read a passwords file.
And in professionally managed environments developers often don't have a say in which directory the application will be placed.
So if you need to read a file like properties, images, certificates, etc. you can place it in the application directory (or .war file) and use getRealPath("") to get the path you need to load.
As an alternative you can place the external files on the classpath but there are sometimes issues with this. For large files most app servers will try to load the entire file into memory and cache it if it is on the classpath.
The getRealPath() gives the absolute path (on the file system) leading to a file specified in the parameters of the call. It returns the path in the format specific to the OS.
The getContextPath() on the other hand returns the URI or the relative path to the resource.
As far as I remember, I've used it to save images or other data files, since it allows you to see where your application is deployed at the moment. For example, Eclipse and Tomcat will create a temporary folder that's buried deep somewhere within your Eclipse profile and deploy the app there.
This is a real path in file system.
From javadoc:
The real path returned will be in a form appropriate to the computer and operating system on which the servlet container is running, including the proper path separators. This method returns null if the servlet container cannot translate the virtual path to a real path for any reason (such as when the content is being made available from a .war archive).
I think it is very clear. Why do we need this? Sometimes web applications perform some manipulation in file system. For example read stuff from files, write files etc. This API allows you to access the place where your JSPs and other stuff is really stored.
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.
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
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();