Everything you’ll wish you didn’t know about disabling Java 7 updates

JavaCupLogo-161Oracle’s Java 7 JRE for OS X was first officially released in October 2012. As expected, there have been issues deploying and testing it, amidst confusion about Apple’s Java 6 updates and it disabling symlinks to the web plugin, the pre-emptive disabling of Java with XProtect, and more.

And of course, the first thing administrators need to verify is that deployed software won’t periodically nag the user to install an update that they don’t have sufficient rights to install, or that they shouldn’t install for other reasons. I’ll cover a few ideas in this post specifically about the updater mechanisms and approaches to disabling it, and focus on other specific issues with this package in future posts.

deployment.properties a.k.a. com.oracle.javadeployment a.k.a. com.oracle.java.JavaAppletPlugin a.k.a. com.oracle.java.Java-Updater a.k.a. com.oracle.java.Helper-Tool

There is a place to disable update checks in the Java Control Panel:

Look! It unchecked the checkbox!

Look! It unchecked the checkbox!

Java uses what’s known as a “Java properties” format to store preferences, that looks similar to a .ini file. Many user preferences seem to touch a file at /Library/Application Support/Oracle/Java/Deployment/deployment.properties. This file’s options are somewhat documented.

In more recent versions, some settings (perhaps those known to have OS X-specific implementations, like the boolean deployment.macosx.check.update) seem to be stored instead in the defaults domain com.oracle.javadeployment in a Java-style namespace:


<key>/com/oracle/javadeployment/</key>
<dict>
    <key>deployment.macosx.check.update</key>
    <string>false</string>
    <key>deployment.modified.timestamp</key>
    <string>1361734949180</string>
    <key>deployment.version</key>
    <string>7.0</string>
</dict>

It doesn’t actually matter for now where the deployment.macosx.check.update setting is actually stored, because it has no effect on whether or not Java 7 will check for updates.

Update 13/03/15: It turns out that this does have an effect, it controls the plugin’s own built-in update checker, that I managed to never see while scrutinizing the Sparkle-based updater. So while it suppresses the nag when the plugin is actually invoked, it doesn’t control the background update check mechanism that’s detailed below and in a related post.

What? Oh.

What? Oh.

There’ve been a few discussion threads about configuring the update setting, but all these attempts seem to do is tell the Control Panel about the state of the checkbox, and not suppress the active checking (and prompting) for updates.

The Java-Updater/Helper-Tool Yin-Yang of Doom

Briefly, here are the basic mechanisms of the update-checking system as it is now (here’s the launchd manpage for reference):

  • There is a LaunchDaemon (com.oracle.java.Helper-Tool.plist) and a LaunchAgent (com.oracle.java.Java-Updater.plist) installed in the usual place within /Library.
  • They are actually symlinks to the web plugin’s installation area, in /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Resources.
  • The LaunchDaemon is not run on a schedule, but rather triggered by changes to the LaunchAgent plist file using a WatchPaths entry.
  • The LaunchDaemon runs Helper-Tool, a Bash script whose purpose is to enforce (based on default times set by an install script) or randomize the StartCalendarInterval key in the LaunchAgent plist, and reload the LaunchAgent. It modifies the file that triggers itself to run… to modify the file… that triggers it to run… you can probably see why this may not be a great idea.
  • The LaunchAgent runs the Java Updater binary located inside a .app bundle in the aforementioned Resources directory.
  • Java Updater.app is essentially a vessel for the Sparkle updater framework, which simply manually calls Sparkle’s “check in background” function. It is a pure Cocoa app. It does not seem to have any link to the Java plugin, settings configurable via the control panel, etc. It is not linked to any Java library.

There are a several major issues with this LaunchAgent/Daemon combo that I’ll cover in a future post.

For now, there are a couple possible workarounds I’d consider that are the least intrusive and most reliable way of suppressing the update check behavior. They each still have issues that are very important to understand if you want to deploy this and not dig yourself out of a hole later. I don’t consider either of them acceptable in the long term, and can’t recommend one over the other – it all depends on your environment.

Neuter Sparkle

One approach is to leverage the configurability of Sparkle. Because Java Updater is directly invoking Sparkle’s check method, overriding check behavior preference keys as outlined here won’t help, as far as I can tell. If you can at least read some Objective-C code you can look at Sparkle’s SUUpdater.m and see for yourself what it does, and make a pretty good guess at what methods Java Updater is calling by inspecting its binary strings (hint: the resetUpdateCycle and checkForUpdatesInBackground methods).

Sparkle supports overriding some preference keys that would typically be defined in a bundle’s Info.plist file, by setting them in the bundle’s user defaults domain instead (at either the user or system level). In this case, we’re looking at the com.oracle.java.JavaAppletPlugin domain, and the SUFeedURL string key. (It might be worth pointing out now that the Java 7 package uses about 4 different preference domains for the plugin and helpers, which doesn’t make figuring these out any easier).

Setting SUFeedURL to an invalid URL like “nil” (or one that doesn’t actually contain a Sparkle appcast feed) will cause Sparkle to fail silently. It’s worth noting, since this is being run as a LaunchAgent, that this doesn’t cause the actual Java Updater app to fail with a non-zero exit code. As far as Java Updater is concerned, it’s done its job and Sparkle just didn’t have anything for it to do.

There are alternate, more desperate Sparkle-related tweaks possible that will yield similar results to the above, which will be in a future post.

Advantages: This preference key can be managed like any other: a defaults command writing the value to /Library/Preferences/com.oracle.java.JavaAppletPlugin, MCX, or a Configuration Profile. The preference can later be removed/changed if desired, and it’s completely independent of the plugin installation.

Disadvantages: This overrides the update URL for anywhere the plugin may want to check for updates, meaning that if one checks the Control Panel manually for an update, it will not be able to check or verify whether it is up to date. It will state that it was last run whenever Java Updater was last run, but that it is “Unable to check for updates”, and to “Please check your internet connection and try again.” You’re essentially removing the ability for the plugin to update itself via its built-in mechanisms, even the user- or support-initiated ones. This puts it roughly on par with the state of deploying Adobe Flash.

Remove (but don’t disable) the LaunchAgent

A second approach is to prevent the LaunchAgent from ever running Java Updater in the first place.

After installing Java, unload the job temporarily, and remove the symlinks so that it’s not loaded again after a restart. You’d run something like this with elevated privileges:

/bin/launchctl unload /Library/LaunchAgents/com.oracle.java.Java-Updater.plist
/bin/launchctl unload /Library/LaunchDaemons/com.oracle.java.Java-Helper.plist
/bin/rm -f /Library/LaunchAgents/com.oracle.java.Java-Updater.plist
/bin/rm -f /Library/LaunchDaemons/com.oracle.java.Java-Helper.plist

You might think, like I did, that you can just launchctl unload -w the job to set it as permanently disabled. It turns out you absolutely should not do this, due to an oversight in the Java 7 installer’s postinstall script. Having these jobs disabled will cause the script, and thus the entire install, to fail.

Advantages: We don’t need to mess with any configuration of the updater mechanism itself.

Disadvantages: We’re still changing the “expected state” of the Java installation, and the fact that disabling the job permanently (ie. launchctl unload -w) causes future installations to fail doesn’t inspire confidence that any of Oracle’s future pre/postinstall scripts will be robust enough or perform even basic sanity checks. We also need to make sure that we unload the job and remove symlinks after every installation. Unloading is actually optional, since that the next update check will usually be a week from the time of installation, and a reboot will cause the launchd jobs to never be loaded again.

Current status

I don’t think either option is generally viable, especially if you’re in environment with a diverse set of client configurations (laptops, admin users, remote workers and sites, etc.) and need to make as few assumptions as possible about how client machines are used and maintained. If, on the other hand, you maintain control over at least software installations and updates, you might decide one of these two workarounds could work despite its limitations, at least until a better solution is discovered or implemented.

After I complained in ##osx-server on IRC, Michael Lynn found a channel through which to report bugs against the JRE, and Rich Trouton documented the steps and caveats in a blog post. If you support Java on your Macs and this is an issue for you, file a bug!

Tagged , , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

13 Comments

  1. FTBZ
    Posted February 25, 2013 at 1:05 am | Permalink

    Why not editing the file deployment.properties directly in each user’s folder ? This is what we’re doing here.

    • Posted February 25, 2013 at 7:34 am | Permalink

      As I mentioned in the post and the various linked threads, this doesn’t actually prevent Java Updater from launching.

      The default StartCalendarInterval for The LaunchAgent is once weekly, so it’s possible to go a while without seeing the issue, unless you modify the LaunchAgent to run more frequently.

      • FTBZ
        Posted February 25, 2013 at 7:44 am | Permalink

        From the start, we have disabled the update by this way and no one of our 400 users has seen an updater message. And I’m using this on my own workstation and I’ve never been annoyed by the Java updater. Perhaps we’re very lucky or something is different in our workstations. I’m doing something wrong ?

        • Posted February 25, 2013 at 8:05 am | Permalink

          How rapidly have you been pushing out new releases? Also, if a user is logged out or the machine is off when the StartCalendarInterval time arrives, the prompt won’t appear. I believe this time is based on the actual time of the Java installation, so this might tell you when users are likely to see a popup.

          Would all of your 400 users tell you if they saw the prompt and just told it to “Skip this version”?

          • FTBZ
            Posted February 25, 2013 at 8:15 am | Permalink

            Sending the package by Absolute Manage on all clients and a script that edits the deployment.properties in each user folder. Our users are really assisted, so every time that a popup appears you will be sure that the helpdesk gets calls. But perhaps no one has called the helpdesk at this time.

            When Apple has disabled Flash the last time, we got more than 10 calls about the missing plug-in only the Monday morning.

            With your information I will check in depth the problem, to be sure my users are not annoyed.

          • Posted February 25, 2013 at 9:53 am | Permalink

            At install time, the postinstall script effectively sets the StartCalendarInterval to be the current hour and minute, with a random day of the week.

            It’s always possible that if the hour/minute is consistent with a time that a user is usually not logged in or the machine is off.

  2. xendo
    Posted February 27, 2013 at 10:13 am | Permalink

    Against my recommendations and wishes, “the business” decided that all our users should have admin access. No, I don’t like it either. It’s a bad, stupid decision. But it’s out of my hands.

    The only version of Java we deploy is the one we know works. However, some users have decided they just *have* to have Java 7 – obviously for no good reason other than they want it, because it’s new – and have installed it using their poorly justified admin privs. For them, I’m taking the exact opposite approach. Anyone who has Java 7 installed, based on an inventory of the installer receipts, gets the latest version shoved down their throats by our software management system. Basically, they’ve also ready jumped off that bridge; if they simply *must* have Java 7 installed, then I’m insisting they have the least vulnerable version.

  3. Brad
    Posted February 27, 2013 at 4:25 pm | Permalink

    WTH Oracle! Thanks for your hard work Tim! I think I am going to stick with the deployment.properties file in /Library… I’m not really down for the two options you have proposed here. If I understand your post correctly, there is still a possibility that it will randomly ask to update due to the time stamp set by the initial installer?

    • Posted February 28, 2013 at 3:32 pm | Permalink

      It will prompt for an update if a user is logged in at the time of the StartCalendarInterval in the LaunchAgent, and yes, this date is set at the time of the installation. But the Helper-Tool will also fix up these times if they change, and possibly randomize the day.

      Again, these are my observations only. Nobody else has confirmed that this is broken, but neither has anyone described why he believes it “seems to be working” for him.

  4. Andrew
    Posted March 3, 2013 at 1:58 am | Permalink

    I’ve created an updated version of the original script that turned off the “Check for Updates” preference. It now removes the Launch Agent and adds additional keys to the deployment.properties file that, in my testing, suppresses the prompt you get when opening a Java app using an older version of Java. This script will have to be run after each version of Java is deployed.

    Updated/Modified script is in this thread: https://jamfnation.jamfsoftware.com/discussion.html?id=6489

    Take a look and see if it works for you… any feedback is appreciated.

  5. Posted March 4, 2013 at 7:32 pm | Permalink

    Thanks Andrew – so that clears things up a bit. The properties file settings are effective, but only when the Java applet loader is checking for updates. It would seem that there’s still no supported mechanism to disable the Sparkle-based update checker.

    This explains why some people have reported the changes seeming to work – they weren’t referring to the same issue, and disabling the checker within the plugin itself _does_ seem to work.

    One of the things preventing others from easily reproducing the issue that’s detailed in this post is the tricks done by the Helper-Tool script to modify the same LaunchAgent plist one is trying to test.

  6. Posted July 2, 2013 at 8:25 pm | Permalink

    Try this with sudo or as root:

    /bin/mkdir -pv “/Library/Application Support/Oracle/Java/Deployment/”
    /bin/echo “#deployment.properties” >> “/Library/Application Support/Oracle/Java/Deployment/deployment.properties”
    /bin/echo “deployment.macosx.check.update=false” >> “/Library/Application Support/Oracle/Java/Deployment/deployment.properties”
    # Java-Updater should parse deployment.properties as necessary and file ~/Library/Preferences/com.oracle.javadeployment.plist should be auto-generated
    # by the LaunchAgent /Library/LaunchAgents/com.oracle.java.Java-Updater.plist
    # User-specified preferences will go in ~/Library/Application Support/Oracle/Java/Deployment/deployment.properties
    # If the user-specified preferences turns on updates, it will take precedence over the system-wide disablement
    # and set “deployment.macosx.check.update=true” in ~/Library/Application Support/Oracle/Java/Deployment/deployment.properties

  7. MacGuyver
    Posted November 9, 2014 at 3:23 pm | Permalink

    Simpler solution? Block ALL traffic and whitelist.

    On our university-issued computers I installed TCPBlock (http://tcpblock.wordpress.com/ … get it while it is still free). I then checked only “Enabled”, “Block incoming connections” and “White List”. This effectively blocks *all* TCP/IP traffic except those you WANT. From the “Connecting Apps” tab you then click “Insert name into App list”. I accomplish this by launching desired apps one by one, with no other apps running, and inserting the names of apps and background processes that are needed.

    This is the only, simple, unobtrusive method I have found to manage security on OSX with out having to continually monitor changes to code, architecture, etc. Any new apps/processes are automatically blocked before you see them show up in TCPBlock’s monitor list. The only way an app can subvert this would be to replace the same app name/location. But TCPBlock has a way to manage this too. You can enable “Hash check” (requires you to re-add any existing apps to the whitelist). Unfortunately, this means the next time Firefox updates itself, the hash will change and it will be auto-blocked. But at least you regain control.

    This is a great solution for 10.6 (Snow Leopard) which has been left out in the cold by Apple in terms of security patches. It doesn’t protect you against fundamental flaws in code or architecture, but it does afford some manual control and guards against new software being installed with out your knowledge. Yes, there are more complex and mature software tools out there, and I am not saying you should not use any of them. This is just an easier option for most users.

    The author of TCPBlock has put it up for sale because they can no longer develop it. TCPBlock does not work with 10.10 (Yosemite), but it does work with 10.6 through 10.9. There is an older version that works with 10.5 on the web site linked above.

    Hopefully this will aid someone as much as it has me. Apologies for the late post on an old topic, but I think this is important info.

Leave a Reply