Where to put global application data in Vista? - java

Where in a Windows (Vista) system should I place data that ought to be readable and writable by everyone, i.e. every user of the computer? Vista's concepts of C:\Users\xxx\AppData\something, C:\Program Files and C:\ProgramData directories and UAC are a bit confusing.
Furthermore, is there any ready solution to determine those locations with Java? I suppose that it requires some interaction with native libraries, since System.getProperties has just user.home and user.dir, neither of which is globally writable.

In vista c:\ProgramData is the place, this replaces what used to be C:\Documents and Settings\AllUsers\AppData in XP.
I'm not sure about the specifics of doing this in java.. but, the ALLUSERSPROFILE environment variable gives you the path if you can get hold of that. You should always use this instead of hard coding the path, because the folder name changes on different internationalized versions of the OS.

If you need to allow users that do not have administrator privileges to modify the global settings then the proper approach is to create an installer for the application and during the install set the permissions on the "Common Application Data" folder such that users area allowed to write to it.
See this post: Where to put common writable application files?

Unless the data is really large, you might want to take a look at the Preferences API; in particular Preferences.systemNodeForPackage. This gives you a platform-independent, system-wide, backend-agnostic storage facility, if that's what you're after.

Furthermore, is there any ready
solution to determine those locations
with Java?
You can check approrpirate environment variables, for instance %PROGRAMDATA%, %PROGRAMFILES% and others.

Related

How to read "OS variables" from Java (not System.properties)

I just read the excellent 12 Factor App document and it really registered with me. In Chapter 3 Config, the author stresses that:
The twelve-factor app stores config in environment variables (often shortened to env vars or env). Env vars are easy to change between deploys without changing any code; unlike config files, there is little chance of them being checked into the code repo accidentally; and unlike custom config files, or other config mechanisms such as Java System Properties, they are a language- and OS-agnostic standard.
I'm wondering what this implies for Java projects.
Typically, when it comes to my Java apps, I think of "env vars" as things that I can read from System.getProperty("blah"). Is this what the author is talking about? I don't think this is, because the author explicitly states "and unlike...or other config mechanisms such as Java System Properties...".
If not, what would be an example of an OS-agnostic "env var" that would satisfy this document's definition of "env var"? How would I read it from inside Java code?
Either way, some process has to first set each env var on the OS, so that the var is set & available by the time the app runs. What processes/methods could do this type of pre-run setup?
Use System.getenv() instead of System.getProperty(). System.getenv() returns value of specified environment variable defined in your OS.
Other, and probably preferable way is to pass selected OS environment variable to your JVM using -D command switch and then retrieve them using System.getProperty(). This way is more cross-platform: you may use the same java code even if specific platform does not support specific variable.
Updating the OS variables is absolutely other task. First, I do not think that you really want to do this. If you still want try to ask another question: probably other solution exists.
There is no cross platform API that does this in JDK and I do not know 3rd party library that dies this too. I personally planned to implement one but did not have time for this so far.
Windows stores variables in registry, so you can add variable to registry. But this does not affect current instance of shell, so your own java process will not see variables that it updated itself. You can however create process and run SET myvar=myvalue. If you want to write to registry you can use any of available libraries. Occasionally I implemented such library too. Its advantage is that it does not run any native code and is very compact. Take a look here.
On Unix you can run myvar=myvalue and if you want other processes to see this variable you have to run export myvar. But it still does not make this variable persisted. If you want this variable to survive OS restart you have to add it to one of the initial scripts (e.g. .bashrc). But this is completely depends on your system, configuration, permissions, shell etc.
Yes, the author is talking about environment variables. You can read environment variables from java code using the following snippet:
System.getenv(env);
See: http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getenv-java.lang.String-
If you want OS agnostic way, you can use JVM arguments and read using
System.getProperties();
For example, if you want a variable called var1 to be passed, use java -Dvar1=true -jar myJar.jar

Running one file on multiple servers

Right now my team deals with about 4-5 different servers and about 2-3 different DB servers and we're using environmental variables to decide which server we're on and what server configuration to use.
Is there a better way to do this as my team continues to expand? I've considered compiler flags / args, but it doesn't seem as robust.
From my perspective, in java, you have basically 3 ways to crack this cookie:
Environment variables
-D JVM parameters (which are System Properties)
properties files
You've already discovered Environment Variables and that is pretty much "the unix way" to get the effect you are after; different configuration to common binary that customizes the running application for the environment it is executing on.
System Properties are really the Java "moral equivalent" of Environment Variables. They come in via -D parameters on your application's command line like...
java -Dlogback.configurationFile=/opt/dm/logback.xml -cp app.jar org.rekdev.App
Explicit Properties file processing http://docs.oracle.com/javase/tutorial/essential/environment/properties.html in Java is a third variant which you often see coupled with -D to get something like default behavior which can be overridden at runtime from the command line. That is what is basically going on with the logback.xml configuration above, the JAR file has a logback.xml inside it that will be used unless a System Property called "logback.configurationFile" exists, at which point the App will load it instead.
As you try to figure out how to keep this all in sync and working correctly in a multi-server environment, consider the use of chef http://wiki.opscode.com/display/chef/Home to do the deployments and put each specific environment's customizations under chefs control. Put the chef "recipes" in version control and, voila, full on configuration management.
SHIP IT!
I can see two scenarios
You embed all the different properties within your package (can be a war, ear, jar, or on the file system /yourapp/etc/)
You embed only one property file and this one is created during build (with ant or maven)
Say your app is named foo
Solution 1
It has the advantage that your app can be put as-is on any of the supported servers (all that have a property file in your app package).
Your properties will be named foo.dev.properties, foo.test.properties, foo.prod.properties, foo.damien.properties, foo.bob.properties.
One other advantage is that every developer working has its own dev file that he can safely push on svn/git/whatever and be sure that other developer won't destroy his config.
At runtime the application can check the -D parameter or even retrieve the hostname dinamycally, in order to load the correct property file.
Solution 2
It has the advantage that your package is not polluted by unnecessary properties files.
You must configure a lot of ant tasks/maven target in order to build for specific environment. You will have in your source directory the properties files for the environments also, but only one will be shipped with your app. This one foo.properties will only have placeholders for values, and values will be inferred within it using foo.ENV.properties with the right ant task/maven target.
At my actual job and the previous one also, we did use the solution 1, I think it brings flexibility.
Some parameter (like database user/password) were fetched directly from environment variables on the Unix servers though (so that only the server admins knew the credentials).
You can safely mix the solutions, in order to get where you feel there is the more flexibility for you and your team.

How to find the PostgreSQL installation folder via Java without user interaction?

In my Java application I want to implement the option to dump/restore a PostgreSQL database. Some google research showed me that calling pg_dump/pg_restore via Java's ProcessBuilder is probably the best way to achieve this.
Now I'd like to determine the system's directory of pg_dump.exe/pg_restore.exe without asking the user to specify it manually. How can I find the PostgreSQL installation path through Java?
Thanks in advance and
all the best,
Matthias
You can't do that portably. I suggest that you just call the programs without path by default and rely on the user having set an appropriate path, and have a way for the user to configure the paths explicitly. You can also look in some likely directories for a default configuration. But it will be quite fragile in general, especially if you want to cover all of Windows, Linux, and Mac, say.
A good idea would be to simply define an environment variable called PG_HOME and use System.getEnv("PG_HOME"); to retrieve that.
Look inside the default directories first (probably somewhere C:\Program Files (x86)\PostgreSQL\...). Use different combinations to be as tolerable as possible.
If it fails, just search the file system. Nice example here.
Also see How to do a backup from a Postgresql-DB via JDBC?

Good repository organization practice for Java properties files

I maintain a multi-platform development framework that attempts to configure environment variables based on certain system and environment information that is inferred via various means. Once I infer these variables, I store them in a Java properties file for later use. This file could also be edited by the user of my framework (a developer).
I have a class called Env that manages this properties file, and it's in a package called org.myproject.config. I'm currently storing the default properties file in src/org/myproject/config. I'm wondering, is it good practice to store a config-type file under this directory of my project? Not sure where to go with this. Any input is appreciated.
-tjw
You should use the Java Preferences API if you wish to maintain platform neutrality. Once you specify a file path, you make a lot of assumptions about the environment your application is running on.
From what we experienced, environment specific information should not be stored on a repository. Someone updates by mistake an file and when he commits other users/environments are affected. We keep environment configuration separate and local on each environment. In our case the environment variables are stored in database tables, and the only environment information we have in property files is the minimum required fields to create a JDBC connection to the location of the environment variables. updates to environments pass through a IT change request or are done via an software upgrade.

reading/writing to files so it works in both a linux/windows environment

If someone installs my java application on their computer (windows/linux/mac), how can I make sure my applications ability to read/write files works in all environments.
Is there a way for my application to figure out where in the file system it is installed, and then read/write safely?
Note: these files that are being read/written are all application files i.e. it is not trying to reference any operating system file/registry.
So all files can be within the applications root folder (wherever it is installed)
Using the java.io.File (or perhaps java.nio package). This will generally work cross-platform, but you will need to be aware of platform differences and code around these. For example, you need to use things like File.pathSeparator to ensure you use the correct path separator for the platform. Also, depending on what you are doing, there are differences between how locking works etc, and not all operations are guaranteed to work - some just fail silently.
Java's file IO library is platform independent and should work on any OS that the jvm is installed on. With that said, some file systems behave differently (permissions, user/group, etc), which can cause your file operations to succeed on one platform, but fail on another. For this reason, it is always a good idea to test your code on all systems you wish your system to run on.
You need to set a property, e.g., with "-Dapphome=abx" at the command line or in some configuration file. In the former you can retrieve it with System.getProperty("apphome"), in the latter you still need to have some way to find that configuration file unfortunately.
If it helps you can find the user's home directory with System.getProperty("user.home"). This can be very helpful since you can read per-user configuration files by using that as a starting point. Common files for multiple users will need to go into the system somewhere, e.g., /etc/appname/config.properties or C:\
BTW you should use System.getProperty("java.io.tmpdir") for your temporary files. Don't clutter up the directory where your app was launched (if you can -- you may not have the necessary permissions!) or the user's home directory. You need to be careful though - create a subdirectory for your app, and maybe a subdirectory for each user, to avoid the risk of one app stepping on the temporary files used by a second app.
The Java File class accepts "/" as path separators, so just use that. If you need the root drives do not code C: or anything but ask the JRE for the roots, and use them.
Is there a way for my application to figure out where in the file system it is installed, and then read/write safely?
You can maybe read the user.dir system property to get the path that the application was started in. Note that user.dir is a read only property, i.e. you can't change the "current directory" by setting the user.dir property. You read system properties with the System.getProperty(String) method. This is not exactly the same thing as "installed in" but it may work. But it's kinda weak.
If really you want the location of the install directory, either force the user to set an environment variable (MYAPP_HOME) or scan the whole file system. Personally, I don't like these options.
Actually, and if the data are user specific, the best choice in my opinion would be to read/write data in the user home directory (use the system property user.home to get it), for example in something like ~/.yourapp (Windows users never go in their %USER_HOME% anyway) or, even better, in a directory following Freedesktop XDG Base Directory Specification (obviously, only Linux users would care of that).
Then, to read/write, just use the java.io.File which is cross-platform when used properly (e.g. use File.separator if you need to build a path, don't use an hard coded version of the name-separator).

Categories

Resources