Enabling Java Web Start
Some folks like to start their mornings with Java, but I like to start mine with Java Web Start. What brew of coffee is that, you ask? It's the kind that's now powering tXtFL, the football simulator written in the Java programming language. With Java Web Start (JWS), launching a program is as simple as opening a browser page and clicking on a link. JWS will identify the platform, download the correct files, and bring you to the football stadium in no time. But as the road from tXtFL to JWS was considerably more arduous than I had hoped or intended, I'd like to take the liberty to describe how to JWS-enable a program, in hopes that it might help if you are planning to do the same.
Getting your John Hancock: signing JARs
The first step to enabling a program for JWS is to set up both the executable files and the files to execute them. As much as I'd like to click on a link to open up a program, I also want to know whether that program has any security issues. JWS gets around this problem by requiring files it launches to be signed, signifying who is the owner of those files. Signing a file usually involves generating a key and then using that key to sign each of the files that will be downloaded by JWS.
The executable can be generated using a standard JAR command. The command usually specifies a manifest file that identifies the main path, the name of the resulting JAR file, and the .class files and other supporting files necessary to run the program. One of the tricks I've just learned is that JARs for JWS require neither the classpath nor the manifest file at all! All of that information will be provided in a separate launch file.
Here's a jar example from tXtFL. If you want the file to also be executable outside of JWS, you'll need to pass it a manifest file.
To sign the JAR, I needed to generate a key for signing. The following command creates a key named keystore that will identify who I am using the information I provide it, including company name, etc:
Using this key, I can now sign the executable and all the other JAR files that the program requires. tXtFL needs one of a number of SWT files plus the derby.jar database file, which can be individually signed using the appropriate version of the following command:
For a more complete example, see the end of the tXtFL build script.
3-2-1...Liftoff: Setting up for launch
Now that the signed JARs are in place, I can generate the file to execute them. JNLP files are the launch scripts that specify where the executable and support files are and how to access them. The tricky thing is that the JNLP specifies where itself is--or should be. JWS will always check this location and use the version there, regardless of whether it's an older version. Be sure to check that "codebase" specification in the JNLP file if debugging is turning out to be a nightmare (more on that below).
The whole appeal of JWS for tXtFL lies in the platform-dependence of SWT, the underlying graphical toolkit for tXtFL. This Eclipse-based toolkit affords many powerful features at the expense of requiring a separate toolkit to be distributed for each platform on which the program is expected to run. Fortunately for us, JWS can automatically detect the platform and specify the appropriate version of SWT to download. Detecting the platform is merely a matter of using the "resources" tag to specify both the "os" and the "arch". That's right--JWS can detect not only whether the user is using a Mac or a PC, but also whether the computer is running a 32- or 64-bit of Java. One huge source of confusion is that for Java, "32-bit" vs. "64-bit" refers to the version of Java, not necessarily that of the operating system. JWS bypasses that confusion by doing the detection process for you. Here's an example of the syntax:
Ain't that beautiful? When JWS loads the JNLP file on a Mac running 64-bit Java, JWS learns that it should download the swt-mac64.jar file and also pass the -XstartOnFirstThread argument, a requirement for running SWT-based programs on the Mac. Adding similar code for each of the other supported platforms allows custom arguments and resource files for each targeted platform.
Here's a complete example, from tXtFL.
Embedding a database (perfectly optional)
tXtFL had a unique problem on its hands, and that was the fact that deep in the bowels of tXtFL lies legacy code and legacy ways of doing things that longed for a dramatic overhaul. As a lover of all things Text, I originally wanted to make sure that the player and team configuration files in tXtFL were composed of simple text files, one per player or team. The end result was thousands of files that would be a nightmare to transmit over the web merely to play a simple game of football. Of course, these files could be embedded into the JAR executable, but that would defeat the purpose of the text files in the first place since they would be buried rather than accessible in the executable.
As a long-term solution, tXtFL underwent a dramatic overhaul with the introduction of a shiny new Derby database. Derby takes care of these configuration files by completely doing away with them (or almost, as you shall see). Each player and team file becomes merely a line or several lines within the database. The database is generated on the fly when the user first launches the program, and the database resides on the user's computer rather than requiring any additional transaction over the web.
Of course, one issue remaining is how to get the player and team data into the database in the first place. The program still requires a player and a team spreadsheet containing all of the data. Collecting the data in 2 files is certainly an improvement over the thousands of files previously used, but they still require the program to have a way to access files in the first place. It turns out that the solution is rather simple: accessing files using the getResourceAsStream method allows the files to be embedded within the application jar. Now the player and team spreadsheets are packaged right inside tXtFL.jar.
By default, a new Derby database will be instantiated in the working directory. That's fine if the directory is where I'm testing the files, but becomes problematic when a user launches JWS from the browser, in which case Derby tries to write its files to the browser directory but fails because of permission errors. The solution is to set the derby.system.home property to point the browser to a defined, accessible directory.
Testing tXtFL: Finding multiple platforms from which to test JWS
The first place I like to test my files is, of course, on the local system. I always thought of JWS as an Internet-based solution, but it can also be configured to launch from the local computer. I simply changed the codebase to point to the directory on my computer where I had stored the JWS files (eg file:///home/user/src/txtfl/), and JWS loaded the application files from there. When I was ready to "go live," I copied the JWS files to my server and updated the codebase to the server URL.
When I first put the files onto the server and attempted to launch them, I got a page that displayed nothing more than the title and description of the application. The server wasn't configured to tell the browser that the JNLP launch file is meant to be run by Java Web Start, so the browser tried to display the file as a web page. Fortunately I'm hosting my pages through a Linux-based service that gave me access to configuring the server file type specifications.
Many folk on the web castigate the WORA principle of Java--"Write Once, Run Anywhere." I think that everyone agrees that Java (or life) isn't quite that simple even at it's best, but it's a software principle that I for whatever reason have come to believe in. I guess I like to be able to use my software regardless of whether I'm sitting at a Windows or a Linux or a Mac box, and I want whoever's using my software to have the same options. Most importantly, I want everybody everywhere to be able to play virtual football whenever they can, football season or otherwise.
Getting your John Hancock: signing JARs
The first step to enabling a program for JWS is to set up both the executable files and the files to execute them. As much as I'd like to click on a link to open up a program, I also want to know whether that program has any security issues. JWS gets around this problem by requiring files it launches to be signed, signifying who is the owner of those files. Signing a file usually involves generating a key and then using that key to sign each of the files that will be downloaded by JWS.
The executable can be generated using a standard JAR command. The command usually specifies a manifest file that identifies the main path, the name of the resulting JAR file, and the .class files and other supporting files necessary to run the program. One of the tricks I've just learned is that JARs for JWS require neither the classpath nor the manifest file at all! All of that information will be provided in a separate launch file.
Here's a jar example from tXtFL. If you want the file to also be executable outside of JWS, you'll need to pass it a manifest file.
jar -cf tXtFL.jar com about.txt logo.png logo-draft.png draft
To sign the JAR, I needed to generate a key for signing. The following command creates a key named keystore that will identify who I am using the information I provide it, including company name, etc:
keytool -genkey -keystore keystore -alias myself
Using this key, I can now sign the executable and all the other JAR files that the program requires. tXtFL needs one of a number of SWT files plus the derby.jar database file, which can be individually signed using the appropriate version of the following command:
jarsigner -keystore keystore tXtFL.jar myself
For a more complete example, see the end of the tXtFL build script.
3-2-1...Liftoff: Setting up for launch
Now that the signed JARs are in place, I can generate the file to execute them. JNLP files are the launch scripts that specify where the executable and support files are and how to access them. The tricky thing is that the JNLP specifies where itself is--or should be. JWS will always check this location and use the version there, regardless of whether it's an older version. Be sure to check that "codebase" specification in the JNLP file if debugging is turning out to be a nightmare (more on that below).
The whole appeal of JWS for tXtFL lies in the platform-dependence of SWT, the underlying graphical toolkit for tXtFL. This Eclipse-based toolkit affords many powerful features at the expense of requiring a separate toolkit to be distributed for each platform on which the program is expected to run. Fortunately for us, JWS can automatically detect the platform and specify the appropriate version of SWT to download. Detecting the platform is merely a matter of using the "resources" tag to specify both the "os" and the "arch". That's right--JWS can detect not only whether the user is using a Mac or a PC, but also whether the computer is running a 32- or 64-bit of Java. One huge source of confusion is that for Java, "32-bit" vs. "64-bit" refers to the version of Java, not necessarily that of the operating system. JWS bypasses that confusion by doing the detection process for you. Here's an example of the syntax:
<resources os="Mac OS X" arch="x86_64">
<j2se version="1.6*" java-vm-args="-XstartOnFirstThread" />
<jar href="lib/swt-mac64.jar" />
</resources>
Ain't that beautiful? When JWS loads the JNLP file on a Mac running 64-bit Java, JWS learns that it should download the swt-mac64.jar file and also pass the -XstartOnFirstThread argument, a requirement for running SWT-based programs on the Mac. Adding similar code for each of the other supported platforms allows custom arguments and resource files for each targeted platform.
Here's a complete example, from tXtFL.
Embedding a database (perfectly optional)
tXtFL had a unique problem on its hands, and that was the fact that deep in the bowels of tXtFL lies legacy code and legacy ways of doing things that longed for a dramatic overhaul. As a lover of all things Text, I originally wanted to make sure that the player and team configuration files in tXtFL were composed of simple text files, one per player or team. The end result was thousands of files that would be a nightmare to transmit over the web merely to play a simple game of football. Of course, these files could be embedded into the JAR executable, but that would defeat the purpose of the text files in the first place since they would be buried rather than accessible in the executable.
As a long-term solution, tXtFL underwent a dramatic overhaul with the introduction of a shiny new Derby database. Derby takes care of these configuration files by completely doing away with them (or almost, as you shall see). Each player and team file becomes merely a line or several lines within the database. The database is generated on the fly when the user first launches the program, and the database resides on the user's computer rather than requiring any additional transaction over the web.
Of course, one issue remaining is how to get the player and team data into the database in the first place. The program still requires a player and a team spreadsheet containing all of the data. Collecting the data in 2 files is certainly an improvement over the thousands of files previously used, but they still require the program to have a way to access files in the first place. It turns out that the solution is rather simple: accessing files using the getResourceAsStream method allows the files to be embedded within the application jar. Now the player and team spreadsheets are packaged right inside tXtFL.jar.
By default, a new Derby database will be instantiated in the working directory. That's fine if the directory is where I'm testing the files, but becomes problematic when a user launches JWS from the browser, in which case Derby tries to write its files to the browser directory but fails because of permission errors. The solution is to set the derby.system.home property to point the browser to a defined, accessible directory.
Testing tXtFL: Finding multiple platforms from which to test JWS
The first place I like to test my files is, of course, on the local system. I always thought of JWS as an Internet-based solution, but it can also be configured to launch from the local computer. I simply changed the codebase to point to the directory on my computer where I had stored the JWS files (eg file:///home/user/src/txtfl/), and JWS loaded the application files from there. When I was ready to "go live," I copied the JWS files to my server and updated the codebase to the server URL.
When I first put the files onto the server and attempted to launch them, I got a page that displayed nothing more than the title and description of the application. The server wasn't configured to tell the browser that the JNLP launch file is meant to be run by Java Web Start, so the browser tried to display the file as a web page. Fortunately I'm hosting my pages through a Linux-based service that gave me access to configuring the server file type specifications.
Many folk on the web castigate the WORA principle of Java--"Write Once, Run Anywhere." I think that everyone agrees that Java (or life) isn't quite that simple even at it's best, but it's a software principle that I for whatever reason have come to believe in. I guess I like to be able to use my software regardless of whether I'm sitting at a Windows or a Linux or a Mac box, and I want whoever's using my software to have the same options. Most importantly, I want everybody everywhere to be able to play virtual football whenever they can, football season or otherwise.
Comments