Launcher is
designed to make all classes, whoever wrote them, appear as if they are part of
rt.jar: the classes that are included in every Java installation. Since, it isnÕt
practical to get all of the java code in the world into a single jar file, individual
jar files are downloaded Òon demandÓ instead.
Launcher is the
Ôclient sideÕ of this arrangement. Launcher.jar contains a single class file
(called Launcher.class) which operates both as a callable top level class (with
a main) and as a traditional java ClassLoader.
To use it, download
either Launcher.jar or Launcher.class
and then run it by typing one of the following from the command line (you can
also just double-click the jar file and type the class name into the dialog
that pops up):
java
Ðjar Launcher.jar <aClassName> <args>
or
java
Launcher <aClassName> <args>
Example:
java
Launcher groovy.ui.Console
This should
download the Groovy interpreter (and the assembler that it depends on) and
launch a Groovy console.
If youÕre using
Windows and youÕre behind a firewall, youÕll need something like this instead
of the above:
java -Dhttp.proxyHost=inproxy0
-Dhttp.proxyPort=8080 Launcher groovy.ui.Console
Once the Groovy
console is up and running, type the following into it (use the ÒRunÓ menu item,
or its shortcut, to execute each statement):
f
= new javax.swing.JFrame()
bob.Guide.edit(f)
de.progra.charting.test.GraphFrame.main()
You should see
the ClassLoader download and run, first, a GUI Builder (that we wrote) and then
a graphing package from some folks in Germany. Both the GUI builder and the graph
drawing routines are downloaded dynamically - at the point when you execute the
statements in the Groovy interpreter. Neither package was changed to
accommodate this scheme.
Here are some
other examples:
org.bitstorm.gameoflife.StandaloneGameOfLife.main()
nahay.games.MegaTTT.main()
How it Works
Launcher works
by using a web server as an index: effectively mapping class names to the
addresses of the jar files that contain them. In the circumstances where an
ordinary ClassLoader would fail, throwing a ClassNotFoundException, Launcher
tries to find the class in a jar file on the Internet. It does this using the
following steps:
á
go to the
central index site, with a URL that includes the name of the required class
á
do an HTTP
request and download the page
á
get the URL
of the jar file from that page
á
download that
jar file
á
cache it
locally
á
add that
local file to the classpath
á
load the
class
á
continue Ð as
if the jar file had been there all along.
The next time a
class from this jar file is encountered it will be loaded in the normal way. If
any of those classes make references to classes not in the jar file Ð any
unknown classes are handled the same way Ð potentially causing a set of jar
files to be downloaded one after the other. For example, many classes in
jfreechart.jar depend on jcommon.jar - and drawing a graph causes jcommon.jar
to be downloaded as well as jfreechart.jar (unless jcommon.jar has been
downloaded already).
The indexing
service is currently organised as a simple website (you can browse it from here). HereÕs the list of URLÕs that
itÕs based on.
The next step is
to add some CGI scripts to allow people to add their own jar files to the index
Ð but thatÕs not done yet. If you have jar files youÕd like indexed, drop me a
line at philip<at>pmilne<dot>net. ItÕs quick to do Ð Indexer.java in the src directory
takes the URL of a jar file, downloads the jar file and updates the index
according to the package names in the jar file.
Scaling
One problem with
creating a repositories of jar files on a central server is that the server has
to vend all of the jar files it holds Ð and this can create a performance bottleneck.
Here, the response from the indexing service is less than a packet in length
[check this]* so the Launcher is able to disintermediate the indexing service
with a minimal expenditure of server-side resources. After that, the
ClassLoader can download the, much larger, jarfiles directly from the locations
where they were originally published. According to the back of this Ôere
envelope, a single machine using this technique could cope with every (unique)
jar file on the Internet.
Versioning?
An excellent
question. (!) Basically, weÕve not done anything on versioning yet. Sometimes
an application will only work with an older version of a jar file than the
current one. You can handle this situation on an ad hoc basis by downloading
the legacy jar file and putting it on the classpath. Launcher is a subclass of
URLClassloader and entries found in a supplied classpath will occlude those
that could be found in the index.
Should we add
dependency info to the site so version dependencies can be stated when present?
What do you do if two jar files in an application depend on different versions
of a third jar file? This is solvable: we can use the fact that Java virtual
machines can simultaneously use multiple versions of the same class files Ð as
long as their classloaders are different. [Identity of classes in a VM is
determined by the tuple of class name and class loader]. But is all this more
complicated than itÕs worth? Practical instances of the versioning problem seem
surprisingly rare in popular packages and are not show-stoppers because an
explicit classpath can be used to specify a specific version in the unusual
case where the current version is not the one thatÕs required.
The Cache
The Cache of jar
files is located in your home directory (C:WINNT/profiles/<userName> in
windows) in a (typically hidden) directory called .launcher/Cache.
To Do
á
Server side
components to allow third parties to submit and edit links to their sites.
á
More thought
on versioning.
á
Date
checking, to update cache when a, similarly named, version of a jar file appears. [Easy to do this].
á
Progress bar
for downloads.
á
Avoid
corruption in aborted downloads. Buffer downloads Ð or load to intermediate
file.
If you have
thoughts on these subjects or would like to contribute, please drop me a line
at philip<at>pmilne<dot>net.