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