APPDATA equivalent on macos and linux?

On windows, using jpackage, I can silently install my app into APPDATA local. I rely on this behavior to set registry settings for my starter scripts for the app and updater, using,

System.getenv("APPDATA")

I do this so as not to have security issues with my app.

These are the locations where the apps are installed on mac and linux.

On Linux/Mac I believe they are talking about,

System.getProperty("user.home");

On windows I was having problems with the install due to security issues that do not apply to APPDATA. Was getting permission denied errors if I remember correctly.

Can I expect to run into similar issues on mac/linux using user.home?

The document you linked only shows the internal structure of the apps as generated by jpackage. This will end up being placed somewhere appropriate in the user’s system.

What error were you getting? I’ve been one of the primary maintainers of a java application that relies on user.home property cross platform, and we’ve never had a report of a security error.


For more background:

Each platform has it’s own set of conventions for keeping application data:

  • Linux typically uses ${user.home}/.<app-name> (Note the leading dot to place a hidden directory)
  • Windows may use ${user.home}/AppData/. The full path for this should be what you get when you pull the APPDATA property.
  • Also on Windows, some programs use the same pattern as on linux. Including: gimp, virtualbox, ssh, oracle’s system-installed JRE, (Usage stats) & the JME SDK.
  • OSX prefers ~/Library/Application Support/<appname> (more specifically, the AppID defined in your Info.plist)
    • This path should be available under the system property NSApplicationSupportDirectory, similar to how you are pulling APPDATA on windows. See this Apple Document.
  • Of course, the linux-style hidden folder also work on mac.

The SDK seems to use the dot-folder approach, because it mostly just works on every platform.

Be aware that any of these locations are vulnerable to being deleted by the user, or another app. You should not be relying on them to be populated by your installer. Instead, make sure the app has a ‘bootstrap’ mode that can backfill the defaults if your configuration files are not present.

Right, From the docs.

The installation directory is platform-specific:

  • On Linux, the default is /opt/application-name
  • On macOS, the default is /Applications/application-name
  • On Windows, the default is c:\Program Files\application-name; if the --win-per-user-install option is used, the default is C:\Users\user-name\AppData\Local\application-name

These locations are not configurable when using jpackage , just subfolders of what I assume is user.home.

I know nothing about linux and mac os variables. On windows its
“ProgramFiles” by default, so I assume that user.home = /opt on linux and user.home = /Applications on mac?

If my memory is correct, using url prefix of file:/// on windows was getting permission denied from the
“ProgramFiles” directory.

APPDATA, no problems.

I don’t see how that’s a problem. Kinda confused by this since the user cant install it to a different location as jpackage is in control of the install. If what should be there isn’t, things should not work.

I don’t use defaults but instead throw exceptions. If I don’t find what I am expecting, then it should not work period is how I look at it. At most, an error message telling them to contact support should be displayed is what I thought made sense. As long as the user leaves the program alone, no problem. If they meddle with it, that’s on them.

I dont understand why you would want defaults if somethings not as you expect it to be but I have not ran this with users yet so you may be right.

No, with the exception of the --win-per-user-install, these are not subfolders of user.home

These installation locations are Absolute paths for system-wide applications.

user.home is the individual’s home directory:

  • /home/<username> on Linux
  • C:\Users\<username> on Windows
  • /Users/<username> on Mac

If you’re trying to open a file in java, the pathname is just a string without a prefix. Exactly as if you type it on the command line, eg. C:\My\Crazy\Directory

Your mentioning the user.home stuff made me believe that you were trying to manage per-user configurations/preferences/etc.

If this is actually per-application, you’d need to approach it a little differently. Some questions so that we aren’t talking past each other:

  • Is this data read from inside the application?
  • Is this data read from outside the application, such as by a launcher?
  • Is this data written from inside the application?
  • Is this data written from outside the application, such as by a launcher?

I am using the updater to grab the file as part of the update process.

URLConnection downloadFileConnection = new URI(downloadUrl).toURL().openConnection();

Keeps everything in a small method.

The updater takes arguments.

  • Update server url
  • Version file to check for on update server
  • The dir where the version file lives on update server
  • Current version file name locally
  • The fully qualified path to the start script of calling app, based on os
  • The fully qualified path to the parent dir of the updater, based on os

The calling app sets them from enums in a UpdateCheck class.
The class that runs the check is in the client.

The updater:
The updater is not on the class path of the calling app.
The updater lives in a subfolder of the calling app.
Its only function is to look where you tell it and download what you tell it to get.

The updater has all its info in enums:

  • Where to save a download app file to.
  • Where to save a new updater file to.
  • The name of its version file.
  • The name of its startup script, based on os

The download server holds the exact same directory structure as is used for the calling app and updater app. Version files are created as part of the distZip build or jpackage build. I just unzip the updater distribution on the update server or upload the app image folder to the update server after jpackage is ran.

When client starts, it looks to see if any new updater files have been downloaded first, if so, moves them into the appropriate folders as is dictated by the version files. Then runs the updater. If nothing new, just runs the updater.

When updater starts, it first checks for a new updater, if found downloads it, exits and starts calling app. No new updater, it checks for new calling app. If found downloads, installs it and starts calling app. If nothing new for either, starts calling app with argument notifying the calling app nothing new has downloaded.

When calling app recieves notifcation there are no new updates, it skips updating.

If there are any problems, the app stops or the updater stops, breaking the cycle.

On windows, I use

environment variable to get the APPDATA dir.

This works fine for windows as I am familar with it but I am unsure of the proper way to get the linux /opt dir or the mac /Applications directory and once I do get them whether I will hit the same problem of grabbing a file.

Doing anything other than reading from the program files dir requires elevated privileges. I.e. run as administrator.

The general rule is to install it in program files and store the configuration elsewhere such as the user home dir.

If the updater is in the program files dir, it can access the user.home and download things to there but can things in user.home move files around inside program files?

Edit: I see it cant, as your reply says.

So I will hit the same problem in mac and linux. jpackage wont let you use user.home on linux or mac, just windows for some reason.

So I have a conundrum.

I cant let user determine where to install because if they install in program files it would break updater.

I cant allow user to just unzip the file anywhere because they may unzip it somewhere that breaks updater.

jpackage completely fixes the problem on windows by allowing access APPDATA but Im totaly screwed for linux and mac.

You can check the operating system and location because it’s static. Windows + Program Files = elevated privileges. You’ll have to branch that logic in the installer/updater.

Edit: You can also check it in the installer and warn the user that elevated privileges are required if they install it in Program FIles.

Is there a way to get and keep elevated privlages?

Edit: I see by web search there is some things. I will check it out. Already see apple is a prick about it.

Well, the flag for windows makes it pretty obvious: If you want to make system wide changes (i.e. installing/updating), you need system administrator privilegs.

For Linux, I guess you can only error out and request to be run with sudo privilegs (there also is a suid bit or something, though), for windows it is something about the app manifest, so nothing a jar can really do.

Is it uncommon to install apps in user.home on linux/mac?

Steam installs there, so I’d say no. There is a possibility that the user home is mounted with noexec flag, but i would say that it is generally safe to install there…

Not uncommon at all.

Especially on Linux, you do not want to force installation in one of the system folders. There are many flavors of Linux, and they each have different policies on how third-party software is installed. It’s one thing if you are supplying distro specific packaging, such as a .deb or .rpm or .pkg, but, for generic installs, they should either be Unzip-in-place, or at most, apply file associations, etc.

Mac actually usually puts things in /Users/<name>/Applications

I’m starting to think that this tool has issues… eek.

So, to recap:

  • Main application needs to write a configuration file that the updater uses to do a proper update

I’m also noting that if there is a new updater program/class, you’re making two full round trips, launching App>updater>App>updater>App before its up and running? But leave that aside.

Good news: Since your configuration file is a core part of the app system, and you know where the App and updater are in relation to each other, you don’t have to worry about managing all the system-specific stuff for this one file.

What you need is to teach the application how to find it’s own exact location at runtime.

The following was originally posted by someone named David Smiley on a mailing list in 2002:

    URL url = <yourApp>.class.getResource("/path/to/<yourApp>.class");
    if (url.toString().startsWith("jar:"))
      {
        String furl = url.getFile();
        furl = furl.substring(0, furl.indexOf('!'));
        dir = new File(new URL(furl).getFile()).getParent();
        if (!new File(dir).exists())
          return <error>;
      }

You probably want to put it in a try block, but what It’ll do is give you the Absolute Path to the folder that your app.jar is in. You can then write your update config to whatever location relative to that that you need. (Probably in the subfolder where the updater lives)

And if the attempt to write the update config fails due to access conrol, just pop an error: “The update process requires Administrator level access. Please re-run the application with admin privileges.”

After more study, I will be re-writing the app to use a different approach incorporating the suggestions made here.

I will get back after the re-write.

2 Likes

I just confirmed with another mac user that this constant does not seem to be readable from inside java. Will probably need to hand-code the path relative to user.home

Sorry if this point caused any frustration.

According to the docs on oracle site for jpackage, you can pass the absolute path using the install-dist argument.

I dont see how you can do this if the path must be known in advance so you can pass it to jpackage.

Without variables, how do you even know what drive is to be used.

I just tested using the tilde for windows home path and it installed the app under the tilde (~) in program files.

When using the %USERPROFILE% variable it thows a warning,

Install directory should be a relative sub-path under the default installation location such as "Program Files".

This means jpackage is worthless as an installer.

Edit: It will use the project name as default if you pass a hard coded absolute path.

You can let the user choose where to install the app but you as the app designer cant determine where YOU want it installed.

You should be allowed to install in user.home always. I don’t get the logic on this.