Using Xdg Directory Specification on Java Application

Using XDG directory specification on Java application

On Gnome there is java-gnome support library, it is doing native calls for better integration with gnome. It can get path of user dirs via calls to Glib.getUserSpecialDir. Haven't tried this one myself, might be worth a try if you are planning to use more desktop-integrated features in your program.

Should A Program Create XDG Folders?

I've settled on my own answer after a few years of thinking:

  1. never create non-standard base XDG directories, but
  2. it may be okay to automatically create the standard XDG base directories, and
  3. you should automatically create any of your application's subdirectories within the base directories.

I think it can be good to be automatically helpful, but it is also very important to not worsen a user's mistakes.

If I write XDG_DATA_HOME=~/.locals/hare in my environment variable configuration, I might have wanted that, but it's much more likely that I made a typo of ~/.local/share. So the most helpful, least disruptive, and least wrong thing to do in that case is to report the lack of the requested XDG base directory.

So, if the user has specified a custom XDG base directory, and that base directory does not exist, never try to create it. Don't put your user in a situation where for example next to their standard ~/.config directory they get a ~/.configs or ~/.comfig directory which also contains some of their configurations, until one day they fix the typo and suddenly their programs behave as if they were reset to the defaults. This is a situation where early detection of mistakes is the most helpful thing to do in the long run, so tell the user immediately "this doesn't exist".

But if the user has not asked for a custom base directory, and you're about to use the known, standard location, then it's fine to try to automatically create it, if you ensure you only create it with reasonable ownership, permissions, and so on.

Finally, when the base XDG directory exists, most apps should probably make their own subdirectory inside that, and you definitely should create that app-specific directory, and any other subdirectories inside that one, automatically.

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.

General Path To Downloads Folder

Check out this question.
Use...

String home = System.getProperty("user.home");
File file = new File(home+"/Downloads/" + fileName + ".txt");

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.



Related Topics



Leave a reply



Submit