Eclipse RCP: Making use of configuration directory - java

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();

Related

Good locations for config file? (shared between users, common per machine)

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.

When distributing a Java-written application to multiple users, how do I reference a common directory that is unique to each user?

Disclaimer:I know that this is something that is covered very early in learning Java, however, I am just a hobbyist and I am self-taught. I am only as good as the information I find online is. Please do not be derisive or condescending. We were all new at one time.
As mentioned, I have a written a Java Form app. What it does it takes user input and writes it to an Excel file. How can I dynamically reference a file (the Excel, in particular) that is on each users' computer, found in a different directory but not have to hardcode each user's exact file path and distribute individually?
Thanks!
I think you are looking for user.home property.
System.getProperty("user.home");
Which will give you, say, c:\users\mk
and then you can append your path to that.
You could have the application ask the user for the path.
As part of the application configuration have the user define an environment variable using a known name with either the desired directory or the install directory (then use the variable to derive the desired directory).
for example:
tell the user to define BLAMMY_HOME which contains the install location of your software (named BLAMMY).
derive the desired directory by concatinating the value of BLAMMY_HOME and "/desired/directory/name".
or
have the user define BLAMMY_SPOT which contains the full path the the desired directory.
use the value of BLAMMY_SPOT in your application.
Defaulting to using user.home is fine, but I (personally) do not like that technique (as a user).
Take an example of 2 users
UserA path: /opt/file/directories/target (contains the excel file)
UserB path: /opt/directories/target (contains the excel file)
There's absolutely no way to find the target directory (except searching for it, but then you might find another one) unless the application has access to some out of band information. For example, Java offers the user.home property
String pathPrefix = System.getProperty("user.home");
so you can use that and make your target directory relative to that.
That's the whole purpose of applications having installation/working directories. As another example, take the Windows Registry. Imagine you had to download a patch from the internet. The patch itself wouldn't be able to check all the paths on the file system until it found yours (each user has a different one). Instead, it can find that path from the Windows Registry (or something comparable depending on the application).

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

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").

Saving properties for Java application during setup

I've created a Java application that is basically an interface to a MySQL database. It helps organize and keep track of data. We are using it in my workplace with no problem - I have exported it from Eclipse as a jar file and given everyone a copy of this jar file.
Now we want to make this software available to other workplaces. The problem is that the URL, username, and password for the database are hardcoded in the application. I want to create a setup process for it so that when someone downloads it, they go through a wizard that downloads MySQL and sets up the database wherever they choose. The person can then distribute the jar file to everyone in their workplace without them having to do the setup, because everyone will be accessing the same database.
This process must save the database URL, username, and password somehow so that the people in the workplace can run the jar from whatever computer. This makes me think that they should be saved inside the jar... is a Properties file that I need? Can I put a Properties file inside the jar and allow it to be edited during the setup process?
Any guidance is greatly appreciated, I'm very new to this!
==================================================================================
EDIT: I think what I'm going to do now is let the user install MySQL and set up their database themself. As the answers below suggested, having this automatic might be more trouble than it's worth, as I would have to deal with everyone's different platforms, preferences for setting up the database, security concerns, etc. Once they do this, they will just download my jar file.
I've added a properties file to my jar file to store the database URL, username, and password. This file is initially empty, so when the user runs the jar for the first time, the program will attempt to access the properties file, see that it's empty, and prompt the user to enter this information. It will then extract the properties file from the jar, edit in their information, and stick the properties file back into the jar. Then, the person should be able to distribute the updated jar to their coworkers and they should all be able to open it without having to supply that information. I've got this part almost working. I'm also going to add the ability to "reconfigure" the program - in case the user moves their database - by calling the same method (they would again have to distribute the new version of the program).
Next I want to try securing the properties file somehow by encrypting it or obfuscating the code (although I think that only works for class files and not text files...?). My concern is that anyone in the workplace can unjar it and open the properties file, then use the URL, username, and password to access the database on their own and cause damage. Ideally, no one would be able to unjar it at all except for the program itself.
If anyone has other concerns about my method, please let me know!
First, it would not be trivial to set up a database in a central location in a workplace so that it is accessible by different users. Also, there is the problem of the first user setting it up and then re-distributing the application to others.
Answering the technical questions - the easiest way would be to unjar to a known location, edit the properties file at that location, and then re-jar to a new file, perhaps with a suffix specific to that workplace.
You could save the property file in a subdirectory of the user home directory, obtained by System.getProperty("user.home). Also, have a look at the Apache Commons Configuration library.
How do you deploy your Application?
If you're using Webstart, you could define your properties in your jnlp-File and access them with System.getProperties(...);
Take a look at HSQLDB. It is a lighter weight db that is rely easy to setup. You can configure it to me the db if it doesn't exist and us it if it does. However, if you need something like MySQL and want to have many users connecting to it from different workstations, I would not recommend downloading and configuring it through an install process. There will be a lot of network and security concerns. As a side note a properties file is a good idea.
This is a link that explains some of the security concerns to think about. Also, thinking about this a little more, users might not have the necessary permissions to setup/configure a db server. It is probably safer/easier for you in the long run to allow them to set up the db server and have them put a properties file in your applications classpath.
As a side note, have you considered making this a web application? That could make things even simpler for you, people wouldnt have to download anything, and there would be no setup for most users.

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

Categories