Auto-update of native application bundles

Igor Nekrestyanov igor.nekrestyanov at oracle.com
Sun Jun 17 00:26:30 PDT 2012


Hi Dan,

thanks for the great writeup (and pushing this ahead).
I finally started to look into this more actively. Some 
questions/suggestions below.

On 6/15/12 5:02 PM, Daniel Zwolenski wrote:
> *The General Approach*
>
> Richard made reference to Sparkle<http://sparkle.andymatuschak.org/>  and
> like him, I feel this is a nice reference point for the style of solution
> we want. The neat thing that Sparkle does is it provides an API that
> developers can use inside their code to completely control and customise
> the upgrade of their application. This is the approach I've gone for as
> well. My POC includes a Java API that you can call to check for updates and
> trigger downloads.
IMHO, main great thing about Sparkle is that it is easy to add basic AU 
functionality to the application
(see https://github.com/andymatuschak/Sparkle/wiki)
You do not need to use API unless you want custom solution.

Therefore i am not focusing here on the public API part (keeping this 
for later).

> I think this approach is neat and powerful, but it's worth mentioning that
> there is still some debate amongst the JFX team about whether to go down
> this road or avoid the Java API and use off-the-shelf native updaters. Your
> input will very likely influence this decision so if you have a preference
> one way or the other (or have other suggestions), be vocal.

I think there is no debate on that having some "all in java with minimal 
native code solution" is good idea.
Debate is about whether it should be the only option or it also make 
sense to support other native update mechanisms
(e.g. use Sparkle directly on Mac).

As Dan said, thoughts on this are very welcome.
>
>
> An XML deployment descriptor, called 'app-profile.xml' is used to describe
> the JARs, JRE versions and other launch details - it is semantically
> comparable to a traditional JNLP file. When an update is triggered via the
> API, the code checks the remote URL for an updated app-profile.xml. If it
> finds one it downloads this and checks this with its local app-profile.xml.
> Any jars that have different version numbers are downloaded, and if a new
> JRE version is defined in the app-profile then the zip for this is
> downloaded and extracted as well.
Where these files are placed after download?
> Once all the downloading is complete, the
> local app-profile is then overwritten with the newer one and this newer one
> will be used by the native launcher to set the classpath next time the app
> is started.
>
>
IMHO, we can simplify this model following Sparkle ideas.
Sparkle does not know anything about application structure, etc. and 
focus only on update process.
This makes it easy to use, simple  and robust as it focuses on solves 
one problem.

Important points are:
     A. Sparkle uses "appcast" to describe what updates are available
           
(https://github.com/andymatuschak/Sparkle/wiki/publishing-an-update)
         Appcast includes
           - version of updates available
           - meta info about each update (type of update (e.g. regular 
or mandatory security), release notes, system requirements (e.g. Windows 
version 7+), etc.)
           - set of available update packages (aka enclosures) (e.g. 
full app image, delta patches for several versions, etc.)

    B. Sparkle knows where to look for update meta info in the 
application package (on Mac it is part of Info.plist that is some kind 
of application manifest).
        However, it only needs to know:
           - URL of appcast
           - location of key to verify update signatures
           - application update settings/preferences
        The expectation is that once app is updated this info will be 
updated (as it is part of "update package")
        [Note: this is not so simple on Windows if we want to make sure 
we are good citizens and need to update registry]

    C. Update package is black box for Sparkle core.
        It is modeled as
           - file hosted at given URL
           - signed with given key (signature is part of appcast)
           - package type defines what "update driver" will be handling 
this update
               (this allows to add support for various update packages 
as plugins/modules)
           - for most type for packages (except running installers) 
Sparkle goal is to update set of application files only
              [Windows or linux require more work as we may need to 
update registry or package info]

    D. If admin permissions are needed they only asked before update is 
finally installed.
        Sparkle does not keep any internal state in the app folder as it 
may be only writable for admins.

    E. Application relaunch after update

This is close to your approach but there are some important differences.
Following Sparkle more closely may simplify things and at the same time 
add some desired features
(e.g. ability to validate origin of autoupdate)

My current thoughts on "ideal" basic experience are as follows.

========
Developer experience (no java coding needed):

   1. At the package time developer requests autoupdate support in the 
native cobundle by providing:
         * http URL to appcast + public key/cert to validate update 
signatures
         * or https url
      and define application update mode (mandatory update, ask user and 
allow him to skip version, etc.)

   2. Once update is needed developer creates "update package" (single 
file).
     For this he will need to run same packager utility/ant task but 
specify base image, certificate to sign package and
     and desired type of update (full or incremental)

   3. Developer need to create an appcast xml file (default can be 
generated in step 2 but he may want to customize it)

   4. Developer stages both appcast and package bundle
=========

End user (background updates):
    * installs application with background update policy
    * every time application is launched it will check appcast (if online)
    * if update is detected it will be loaded silently while app is running
    * Once update is loaded and verified user will be prompted to 
install it (it may be notification if update is mandatory)
    * If he agrees then he may see UAC dialog (unless installation is on 
per user basis)
    * application relaunch after update
    * attempt to launch app being updated should fail with reasonable 
dialog and should not fail update process (it may need to wait will .exe 
is unlocked)
=========

Implementation wise i was thinking along the lines of what you have 
(only glimpsed though your code it though) with following
notable differences:

    * update package is single file and we only worry about it version.
      No need to worry about version of individual components as we do 
not want arbitrary mix&match.

    * updates are loaded to ~/.javafx/app-key (where app-key could be 
derivation from app install location)
      We also keep user answers for this app in that folder (e.g. had we 
offered him this version already? when was last time we checked for 
update, etc)

    * Update process is:
         - build temp full app image in the ~/.javafx/app-key/version
         - if update accepted then exit application and run helper app 
co copy image over
         - possibly run "post upgrade script" as part of helper app
             (this script is specific to "bundler" technology used. E.g. 
for EXE/MSI bundlers we may want to update few keys in registry
         - relaunch app
         - AU module running in the relaunched app could detect temp 
"image" that is the same as current version and erase it

       Note that there could be several alternative helper apps. E.g. 
one that pops UAC and one that does not for per user installs.
       Or helper up to install update packaged as MSI (by running 
msiexec silently and showing progress)

       I was even thinking that "helper" can be implemented in java with 
minimal native code (to elevate permissions and update registry if needed).

    * Integration with application bundle
        - details on whether AU is needed and what type is it may be 
embedded application manifest for main app jar
        - embedded jauncher will read them and use internal AU APIs 
accordingly
           (this is why no user code changes are needed)
        - current app version, update url and signature are part of 
package.cfg or manifest in the main app jar
        - AU support itself is part of jfxrt.jar, bundle also includes 
helper app to "move image into final location" and
            script to do "post update config"

What do you think?

-igor



More information about the openjfx-dev mailing list