Find Place for Dedicated Application Folder

Find place for dedicated application folder

There should be, but there isn't. I even submitted a bug/RFE about it, but as far as I know, it was never accepted. Here's what I use:

public class ApplicationDirectories {
private static final Logger logger =
Logger.getLogger(ApplicationDirectories.class.getName());

private static final Path config;

private static final Path data;

private static final Path cache;

static {
String os = System.getProperty("os.name");
String home = System.getProperty("user.home");

if (os.contains("Mac")) {
config = Paths.get(home, "Library", "Application Support");
data = config;
cache = config;
} else if (os.contains("Windows")) {
String version = System.getProperty("os.version");
if (version.startsWith("5.")) {
config = getFromEnv("APPDATA", false,
Paths.get(home, "Application Data"));
data = config;
cache = Paths.get(home, "Local Settings", "Application Data");
} else {
config = getFromEnv("APPDATA", false,
Paths.get(home, "AppData", "Roaming"));
data = config;
cache = getFromEnv("LOCALAPPDATA", false,
Paths.get(home, "AppData", "Local"));
}
} else {
config = getFromEnv("XDG_CONFIG_HOME", true,
Paths.get(home, ".config"));
data = getFromEnv("XDG_DATA_HOME", true,
Paths.get(home, ".local", "share"));
cache = getFromEnv("XDG_CACHE_HOME", true,
Paths.get(home, ".cache"));
}
}

/** Prevents instantiation. */
private ApplicationDirectories() {
}

/**
* Retrieves a path from an environment variable, substituting a default
* if the value is absent or invalid.
*
* @param envVar name of environment variable to read
* @param mustBeAbsolute whether enviroment variable's value should be
* considered invalid if it's not an absolute path
* @param defaultPath default to use if environment variable is absent
* or invalid
*
* @return environment variable's value as a {@code Path},
* or {@code defaultPath}
*/
private static Path getFromEnv(String envVar,
boolean mustBeAbsolute,
Path defaultPath) {
Path dir;
String envDir = System.getenv(envVar);
if (envDir == null || envDir.isEmpty()) {
dir = defaultPath;
logger.log(Level.CONFIG,
envVar + " not defined in environment"
+ ", falling back on \"{0}\"", dir);
} else {
dir = Paths.get(envDir);
if (mustBeAbsolute && !dir.isAbsolute()) {
dir = defaultPath;
logger.log(Level.CONFIG,
envVar + " is not an absolute path"
+ ", falling back on \"{0}\"", dir);
}
}
return dir;
}

/**
* Returns directory where the native system expects an application
* to store configuration files for the current user. No attempt is made
* to create the directory, and no checks are done to see if it exists.
*
* @param appName name of application
*/
public static Path configDir(String appName)
{
return config.resolve(appName);
}

/**
* Returns directory where the native system expects an application
* to store implicit data files for the current user. No attempt is made
* to create the directory, and no checks are done to see if it exists.
*
* @param appName name of application
*/
public static Path dataDir(String appName)
{
return data.resolve(appName);
}

/**
* Returns directory where the native system expects an application
* to store cached data for the current user. No attempt is made
* to create the directory, and no checks are done to see if it exists.
*
* @param appName name of application
*/
public static Path cacheDir(String appName)
{
return cache.resolve(appName);
}
}

Some notes:

I'm not sure the code for older Windows versions is even necessary, as Java 8 doesn't run on Windows XP.

The XDG Directory Specification says “All paths set in these environment variables must be absolute. If an implementation encounters a relative path in any of these variables it should consider the path invalid and ignore it.”

What is the cross-platform way of obtaining the path to the local application data directory?

You could probably say something like (contradict me if I am wrong, or if this a bad approach)

private String workingDirectory;
//here, we assign the name of the OS, according to Java, to a variable...
private String OS = (System.getProperty("os.name")).toUpperCase();
//to determine what the workingDirectory is.
//if it is some version of Windows
if (OS.contains("WIN"))
{
//it is simply the location of the "AppData" folder
workingDirectory = System.getenv("AppData");
}
//Otherwise, we assume Linux or Mac
else
{
//in either case, we would start in the user's home directory
workingDirectory = System.getProperty("user.home");
//if we are on a Mac, we are not done, we look for "Application Support"
workingDirectory += "/Library/Application Support";
}
//we are now free to set the workingDirectory to the subdirectory that is our
//folder.

Note that, in this code, I am taking full advantage that Java treats '/' the same as '\\' when dealing with directories. Windows uses '\\' as pathSeparator, but it is happy with '/', too. (At least Windows 7 is.) It is also case-insensitive on it's environment variables; we could have just as easily said workingDirectory = System.getenv("APPDATA"); and it would have worked just as well.

Find directory for application data on linux and macintosh

this should work. just one thing: on linux it is preferred to keep settings in a hidden folder in user directory. So for linux, either put your folder under $HOME/.config, or start the name with the . to make it hidden.

Simple Java Desktop App: Config files and where to put them

To package an application means putting the classes in a .jar file, which is a zip file with one or more special Java-specific entries in it. (There are many Stack Overflow answers describing how to do this, such as Java: export to an .jar file in eclipse . Feel free to search for more.)

The problem is, an application cannot write to its own .jar file. On Windows the file is likely to be locked and unwritable; on other systems, it may be possible but is a bad idea for many reasons. You should always consider a .jar file read-only.

Since the configuration needs to be writable, it cannot live in your .jar file, but your .jar file can (and should) include your config.json as a default configuration. The actual configuration should be a file somewhere under the user’s home directory; if it doesn’t exist, your program should copy your default configuration to that location to allow future modification. Alternatively, your program could just ignore missing configuration and default to reading your internally packaged default configuration, but in that case, you’d want to provide thorough documentation of the writable configuration file’s format for end users, since they won’t have an example to go by.

Files packaged inside a .jar file are called resources and are read using the Class.getResource or Class.getResourceAsStream method. You cannot read them using File or Path objects, because they are parts of a zip archive, not actual files. Copying it to a writable location typically looks like this:

Path userConfigFile = /* ... */;

try (InputStream defaultConfig = getClass().getResourceAsStream("/config/config.json")) {
Files.copy(defaultConfig, userConfigFile);
}

For more information on reading resources, you may want to read about Access to Resources.

On Windows, the actual location of a configuration file is usually in a subdirectory of the user’s AppData directory:

Path configParent;

String appData = System.getEnv("APPDATA");
if (appData != null) {
configParent = Paths.get(appData);
} else {
configParent = Paths.get(
System.getProperty("user.home"), "AppData", "Local");
}

Path configDir = configParent.resolve(applicationName);
Files.createDirectories(configDir);

Path userConfigFile = configDir.resolve("config.json");

If you’re interested in discussion of where to place user configuration files on other platforms, see this question and this one.

Deploy a writable properties file in jpackaged Java application

Package default settings in your application. At runtime, store a writable copy of them in a known file location, typically in a directory that is a descendant of the user’s home directory:

String applicationName = /* ... */;

Properties configuration;

String os = System.getProperty("os.name");
String home = System.getProperty("user.home");

Path localSettingsDir;
if (os.contains("Mac")) {
localSettingsDir = Path.of(home, "Library", "Application Support");
} else if (os.contains("Windows")) {
String appData = System.getenv("APPDATA");
if (appData != null) {
localSettingsDir = Path.of(appData);
} else {
localSettingsDir = Path.of(home, "AppData", "Roaming");
}
} else {
String configHome = System.getenv("XDG_CONFIG_HOME");
if (configHome != null) {
localSettingsDir = Path.of(configHome);
if (!localSettingsDir.isAbsolute()) {
localSettingsDir = Path.of(home, ".config");
}
} else {
localSettingsDir = Path.of(home, ".config");
}
}

Path localSettings = localSettingsDir.resolve(
Path.of(applicationName, "config.properties"));

configuration = new Properties();
if (!Files.exists(localSettings)) {
Files.createDirectories(localSettings.getParent());
try (InputStream defaultSettings =
MyApplication.class.getResourceAsStream("config.properties")) {

Files.copy(defaultSettings, localSettings);
}
}

try (Reader settingsSource = Files.newBufferedReader(localSettings)) {
configuration.load(settingsSource);
}

For more details on determining a system’s application configuration directory, see Find place for dedicated application folder.

Application packaged with Jlink cannot find properties file

It has never been correct to assume classes come from a .jar file which is a physical file in the platform’s file system. (Also, getCodeSource() can return null).

Windows, OS X, and Linux all have standard locations where user configuration files are stored. See Find place for dedicated application folder. To oversimplify the information from that link:

  • Windows: System.getProperty("user.home") + "\\AppData\\Roaming\\CryoStats"
  • OS X: System.getProperty("user.home") + "/Library/Application Support/CryoStats"
  • Linux: System.getProperty("user.home") + "/.config/CryoStats"

(Don’t just use those paths directly; it’s actually more complicated than this. Read the link for the full explanation.)

Never use System.getProperty("user.dir")—that’s the current directory, and it could be anything, including the system’s root directory. On the other hand, the user.home system property points to a stable location, namely the user’s home directory.

What I do for application configuration is: I include the default configuration inside the program, as a read-only resource. When the program exits, it saves that configuration, possibly modified by the user, to the system’s standard configuration location.

Where should cross-platform apps keep their data?

Each platform has its own API for finding the user's home folder, or documents folder, or preferences folder.

  • Windows: SHGetFolderPath() or SHGetKnownFolderPath()
  • Mac OS X and iPhone OS: NSSearchPathForDirectoriesInDomains()
  • Unix: $HOME environment variable

Don't hardcode specific paths or just tack a prefix and suffix on the user's name. Also, try to follow whatever conventions there are for the platform for naming the files.



Related Topics



Leave a reply



Submit