Platform-independent way for storing application data

J

Jan Thomä

Hi,

i am currently trying to find a way to store application data in a
platform independent way. I have a derby database which i need to store
somewhere on the user's PC. On Windows a place for this would be
C:\Documents and Settings\<username>\Application Data. On Mac OS x the
path would be /home/<username>/Library/Application Support/. However I
don't want to hard code these locations as (especially when using
multilanguage versions of windows) they might be different on any
system. I know that i can store preferences with the preferences API
that is part of the JDK, but this doesnt work for my Derby database
file. Is there an API (maybe even inside the JDK) that allows me to
retrieve the system dependent folder name where application data is to
be stored? Any help would be greatly appreciated.

Best regards,
Jan
 
S

Stefan Ram

Jan Thomä said:
Is there an API (maybe even inside the JDK) that allows me to
retrieve the system dependent folder name where application
data is to be stored? Any help would be greatly appreciated.

I did a search for existing specifications and implementations
for this.

After getting no results, I wrote a specification and
implementation myself.

Portadir intends to establich a vocabulary for directories:

http://www.purl.org/stefan_ram/pub/the_portadir_specification

Here is a preliminary publication of the class »FileSystem«
that implements some directories specified in Portadir.

http://www.purl.org/stefan_ram/java/FileSystem.java

The regular publication is expected to be part of the next
release of »ram.jar«.
 
T

Thomas Kellerer

Jan Thomä wrote on 19.02.2009 00:26:
Hi,

i am currently trying to find a way to store application data in a
platform independent way. I have a derby database which i need to store
somewhere on the user's PC. On Windows a place for this would be
C:\Documents and Settings\<username>\Application Data. On Mac OS x the
path would be /home/<username>/Library/Application Support/. However I
don't want to hard code these locations as (especially when using
multilanguage versions of windows) they might be different on any
system. I know that i can store preferences with the preferences API
that is part of the JDK, but this doesnt work for my Derby database
file. Is there an API (maybe even inside the JDK) that allows me to
retrieve the system dependent folder name where application data is to
be stored? Any help would be greatly appreciated.

What's wrong with System.getProperty("user.home")

File datadir = new File(System.getProperty("user.home"), "MyAppData")
 
S

Stefan Ram

Jan Thomä said:
Is there an API (maybe even inside the JDK) that allows me to
retrieve the system dependent folder name where application
data is to be stored?

In addition to my previous answer:

The direct answer to your question is: »No« (as of JDK 1.6).

(What you are looking for is called »user var directory« in
Portadir.)

I also tried to find this: Let's use Windows as an example.

Windows has a set of system variables called »Shell Folders«,
the Shell Folder you are looking for is called »AppData«.

There is no way to read »AppData« by only using Java SE.

Some parts of the Windows-JDK access the Shell Folders
internally, for example to configure and install Java itself,
but these features are not accessible to Java applications.

Under Windows, an application can access these variables with
JNI, but this would be a solution for this platform only.

Another option would be to ask the user for the folder to be
used upon the first time your programm is started and then
remember his choice (think of the Eclipse »workspace«).

Another option would be to accept the path to this folder via
a command line argument, for example, »-Dapp.data=path«. Then
it could be provided by a small shell script wrapping the
application start.

Remember: The Shell Folder AddData does not always have to be
»C:\Documents and Settings\<username>\Application Data«,
it can be somewhere else (for example under Windows 98)
and it could have been relocated by the user or administrator
to any other location. So you can not get it by obtaining
the username and then using
C:\Documents and Settings\<username>\Application Data
. It needs to be obtained using the Win32-Shell-Folders-API.

(Mozilla Firefox and K-Melon have the bad habit to actually
/modify/ this Shell Variable back to the Windows default (at
least at my site), which disturbs up a lot of other software.
One has to manually restore it back to the path wanted.)
 
N

neuneudr

On Feb 18, 11:49 pm, (e-mail address removed)-berlin.de (Stefan Ram) wrote:
....

Is your whole post (and code example) sarcasm?

Broken lazy instantiation, use of "Windows" backslash
file separator in the .java source code (which is a big
no-no), misuse of the "multiline comments", no proper
Javadocs, etc.

That .java source file is worthy of the daily WTF IMHO.

Is this a way to help the original poster and is
this way to help future readers, browsing the
archives, looking for an answer to the same problem,
by marking your post as "non archivable" ?
 
J

John B. Matthews

On Feb 18, 11:49 pm, (e-mail address removed)-berlin.de (Stefan Ram) wrote:
...

Is your whole post (and code example) sarcasm?

Broken lazy instantiation, use of "Windows" backslash
file separator in the .java source code (which is a big
no-no), misuse of the "multiline comments", no proper
Javadocs, etc.

I am unable to locate a backslash used as a file separator, except in
the helpful comments. The example is distributed under the terms of GNU
Public License, should you wish amplify the documentation.
That .java source file is worthy of the daily WTF IMHO.

I enjoy that site immensely, but you may wish to prepare yourself for
rejection.
Is this a way to help the original poster and is
this way to help future readers, browsing the
archives, looking for an answer to the same problem,
by marking your post as "non archivable" ?

I'm sorry, I seem to have overlooked your contribution.
 
S

Stefan Ram

Thomas Kellerer said:
What's wrong with System.getProperty("user.home")


| A footnote to anticipate answers involving »user.home«:
|
| The documentation says this:
|
| »This set of system properties always includes values for
| the following key: [...]
|
| user.home User's home directory
| user.dir User's current working directory«
|
|http://download.java.net/jdk7/docs/api/java/lang/System.html
|
| »java.lang.System.getProperty( "user.home" )« might sound
| good, but gives »C:\WINDOWS« on Windows 9x, which might not be
| the best place for user documents.
|
| It is not only broken on Windows 9x, but also on Windows NT:
|
| »We made a conscious decision to change the definition of
| the "user.home" property on Win32; see 4100238 for more
| info. This is not a bug.«
|
|http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4162112
|
| (If Sun Microsystems, Inc. is free to make such a decision,
| but why don't they document it at the appropriate place, that is, in
| http://download.java.net/jdk7/docs/api/java/lang/System.html?)
|
| It also is broken on Vista:
|
|http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6519127
|
| And also Microsoft®'s VM does it wrong:
|
|http://support.microsoft.com/kb/221206
|
| So we know now that there is a conspiracy among JVM producers
| to make »java.lang.System.getProperty( "user.home" )« unusable.
| (When it can not be used on Windows, it can not be used in a
| portable desktop Java program generally, because this should run
| under /all/ supported platforms.)
|
 
S

Stefan Ram


Another report:

»On every other platform, the "user.home" system property
is a sensible, sane location in which to store documents.
Under Windows, it is not.«

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5045575

The OP was not actually searching for a user home directory,
but for a directory to store application data. In this case it
might not be this disturbing that user.home often is not the
user home directory. But the irregular mapping of user.home to
directories with different semantics under different
platforms, different versions of Windows and different
versions of Java can still be disturbing.
 
O

Owen Jacobson

I did a search for existing specifications and implementations
for this.

After getting no results, I wrote a specification and
implementation myself.

Portadir intends to establich a vocabulary for directories:

http://www.purl.org/stefan_ram/pub/the_portadir_specification

Here is a preliminary publication of the class »FileSystem«
that implements some directories specified in Portadir.

http://www.purl.org/stefan_ram/java/FileSystem.java

The regular publication is expected to be part of the next
release of »ram.jar«.

I had a quick gander at the Portadir source and, while it's good, I
think it's missing some crucial bits. Allow me to elaborate on the
pointless incompatabilities between OSes for a moment.

Under linux, and indeed most unices, the conventions for storing user data are:
1. Preferences in $HOME/.appname or $HOME/.appname/some/subpath.
2. Other data either in the cwd or in $HOME/something.
There are no APIs for locating these places, because the conventions
are so simple you don't actually need one.

Under Mac OS, which is technically a Unix but doesn't act like one,
things are a little more complicated:
1. Preferences stored in the preferences system, which is ultimately
backed by .plist files (specifically) in specific locations, searched
in a particular order. There is an API for setting and getting the
preferences for a given "domain" (read "application"). User-specific
preferences go in
~/Library/Preferences/com.example.your.domain.here.plist, should you
need to manipulate them directly. In Apple's JVM, the default
java.util.Preferences provider is aware of this system.
2. "Documents" - that is, freestanding files users directly care about
and manipulate, under $HOME/Documents[0], with the exact subdirectory
and name up to the user via the save dialog.
3. "Application Data" - that is, files the app needs to maintain in
order to run but that aren't intended for normal manipulation by users
- go in an "Application Data" folder, which is ~/Library/Application
Data/, organized by application name (usually).
There's an added complication in that all of thse names are
localizable; however, there are also APIs for looking up the physical
paths for the various special locations. These APIs are part of the OS
X API, not POSIX or any other standard.

Under Windows, things are pretty much as they are under Mac OS, except
that preferences go in the registry, instead of on the filesystem. Once
again, the default java.util.Preferences provider is aware of this and
does the right thing. There are APIs for looking up the documents and
per-user application data directories, which are part of the Windows
API and are relatively incompatible with their Mac equivalents.

The practical upshot is that the only things you can rely on are:

- The user's home director is at ${user.home} but may not be the right
"default" save location.
- The Preferences API knows where to store settings in a
platform-dependent way.
- The current directory is at ${user.dir} but may not be the right
"default" save location either, for GUI apps. It's reliable for
command-line apps.
- The default Swing L&F probably gets the save dialog right if you
don't specify a directory yourself.

Unfortunately, there's no straightforward way to get either the
Application Data directory or the actual default save location for the
platform. While I'd love to see those in Portadir, I don't imagine it
happening. Writing a portable Java GUI app and getting all of the
various directories correct is pretty much impossible.

I apologize for the disjointed nature of this post; I'm TIRED and I've
been answering phones all day.

-o

[0] Actually, it's worse than that. There are ~/Documents, ~/Music,
~/Movies, ~/Pictures, and ~/Downloads, and application authors are
expected to use the correct one. All of these names are localised, to
boot.
 
O

Owen Jacobson

Another report:

»On every other platform, the "user.home" system property
is a sensible, sane location in which to store documents.
Under Windows, it is not.«

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5045575

The OP was not actually searching for a user home directory,
but for a directory to store application data. In this case it
might not be this disturbing that user.home often is not the
user home directory. But the irregular mapping of user.home to
directories with different semantics under different
platforms, different versions of Windows and different
versions of Java can still be disturbing.

On OS X, user.home is not a sensible place for storing application
data. It's close, and it's probably acceptable for documents, but for
application internal data (for example, a newsreader's downloaded
article cache), both user.home and ${user.home}/.app/something are
irritatingly wrong.

-o
 
J

Jan Thomä

Hi again,

thanks for all your answers and the discussion. It seems there is no
easy way of doing this. A friend of mine pointed me to JS-296, which
has a class LocalStorage which provides exactly the directory I had in
mind. Their code is:

public File getDirectory() {
if (directory == unspecifiedFile) {
directory = null;
String userHome = null;
try {
userHome = System.getProperty("user.home");
}
catch(SecurityException ignore) {
}
if (userHome != null) {
String applicationId = getApplicationId();
OSId osId = getOSId();
if (osId == OSId.WINDOWS) {
File appDataDir = null;
try {
String appDataEV = System.getenv("APPDATA");
if ((appDataEV != null) && (appDataEV.length() > 0)) {
appDataDir = new File(appDataEV);
}
}
catch(SecurityException ignore) {
}
String vendorId = getVendorId();
if ((appDataDir != null) && appDataDir.isDirectory()) {
// ${APPDATA}\{vendorId}\${applicationId}
String path = vendorId + "\\" + applicationId + "\\";
directory = new File(appDataDir, path);
}
else {
// ${userHome}\Application Data\${vendorId}\${applicationId}
String path = "Application Data\\" + vendorId + "\\" + applicationId + "\\";
directory = new File(userHome, path);
}
}
else if (osId == OSId.OSX) {
// ${userHome}/Library/Application Support/${applicationId}
String path = "Library/Application Support/"+applicationId+"/";
directory = new File(userHome, path);
}
else {
// ${userHome}/.${applicationId}/
String path = "."+applicationId+"/";
directory = new File(userHome, path);
}
}
}
return directory;
}

So on Windows, they are using a system environment variable to find out
the localized name of the application data folder. I am not quite sure
if their Mac OS X implementation is correct, as Owen Jacobson pointed
out that Mac OS X directories are localized as well. I cannot confirm
this right now, they don't seem to be localized on my machine, at least
not on the console level. It looks like finder is doing some fancy
things creating localized names for the folders. But when you look on
console level, all folders are in "plain english", so their code might
actually work.

Best regards,
Jan
 
S

Stefan Ram

Jan Thomä said:
String appDataEV = System.getenv("APPDATA");

This will work under Windows 2000/XP/Vista, but not under
Windows 95/98. I have no report about Windows ME, but assume
that it behaves like 95/98 in this regard. I do not have any
idead about Windows NT in this regard.

The Win32-call »GetFolderPath( CSIDL_APPDATA )« (from C or
C++) should work on Windows 98 (possibly, 95) and all later
versions of Windows. (But, of course, is not possible in pure
Java, only in C. Also, it uses shell32.dll and user32.dll,
which are reported to consume a lot of precious »desktop
heap«, a lack of which could lead to application failures.)
 
S

Stefan Ram

Owen Jacobson said:
On OS X, user.home is not a sensible place for storing application
data. It's close, and it's probably acceptable for documents, but for
application internal data (for example, a newsreader's downloaded
article cache), both user.home and ${user.home}/.app/something are
irritatingly wrong.

${user.home}/.app is wrong. But if you feel commited to pure Java,
and can only get 3 or 4 different paths from the environment, it
might still be the choice least worse among those paths.

You can preconfigure this as a default for uneduated users, and
educated users then might change this to a better place during
the installation of the application.
 
J

John B. Matthews

${user.home}/.app is wrong. But if you feel commited to pure Java,
and can only get 3 or 4 different paths from the environment, it
might still be the choice least worse among those paths.

Eclipse, Netbeans and a handful of Java applications I use do this.
You can preconfigure this as a default for uneduated users, and
educated users then might change this to a better place during
the installation of the application.

Out of curiosity, what's the downside?
 
J

Jan Thomä

This will work under Windows 2000/XP/Vista, but not under
Windows 95/98. I have no report about Windows ME, but assume
that it behaves like 95/98 in this regard. I do not have any
idead about Windows NT in this regard.


Well since Windows 95/98/NT have reached EOL already, i don't really
see this as a problem. Java 1.5 does not support Windows 95/NT either,
so this is not an issue in that regard. I'd deem the approach with
APPDATA as good enough for the vast majority of systems out there (and
for my purposes). At least it is definitely not worth going with JNI
for these systems...

Jan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top