com.challengeandresponse.components
Class SoftwareUpdate

java.lang.Object
  extended bycom.challengeandresponse.components.SoftwareUpdate

public class SoftwareUpdate
extends java.lang.Object

This modest little class assists with automatic retrieval of updates to an application. It mostly just calculates platform-specific paths to downloads and related files, and checks version numbers. Configuration data are retrieved from a server, so the application creator has a modest measure of control over the behavior of update clients even when they're out in the field. The class considers that versions for different platforms may not be released simultaneously. If there is a README file, it also retrieves it for display to a user, if you wish, before they approve/deny an update.

A quick overview of how you'd use this:
1. Add SoftwareUpdate jar to your application classpath and add software update code to the app
2. Create config file(s) on the software distribution web server. The config file provides the current version number, an optional hash, an optional readme file url, and an optional download URL for the latest release
If there are application-specific directories, a config file goes in each one. If there's no distinction made for different operating systems, then there's just one config file.
3. The application periodically checks for updates:
3a. instantiate the class, SoftwareUpdate su = new SoftwareUpdate();
3b. call loadConfig() to retrieve config data from the update server
3c. application compares its version serial number to the value returned from getCurrentVersionSerial()
3d. application may display the current version's README file, contents available from getCurrentReadme()
3e. if the app is out of date, it can do whatever is appropriate to get the upgrade from get CurrentDownloadURL()
3f. the application or download helper may do something to allow the downloaded file's hash to be compared to the config file's hash parameter, from getCurrentHash()

This class works well in conjunction with BrowserLauncher 2 available from: http://sourceforge.net/projects/browserlaunch2/

RELEASE NOTES
version 1.10
IMPORTANT!
After instantiating the class, call loadConfig() to fetch its config file (as in example program below).

Version 1.10 makes significant changes to the way update details are configured:
- Now uses a single configuration file (one per OS if you are separating downloads by OS) to hold all the settings that control the remote updater.
- This replaces the previous scheme in which separate "version" and "hash" file names were provided, that would be opened and read.
- This change has been made because it's a cleaner approach, and because it'll be desirable to add more server-control on the remote update clients in the future
- The download URL is now in the config file and is no longer specified in the class. This will allow implementors to alter the download URL at any time or retrieve downloads from somewhere other than the baseURL. Also, because the download URL comes from the server at check-time, installed copies in the field will pick up and use a changed URL immediately, creating flexibility for downloads and customer notification.



Configuration file format
Place one LABEL / VALUE pair per line, separated by white space
"#" at the start of a line comments-out the line
Labels are not case-sensitive. Case of values is preserved as written in the config file.
HTTP and HTTPS protocols are supported. No others
versionserial <the integer serial number of the latest version to compare against>
hash <hash of the latest version>
downloadurl <url to download the latest version from>
readmeurl <url of the readme file for the latest version>

Example configuration files

# My config for Macintosh
# Assumes:
# base URL is "http://my.host/product/download"
# addPlatform(OS_MAC,"mac/") was called to set up a Mac-specific handler.
#
versionserial 120
hash 341F2DE6
downloadurl http://my.host/product/download/mac/Product.dmg
readmeurl http://my.host/product/download/mac/README

SoftwareUpdate anticipates the software distribution web server will have a layout something like this:
a base url: http://host.tld.com/product/current/ --- the base URL
... and under that, if you're distinguishing downloads by operating system, one directory per OS that you support, each with a configuration file
  eg: ./mac/configfile
  eg: ./linux/configfile
  eg: ./windows/configfile
 
... or if not distinguishing by OS, just one configuration file
  eg: ./configfile

Multi-platform support
See the method addPlatform() and the code example below. Just add the platforms you want to recognize (you can make your own) and provide the suffix to add to the baseURL to get to that platform's directory. If no platforms are set up, the OS specific suffix will be returned as "", so that every directory path points only to the baseURL.

Version comparisons
Version comparisons are performed with a Version Serial Number. This is an integer value that should be incremented with each new release. The value stored in the application at your user's computer will be compared to the value stored in the configuration file's 'versionserial' parameter. If the version serial number at the server is higher than the application's versionserial, an update is needed.

The configuration file only "requires" a version serial number... everything else can be optional. If you don't provide the other params, the get...() methods that would return their values will return null.
// example code
// this code assumes there will be three directories, /mac, /linux and /windows, for updates
// it presumes each platform directory will have a config file "CONFIGFILE" containing parameters as described above
//
// just to recap, this is the file and directory structure expected by the below example code:
// http://mysite.com/myproduct/ the base URL
// http://mysite.com/myproduct/mac/CONFIGFILE the config file for Macintosh updates
// http://mysite.com/myproduct/linux/CONFIGFILE the config file for Linux updates
// http://mysite.com/myproduct/windows/CONFIGFILE the config file for Windows updates
//
int thisVersion = 108; // the version of "This program" that will be checked for an available update
SoftwareUpdate su = new SoftwareUpdate("http://mysite.com/myproduct/","CONFIGFILE");
su.addPlatform(OS_MAC,"mac/");
su.addPlatform(OS_WINDOWS,"windows/");
su.addPlatform(OS_LINUX,"linux/");
// lazy exception catching. If caught separately, they tell more about what went wrong...
try {
su.loadConfig();
}
catch (Exception e) {
System.out.println("An exception occurred while loading the config file: "+e.getMessage());
}
System.out.println("Current version: "+su.getCurrentVersionSerial());
System.out.println("Current hash: "+su.getCurrentHash());
System.out.println("Current readme URL: "+su.getCurrentReadmeURL());
System.out.println("Current download URL: "+su.getCurrentDownloadURL());
System.out.println("Current readme file contents: "+su.getCurrentReadme());
// else compare the versions and download if not up to date
if (su.getCurrentVersionSerial() > thisVersion) {
System.out.println("Newer version "+su.getCurrentVersionSerial()+" available for download from: "+su.getCurrentDownloadURL());
}
else {
System.out.println("The current version "+thisVersion+" is up to date");
}
 

Version:
1.10, 11 July 2006
Author:
jim

Field Summary
private  java.lang.String baseURL
           
private static java.lang.String CONFIG_DOWNLOADURL
           
private static java.lang.String CONFIG_HASH
           
private static java.lang.String CONFIG_README
           
private static java.lang.String CONFIG_READMEURL
           
private static java.lang.String CONFIG_VERSIONSERIAL
           
private  java.lang.String configFilename
           
private static java.util.Hashtable configParams
          loadConfig() populates these configuration parameters from the config file.
static java.lang.String COPYRIGHT
           
private  java.lang.String os
           
static java.lang.String OS_LINUX
          Can be used with 'addPlatform()' to ID a Linux operating system
static java.lang.String OS_MAC
          Can be used with 'addPlatform()' to ID a Mac operating system
static java.lang.String OS_WINDOWS
          Can be used with 'addPlatform()' to ID a Windows operating system
private  java.util.Hashtable platforms
          Mapping a platform ID string to a URL path fragment
static java.lang.String PRODUCT
           
static java.lang.String PRODUCT_SHORT
           
private  java.lang.String userAgent
           
private static java.util.HashSet validConfigParams
          a set of valid config parameters, used by loadParams() to keep the configParams table clean
static java.lang.String VERSION
           
static java.lang.String VERSION_FULL
           
static int VERSIONSERIAL
           
 
Constructor Summary
private SoftwareUpdate()
          no-arg constructor isn't available
  SoftwareUpdate(java.lang.String _baseURL, java.lang.String _configFilename)
          Basic constructor.
 
Method Summary
 void addPlatform(java.lang.String osIdentifier, java.lang.String osSuffix)
          Add a platform identifier (based on operating system), specifying the directory that will be appended to the path, and the path to the downloadable current release, for that platform.
 java.lang.String getCurrentDownloadPath()
          Deprecated. badly named. Please use getCurrentDownloadURL
 java.net.URL getCurrentDownloadURL()
          Return the download url as retrieved from the config file
 java.lang.String getCurrentHash()
           
 java.lang.String getCurrentReadme()
          Fetch and return the current README for the latest released version for this operating system
 java.net.URL getCurrentReadmeURL()
          Return the readme URL
 int getCurrentVersion()
          Deprecated. renamed to getCurrentVersionSerial(). getCurrentVersion() will be removed in a future release.
 int getCurrentVersionSerial()
           
 java.lang.String getOS()
          Get the current "OS" setting, which was initialized when the class is instantiated
private  java.lang.String getPlatformPath()
          Return the URL path add-on for the current operating system, if any.
 java.lang.String getUserAgent()
          Return the UserAgent identifier string that's sent to the host
 void loadConfig()
          Call this method before trying to get...() anything from SoftwareUpdate.
static void main(java.lang.String[] params)
          Main method provided for testing and example...
private  java.net.HttpURLConnection openConnectionForURL(java.net.URL u, boolean followRedirects)
          Open a URL, and return a connection to it.
 void setOS(java.lang.String _os)
          Set the current "OS" setting to override the auto-initialized OS value (at class instantiation, the OS string is set from System.getProperty("os.name") )
 void setUserAgent(java.lang.String _newAgent)
          Change the UserAgent identifier string that's sent to the host.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

PRODUCT_SHORT

public static final java.lang.String PRODUCT_SHORT
See Also:
Constant Field Values

PRODUCT

public static final java.lang.String PRODUCT
See Also:
Constant Field Values

VERSION

public static final java.lang.String VERSION
See Also:
Constant Field Values

VERSIONSERIAL

public static final int VERSIONSERIAL
See Also:
Constant Field Values

VERSION_FULL

public static final java.lang.String VERSION_FULL
See Also:
Constant Field Values

COPYRIGHT

public static final java.lang.String COPYRIGHT
See Also:
Constant Field Values

CONFIG_VERSIONSERIAL

private static final java.lang.String CONFIG_VERSIONSERIAL
See Also:
Constant Field Values

CONFIG_HASH

private static final java.lang.String CONFIG_HASH
See Also:
Constant Field Values

CONFIG_DOWNLOADURL

private static final java.lang.String CONFIG_DOWNLOADURL
See Also:
Constant Field Values

CONFIG_READMEURL

private static final java.lang.String CONFIG_READMEURL
See Also:
Constant Field Values

CONFIG_README

private static final java.lang.String CONFIG_README
See Also:
Constant Field Values

validConfigParams

private static final java.util.HashSet validConfigParams
a set of valid config parameters, used by loadParams() to keep the configParams table clean


configParams

private static java.util.Hashtable configParams
loadConfig() populates these configuration parameters from the config file. If an item does not appear in the config file, its value is "null"


platforms

private java.util.Hashtable platforms
Mapping a platform ID string to a URL path fragment


baseURL

private java.lang.String baseURL

configFilename

private java.lang.String configFilename

os

private java.lang.String os

userAgent

private java.lang.String userAgent

OS_MAC

public static final java.lang.String OS_MAC
Can be used with 'addPlatform()' to ID a Mac operating system

See Also:
Constant Field Values

OS_WINDOWS

public static final java.lang.String OS_WINDOWS
Can be used with 'addPlatform()' to ID a Windows operating system

See Also:
Constant Field Values

OS_LINUX

public static final java.lang.String OS_LINUX
Can be used with 'addPlatform()' to ID a Linux operating system

See Also:
Constant Field Values
Constructor Detail

SoftwareUpdate

private SoftwareUpdate()
no-arg constructor isn't available


SoftwareUpdate

public SoftwareUpdate(java.lang.String _baseURL,
                      java.lang.String _configFilename)
Basic constructor. Specify the base URL for everything related to the latest version of the app to be updated, and also the name of a config file that can be found at either the baseURL or in OS-specific subdirectories of the baseURL if segregating by OS.

for example, new SoftwareUpdate("http://my.host/product/","configfile")
anticipates that http://my.host/product/configfile will be a config file if NOT distinguishing by OS
If distinguishing by OS, the the OS-specific directory is interposed in the path just before the file name, for example: http://my.host/product/mac/configfile.

Parameters:
_baseURL - The base URL under which config files(s) and optional per-os directories are found
_configFilename - The name (only) of the configuration file, to be appended to the baseURL (+OS version directory if any)
Method Detail

getCurrentVersion

public int getCurrentVersion()
Deprecated. renamed to getCurrentVersionSerial(). getCurrentVersion() will be removed in a future release.

Returns:
the version serial of the latest version, or -1 if versionserial was not set in the config file

getCurrentVersionSerial

public int getCurrentVersionSerial()
Returns:
the version serial of the latest version, or -1 if versionserial was not set in the config file

getCurrentHash

public java.lang.String getCurrentHash()
Returns:
the hash string from the config file, or null if none was set

getCurrentDownloadURL

public java.net.URL getCurrentDownloadURL()
Return the download url as retrieved from the config file

Returns:
thespecific download URL, or null if no downloadurl was specified in the config file
See Also:
for instructs about setting up different config files and paths for different platforms

getCurrentDownloadPath

public java.lang.String getCurrentDownloadPath()
Deprecated. badly named. Please use getCurrentDownloadURL

Compose and return the URL that points directly to the current-version download for this operating system

Returns:
the current download path (baseURL + OS-specific path to downloadable file)

getCurrentReadmeURL

public java.net.URL getCurrentReadmeURL()
Return the readme URL


getCurrentReadme

public java.lang.String getCurrentReadme()
Fetch and return the current README for the latest released version for this operating system

Returns:
the current ReadMe file for the latest version released for this OS

getOS

public java.lang.String getOS()
Get the current "OS" setting, which was initialized when the class is instantiated


setOS

public void setOS(java.lang.String _os)
Set the current "OS" setting to override the auto-initialized OS value (at class instantiation, the OS string is set from System.getProperty("os.name") )


setUserAgent

public void setUserAgent(java.lang.String _newAgent)
Change the UserAgent identifier string that's sent to the host. If UserAgent is not explicitly set, this class provides a default value.


getUserAgent

public java.lang.String getUserAgent()
Return the UserAgent identifier string that's sent to the host


addPlatform

public void addPlatform(java.lang.String osIdentifier,
                        java.lang.String osSuffix)
Add a platform identifier (based on operating system), specifying the directory that will be appended to the path, and the path to the downloadable current release, for that platform.

The OS is determined via System.getProperty("os.name"). The first partial string match is used. Constants are provided in this class for the three mainstream operating systems (see the constant values OS_*).

Matching is not case sensitive

example: addPlatform(SoftwareUpdate.OS_MACOSX,"mac/");

Parameters:
osIdentifier - a string that will match part of the System.getProperty("os.name") call for the desired OS
osSuffix - the string to append to the URL path, for this OS. This string will be appended to the baseURL that was set in the constructor. Provide trailing slashe as appropriate

loadConfig

public void loadConfig()
                throws java.net.MalformedURLException,
                       java.io.IOException,
                       java.lang.NumberFormatException,
                       java.lang.UnsupportedOperationException
Call this method before trying to get...() anything from SoftwareUpdate. This method will fetch the config file for the current released version ("for the current operating system" if OSes have been added) and will also open and retrieve the README file if a readmeURL was set in the config file.
Unrecognized labels found in the config file are silently ignored.

Throws:
java.net.MalformedURLException - if a valid URL could not be constructed from baseURL,os-path and file name
java.io.IOException - if an error occurred when opening and reading from the constructed URL
java.lang.NumberFormatException - if an expected numeric parameter cannot be parsed as a number
java.lang.UnsupportedOperationException - if a protocol other than HTTP or HTTPs is attempted

getPlatformPath

private java.lang.String getPlatformPath()
Return the URL path add-on for the current operating system, if any. To add a platform and platform-specific path, use addPlatform()

Returns:
the platform-specific path for this OS, or the empty string "" if no match was found or no platform support has been set up
See Also:
addPlatform(String, String)

openConnectionForURL

private java.net.HttpURLConnection openConnectionForURL(java.net.URL u,
                                                        boolean followRedirects)
                                                 throws java.lang.UnsupportedOperationException,
                                                        java.io.IOException
Open a URL, and return a connection to it. Handles HTTP and HTTPS only, throws an exception otherwise.

Parameters:
u - The URL to open.
followRedirects - set 'true' to follow redirects coming back from the server (e.g. http://.../ -> http://.../index.html) false to just load the given page and stop even if there is a Location: header
Returns:
an opened HttpURLConnection
Throws:
java.lang.UnsupportedOperationException - if an invalid protocol (neither HTTP nor HTTPS) is presented
java.io.IOException - if an IO error occurred opening the URL or making the connection

main

public static void main(java.lang.String[] params)
Main method provided for testing and example...

Example config file that might be used with the program below:

 #
 # Per the constructor below, this file would be located at http://myhost.tld/path/morepath/config
 #
 versionserial  110
 hash 3F309A14
 downloadurl http://myhost.tld/path/morepath/Program.sit
 readmeurl http://myhost.tld/path/morepath/README