Universal access to the AppData folder in Java
In this short tutorial, we're going to describe a cross-platform solution to
access the application data folder. On Windows, this folder is known as
%appdata%
-
C:\Users\<Account>\AppData\Roaming\<AppAuthor>\<AppName>
,
on Linux it's /home/<account>/.local/share/<AppName>
and in MacOS, the path should be approximately
/Users/<Account>/Library/Application Support/<AppName>
.
Because of the diversity of these paths, there's no native solution in Java that
would request access to these folders easily.
Dependencies
If we want to do things easily, we have to reach for a third-party library. Certainly there are more, but I'll describe just one I have experience with. It's the appdirs project on GitHub. To manage dependencies, we'll use Gradle, but for Maven the procedure would be very similar.
New project
I'll create the project in IntelliJ Idea. Let's start the IDE and click on the Create New Project button:
In the menu, we'll choose we want a Java project managed by Gradle:
In the first field, we'll enter the name of the organization or other identifier. The second field is for the application name:
In the Gradle settings, we'll check the AutoImport checkbox to avoid synchronizing the project manually:
Finally, we'll set the path to the project:
Finding the repository
Once we have successfully set up the project, we'll go to the Maven repository page, enter the name of
the library: appdirs
in the search box and choose Search. We'll
choose the first option, the library in the net.harawata.appdirs
package, and select the latest version:
On the next page, we'll switch to the Gradle tab and copy the dependency that we'll add to the dependencies list in Gradle:
For those who don't want to go to the website, I attach the dependency below, you can just copy it:
// https://mvnrepository.com/artifact/net.harawata/appdirs compile group: 'net.harawata', name: 'appdirs', version: '1.0.3'
Building
Open the buid.gradle
file and paste the copied line from the
website to the dependency list at the end of the file:
In a few moments, the project should update automatically and download the necessary dependencies. A total of 4 libraries will be downloaded:
- net.harawata:appdirs:1.0.3
- net.java.dev.jna:jna-platform:4.5.2
- net.java.dev.jna:jna:4.5.2
- org.slf4j:slf4j-api:1.7.25
The first is the library we requested. This library has two dependencies:
jna
and slf4j
. The first one is used to access system
function calls. The second one is used for logging.
Exploring the library
Before we start programming, it'd be nice to get to know the library first. We'll see what classes/interfaces the library offers and what we can do with them.
AppDirs
The AppDirs
class is the main communication interface we use to
access the folders. The class contains the following methods for accessing the
folders:
getUserDataDir()
- Returns the system folder for the application datagetUserConfigDir()
- The folder for application configuration filesgetUserCacheDir()
- The application cache foldergetUserLogDir()
- The application log folder
To create an instance, we need the AppDirs.getInstance()
factory
method, which has its own logic to decide how to create the instance. First of
all, it looks which OS the program is running on and selects one of these three
implementations accordingly:
WindowsAppDirs
MacOSXAppDirs
UnixAppDirs
Since it's a GitHub project, we can see how this logic works:
public static AppDirs getInstance() { String os = System.getProperty("os.name").toLowerCase(); if (os.startsWith("mac os x")) { logger.debug("os.name {} is resolved to Mac OS X", os); return new MacOSXAppDirs(); } else if (os.startsWith("windows")) { logger.debug("os.name {} is resolved to Windows", os); WindowsFolderResolver folderResolver = new ShellFolderResolver(); return new WindowsAppDirs(folderResolver); } else { // Assume other *nix. logger.debug("os.name {} is resolved to *nix", os); return new UnixAppDirs(); } }
Based on the os.name
system property the code determines whether
it's MacOS, Windows, or any Linux. True, now you may be wondering why to use the
library when you can write all this yourself. The point is there are directory
separator issues and other things already resolved by the library. For example,
to get the path to the %appdata%
folder on Windows, we must use
native method calls. The library will take care of all this for us.
Testing the library
Now let's create a simple console application where we'll create an
AppDirs
class instance to get to the various application
folders.
import net.harawata.appdirs.AppDirs; import net.harawata.appdirs.AppDirsFactory; public class App { private static final String CREDENTIALS_APP_NAME = "testApplication"; private static final String CREDENTIALS_AUTHOR = "ict.social"; private static final String CREDENTIALS_VERSION = "v1.0"; public static void main(String[] args) { final AppDirs appDirs = AppDirsFactory.getInstance(); final String userDataDir = appDirs.getUserDataDir(CREDENTIALS_APP_NAME, CREDENTIALS_VERSION, CREDENTIALS_AUTHOR); final String userConfigDir = appDirs.getUserConfigDir(CREDENTIALS_APP_NAME, CREDENTIALS_VERSION, CREDENTIALS_AUTHOR); final String userCacheDir = appDirs.getUserCacheDir(CREDENTIALS_APP_NAME, CREDENTIALS_VERSION, CREDENTIALS_AUTHOR); System.out.printf("UserDataDir: %s%n", userDataDir); System.out.printf("UserConfigDir: %s%n", userConfigDir); System.out.printf("UserCacheDir: %s%n", userCacheDir); } }
The result of the application on Linux looks like this:
Console application
UserDataDir: /home/peter/.local/share/testApplication/v1.0
UserConfigDir: /home/peter/.config/testApplication/v1.0
UserCacheDir: /home/peter/.cache/testApplication/v1.0
That would be all. I hope I saved you long hours of researching how to access the application data folder universally on all operating systems.