The Source for Java Technology Collaboration
User: Password:



   

Java Sketchbook: Digging into Java Web Start Java Sketchbook: Digging into Java Web Start

by Joshua Marinacci
09/01/2005

Contents
The Java Web Start Sandbox
Using the Service APIs
in an Unsigned Application
Use Signed Code for Trusted Access
Use Pack 200 for Faster Downloads
   Compress Your .Jars
   Set Up Your Web Server
to Support Pack 200
Extra Polish
   Shortcuts
   Related Content
   File Associations
Conclusion
Resources

Java Web Start is a technology for deploying and updating desktop Java applications easily from a web server. It's much more than a launching protocol, however. JWS provides a sandbox for safely running applications, as well as new compression methods to speed up downloads, and some useful extensions to make your application feel at home on the user's desktop. The first part of this series covered just the basics. This second half will show you everything you need to do to make your application run safely and feel professional.

The Java Web Start Sandbox

In the age of viruses, spyware, and Internet Explorer bugs, security has become one of the most important issues we face as software developers. Java has always had very good support for writing secure applications, being designed with security in mind from the beginning. The lack of memory access and the security manager ensure that an entire class of security bugs have been wiped out. Still, there is always the possibility that a malicious programmer will write a program to purposely delete files or steal personal information. To tackle this issue, Java Web Start provides something known as the sandbox.

The sandbox is a restricted environment that Java Web Start applications run inside by default. Using the security manager and some custom classloaders, it prevents the program from accessing any inappropriate resources or damaging the host computer in any way. In this way, the user is protected from malicious or badly written programs. If you have ever programmed applets before, you may think that this is familiar. Well, you are right. The JWS sandbox was specifically designed with the applet security model in mind. In fact, with the right JNLP, you can turn any existing applet into a JWS app without any modifications. The sandbox also provides some extensions to let you do more than you can with plain applets, like access local files, use the clipboard, and print.

In practical terms the sandbox restricts your program from talking to the host environment in any way. Files, network access, and most system functions are off-limits, as are various ways of getting around these restrictions, like using custom classloaders, Runtime.exec() calls, or native libraries.

Well, this all sounds very limiting. How are you supposed to write an application that does anything useful if you can't work with files, print, or talk to the network? JWS provides three options:

  1. You can work within the restrictions. Just like an applet, the JWS sandbox will let you open a network connection back to the server from which the application was downloaded. You can load and save all data on the server, leaving nothing on the client machine other than the application itself. This works out surprisingly well for a number of application types. It also reduces maintenance and support headaches--if there's no data on the client, it can't become corrupted. If your application can work with the sandbox restrictions, I highly recommend you go this route.
  2. You can use the JWS Service APIs. The sandbox does allow your program to have limited access to the unsafe resources as long as it's under the control of the users. Using the Service APIs in the javax.jnlp package, you can ask the user to approve possibly unsafe actions like the opening of a file. The sandbox will open a file dialog window asking the user to select a file. Then the file will be passed to your application through a safe API. This way, the user has control over which file to use, if any, and your application can read files without using the unsafe java.io.File API.

    The JWS Service APIs give you access to files, the clipboard, printing, and a limited form of data persistence. Java 1.6 (Mustang) will introduce a new SocketService to give your program conditional network access. With these services, you can do almost anything while staying inside the sandbox, provided the user grants permission.

  3. You can go outside of the sandbox. If your program really, really needs unsafe access to the host computer (for example, your program wants to use the JDIC native libraries), then you can ship your program as a signed and trusted application. This means that you must digitally sign the components of your program and place them on your website with the same signature.

    The first time a user downloads your signed program, she will be presented with a dialog box asking her to accept the certificate and continue with the installation. If she says no, the installation will be aborted. If she say yes, then your program will be installed and launched outside of the sandbox, giving you access to the full Java APIs. This only works, of course, if your application is signed, because users need to be able to trust that the program really comes from you. Even when it is signed, you are asking your users to trust you, and some of them may be hesitant to install a program that could potentially do anything. I recommend going with one of the other two options if at all possible, since they involve less risk to the user's computer and don't display a potentially scary security dialog box.

Using the Service APIs in an Unsigned Application

If you want your program to work inside of the sandbox but still access local resources, you need to use the Service APIs. There are APIs to cover all of the major resources you could need to access (files, clipboard, printing, etc.), but they all operate pretty much the same way. When you request an unsafe feature using a Service API, the sandbox will show the user a dialog box asking permission. If the user agrees, you will be able to access that feature. If the user declines, you will get a null value and you'll be unable to use that feature. For example, suppose the My Pirate program from the previous installment of this series let users select a photo from their hard drives to use as their icon in the game. To do this safely, you would need to use FileOpenService:


import javax.jnlp.*;
// more imports ...
public class FileOpener extends ActionListener {
    public void actionPerformed(ActionEvent evt) {
        try {
            FileOpenService open = (FileOpenService)
                ServiceManager.lookup(
                    "javax.jnlp.FileOpenService");
            FileContents fc = open.openFileDialog(null,null);
            // do stuff with the file contents
            // like fc.getName();
        } catch (Exception ex) {
            System.out.println("exception: " + ex);
        }
    }
}

Above is an ActionListener that asks the user for a pirate configuration file. First it obtains a FileOpenService by looking it up from the ServiceManager and then casting to the right type. Then it calls the openFileDialog() method with null for the directory and file type arguments. When it makes the open call, the sandbox will show a dialog box asking the user for the file. Once the user selects a file, the action listener will receive a FileContents object, which has safe methods for retrieving file information.

Note: Mac OS X supports the javax.jnlp.* APIs but it does not provide a standalone .jar to compile against as part of its JDK. You will need to download Sun's JDK for another platform and compile against jnlp.jar from that. Being 100 percent Java code, it will work fine on the Mac.

Use Signed Code for Trusted Access

If your program needs full access to the user's computer, then you must use a signed application. To make your program signed, you must first digitally sign the .jars in your program, and then change your JNLP to request full access. Digitally signed .jars require a keystore, so that's where we'll start.

First you need a keystore. This is a file on your development computer that stores the actual digital keys to prove that your .jars are coming from you. It requires a password for access, ensuring that only you are signing your .jars and not someone who has gained access to your computer. If you already have professional keys from a trusted certificate authority (like VeriSign or Thawte), then you can use those. If you are just doing development work, you can use a self-signed certificate to try out the process. To generate your own keystore with a self-signed certificate, use the keytool program like this:

keytool -genkey -keystore mykeystore -alias mykey

The command above will generate a new key called mykey and place it in a keystore file called mykeystore. When you execute the above command, the keytool application will ask you a series of questions to identify you and your organization, and will also have you create a password to protect the keystore. Once your keystore is ready, you can sign your .jars like this:

jarsigner -keystore mykeystore temp.jar mykey

The jarsigner command above will open the keystore called mykeystore, retrieve the key named mykey, and use the key to sign the temp.jar file. Once your .jars are signed, you can upload them to your web server.

The last step is to change the JNLP file to request full security permissions, like this:

<?xml version="1.0" encoding="UTF-8"?>
<jnlp codebase="http://mypirate.com/downloads/"
    href="mypirate.jnlp">
    ....  the rest of the JNLP file
    <security>
        <all-permissions/>
    </security>
</jnlp>

With the new JNLP and the signed .jars, Java Web Start will ask your users to approve the certificate (including the identifying information you put into the keystore), and then run your application will full permissions. If you will be signing your .jar files over and over, you may want to use the signjar Ant task to automate the process. If you are storing your code and build files in revision control (like a java.net project, for example) be sure to put the actual password in a private property that no one else can see.

For more information on signed .jars, please read the man page for keytool and this great article, which shows more examples of using keytool and how to get a free certificate that works with most browsers' built-in authorities (meaning you won't get the invalid-certificate screen).

Use Pack 200 for Faster Downloads

Once you have your program signed and running, you may want to think about improving its download time. Software has a tendency to grow larger over time, producing ever-larger files to download. To speed up the downloads, Java 1.5 introduced a new form of compression specifically designed for Java code: Pack 200.

Pack 200 was named for the JSR that defined it, and it can compress some .jars by a factor of five, in addition to the typical 2x gzip compression that the Pack 200 tool also applies. This can result in compressed .jars being ten or 15 percent of their uncompressed size. For a widely deployed application, this will greatly speed up installation time for your users and greatly reduce your bandwidth bill. Pack 200 only works well on Java bytecode, so if your .jar has a lot of things in it that aren't class files (like images) you won't see as much compression. Pack 200 takes advantage of duplicated information between classes, so larger .jars will compress better.

Compress Your .Jars

Compressing your .jars with Pack 200 is very easy. Java 1.5 comes with the pack200 command-line program, which converts a standard .jar into a packed .jar. For example, to compress mypirate.jar, you would do this from the command line:

pack200 mypirate.jar.pack.gz mypirate.jar

This will compress mypirate.jar into a new mypirate.jar.pack.gz file without removing the original .jar file. Once packed, you should upload both files to the web server where you will host your application. More on why you should upload both in a second.

If you are using signed .jars, the process is slightly more complicated. pack200 will rearrange your classfiles and the signature depends on the bytes being in a particular order. To sign a packed .jar, you will need to pack, unpack, and then sign the .jar before repacking it. This will preserve the order of the bytes for the signature to work. You can read more on this process in the Pack 200 section of the Java 1.5 deployment guide.

If you are building and deploying your application frequently, you can use an Ant task from the open source java-pack200-ant-task project on java.net to integrate Pack 200 into your build process.

Set Up Your Web Server to Support Pack 200

The server side of Pack 200 is more complicated than the compression phase. Older Java Web Start clients may not understand Pack 200, or it may be turned off for some reason. To support all kinds of JWS clients, Pack 200 has a special negotiation protocol using HTTP headers to download the right .jar. This means you need to set up your web server to support these special headers. Depending on your web server, there are many ways you could do this, but the easiest way is to use a special servlet that comes with the 1.5 JDK, called JnlpDownloadServlet. The following instructions are for Tomcat, but you can adapt them for any J2EE application server.

In the sample/jnlp/servlet/ directory of the Sun J2SE 1.5 JDK, you will find a README file and a couple of .jars. The JnlpDownloadServlet is in the jnlp-servlet.jar file. You need to copy this .jar to the WEB-INF/lib directory of whatever servlet context you will use to serve up your application .jar files. Then you must modify the web.xml file to include the following lines:

<web-app>
    <servlet>
    <servlet-name>JnlpDownloadServlet</servlet-name>
        <servlet-class>
          jnlp.sample.servlet.JnlpDownloadServlet
        </servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>JnlpDownloadServlet</servlet-name>
        <url-pattern>*.jar</url-pattern>
    </servlet-mapping>
</web-app>

The first section, servlet, defines the JnlpDownloadServlet itself. The second section maps all .jar files to this servlet. This means when the client requests a .jar file, the request will go through the download servlet. For any other files (say, image or .jnlp files), the request will proceed as normal. The servlet will look at the HTTP headers to detect if the client supports Pack 200. If the client does support Pack 200 and there are packed .jar files available, then the servlet will send out the packed versions; otherwise it will send the normal .jars. This is why you must have both the standard .jars and the packed ones on your server.

You don't need to modify your JNLP file to use Pack 200. Still refer to your .jars by the normal name (for example, mypirate.jar). The JWS client and the download servlet will transparently use Pack 200 when possible, and of course, older clients will still use the normal .jars.

To further improve download speed, the JnlpDownloadServlet also supports .jar diffs for application updates. This means it sends only the difference between the old and new .jars instead of the entire new .jar. .Jar diffs are beyond the scope of this article, but you can read more about them in the Download Servlet Guide.

Extra Polish

Java Web Start provides a lot of odds and ends to help you polish your application. These extras can add a sense of professionalism. It's worth taking the time to evaluate your application and use them where appropriate.

Shortcuts

Java Web Start allows the user to create an icon for your program if the user requests it. However, you can add hints to prompt the user and request an icon on the desktop. If the user's operating system has the concept of a Start menu, you can also specify a particular place in the menu to put your program and any extra resources. You can do this using the <menu> element and submenu attribute in your JNLP file. For example, if I would like the My Pirate! program to be installed on the desktop and in the Aye-Soft/My Pirate! menu, then I would need to change my JNLP file to look like this:

<jnlp codebase="http://mypirate.com/downloads/"
    href="mypirate.jnlp">
    <information>
        <title>My Pirate!</title>
        <shortcut>
            <!-- put a shortcut on the desktop -->
            <desktop/>
            <!-- put shortcut in start menu too -->
            <menu submenu="Aye-Soft">
        </shortcut>
    </information>
    ... the rest of the JNLP

The <desktop> element inside of <shortcut> asks Java Web Start to put an icon on the desktop if the user allows it. The <menu submenu="Aye-Soft"/> element will put a shortcut with the icon and title of the application (as specified by the <title> element above) in the Aye-Soft menu.

The newest version of Java Web Start supports links for related content. Using the related-content element, you can add a link to extra documentation or other resources that should be installed in the start menu alongside your application.

<jnlp codebase="http://mypirate.com/downloads/"
    href="mypirate.jnlp">
    <information>
        <title>My Pirate!</title>
        <shortcut>
            <menu submenu="My Pirate"/>
            <desktop/>
        </shortcut>
        <related-content href="readme.html">
            <title>Instructions</title>
        </related-content>
... the rest of the JNLP

The <related-content> element in the JNLP file above will put a link to the readme.html file in the menu with the title Instructions.

File Associations

You can request that your application be registered to support certain file types. This means when the user launches a file of the type you set, your program will be launched. You must specify the association using both an extension (the characters after the . in a filename) and a MIME type.

<jnlp codebase="http://mypirate.com/downloads/"
    href="mypirate.jnlp">
    <information>
    <title>My Pirate!</title>
    <association extensions="pirate"
        mime-type="x-application/pirate"/>
... the rest of the JNLP

The association element in the JNLP above would register the application as a handler for all .pirate files and all files with a MIME type of x-application/pirate. You can set up more than one association by using multiple elements.

Conclusion

Java Web Start is a powerful technology for delivering applications as easily as web apps, but with the rich interface and features of standard desktop apps. It is secure, easy to use, and gives your application fast updating capabilities. For more information on Java Web Start, take a look at Sun's complete Java Web Start Developers Guide. For any new application, or even old ones that you would like to deploy widely, I recommend you try out Java Web Start and see how it can improve your maintenance and your users' experience.

Resources

Joshua Marinacci first tried Java in 1995 at the request of his favorite TA and has never looked back.

Read more The Java Sketchbook columns.

View all java.net Articles.

 Feed java.net RSS Feeds