Roll your own applet: the definitive guide

A step-by-step guide to applet deployment.

We've written a few articles on applets before, but compiling our viewer as an applet, or adding your own class to our viewer, is still causing some of our customers grief. None of this is helped by the untimely death of the pack200 Ant extension we've previously advocated. So, to cover these bases completely, here's a step-by-step guide to compiling your own Applet based on our code. Some caveats:

  • The commands are UNIX centric, although hopefully our descriptions of what to do will get you through on other operating systems
  • It's command-line centric. Again, if you prefer to work with an IDE then the descriptions should steer you through.
  • We're assuming you're using ant to build, you have src folder for source, a lib folder for Jars and your viewer will live in the mypackage.pdfviewer package. Every code sample below assumes you're in the parent directory containing src and lib.
  • The end result is going to be a single signed Jar containing our main PDF Library and your own, independent version of our Swing Viewer which you can edit to your hearts content

Let's get started.

First steps

  1. Download the Apache Compress Ant Library task and install ant-compress-1.2.jar and commons-compress-1.2.jar in the lib folder of your Ant installation. Despite Pack200 being a standard part of Java since 1.5 it's never been properly integrated with Apache Ant, and there have been a number of different solutions, workarounds and bodges to get it working. The "Apache Compress" Library referenced above still isn't part of Ant's core, but it is sanctified by the Apache Group and after waiting eight years I guess that's as good as it gets.
  2. Download our PDF Library and unpack the file. Take the bfopdf.jar as well as the other supplementary Jars, and put it in your lib folder.
    cp bfopdf-2.11.24/bfopdf*.jar lib
  3. Download our build.xml, which is a revised version of that found in a previous article.
    wget http://bfo.com/blog/files/build-v2.xml
    

Adding your own features to our applet

The easiest way to work with our applet is to add your own custom features to it. If your new feature is in the mypackage.pdfviewer package and you just want to link it with our applet, all you need to do is create a src/META-INF/services/org.faceless.pdf2.viewer2.ViewerFeature file that lists the class name(s) of your features. Then skip to building and signing below.

Building a completely independent applet

If you want to edit our codebase, rather than just linking in to it, then you're going to need to rename the classes to your own package - doing so will make your life easier, because your classes won't clash with ours, and it will certainly make our lives easier when you email us a stacktrace for debugging. We supply the source code for our viewer with the download package, so here's the steps you need to take to migrate that to your own codebase.

  1. Our download package also contains the source code for the viewer2 package, which is our Swing viewer that you'll be cloning. Copy the source into the src/mypackage/pdfviewer folder of your dev environment:
    mkdir src/mypackage
    cp -r bfopdf-2.11.24/src/viewer2 src/mypackage/pdfviewer
    
  2. Our Jar includes a list of features in the META-INF/services/org.faceless.pdf2.viewer2.ViewerFeature file, which is disovered via service discovery. Your viewer is going to be in a different package, so this file needs to be extracted from our Jar and renamed:
     cd src
     jar xf ../lib/bfopdf.jar META-INF/services
     mv META-INF/services/org.faceless.pdf2.viewer2.ViewerFeature META-INF/services/mypackage.pdfviewer.ViewerFeature
    
  3. If you're working with a version of our package prior to 2.11.25, we didn't include the non-Java resources for the viewer in the "src" folder, so you'll need to extract these from org/faceless/pdf2/viewer2/resources to mypackage/pdfviewer/resources. If you're on 2.11.25 these files were moved as part of step 3 above, so you can skip:
    cd src
    jar xf ../lib/bfopdf.jar org/faceless/pdf2/viewer2/resources
    mv org/faceless/pdf2/viewer2/resources mypackage/pdfviewer
    rm -rf org
    
  4. The source for all our classes is in our org.faceless.pdf2.viewer2 package, but you want to move these to your own mypackage.pdfviewer package. This means updating the "package" and "import" directives in each source file - IDE folk can probably find a menu option to do this, for the rest of us it's time to break out the regular expressions:
    cd src
    sed -i '' 's/org\.faceless\.pdf2\.viewer2/mypackage.pdfviewer/g' mypackage/pdfviewer/*.java mypackage/pdfviewer/*/*.java META-INF/services/*
    
    If you're doing this by hand, don't forget the file in META-INF/services/.
  5. You will need to edit the build.xml file to change the servicefile property. This is the service file containing the list of features which you renamed in step 4 from org.faceless.pdf2.viewer2.ViewerFeature to mypackage.pdfviewer.ViewerFeature - update the property accordingly. You'll also want to exclude our original viewer package from the build, as you're using your own. Add the highlighted line below to the <jar> operation of the build target:
    <jar destfile="${jar}">
      <zipfileset src="${bfopdfjar}">
        <exclude name="${servicedir}/${servicefile}" />
        <exclude name="META-INF/*.RSA" />
        <exclude name="META-INF/*.SF" />
        <exclude name="org/faceless/pdf2/viewer2/**" />
      </zipfileset>
      <fileset dir="build" />
    </jar>
    

Building and Signing

  1. Your build.xml should now be able to run ant build, to compile the code and assemble the classes into the Jar, and now is a good idea to double check this.
    ant build
    
  2. In order to do anything useful with an Applet, it must be signed. We've covered some of this before, but our suggested route these days is to roll everything that's core to your applet - our code, your code, the license - into one Jar, with the additional Jars we supply (for Chinese/Japanese/Korean text, adding new annotations and so on) referenced from JNLP if you need them. In recent versions of the Applet Classloader these Jars must be signed with the same key to avoid warnings or errors. So you need a Keystore. Maybe you have one already, but if not here's a quick way to generate one for testing:
     keytool -genkey -alias myalias -keystore keystore.jks \
      -storepass password -keypass password \
      -dname "cn=me"
    
  3. You may need to edit the build.xml file to tell it where to find your signing key. Change the ks.path, ks.alias and ks.password properties to the location of your KeyStore file and the alias/password for signing. In the example above these would be keystore.jks, myalias and password respectively.
  4. You're now able to sign the Jar with ant sign. This will sign the original Jar, the supplementary Jars, and create a pack200 version of the signed Jar.
    ant sign
  5. Lastly, create the JNLP file from the bfopdf-applet.jnlp included with our download package. You should only need to update the name of the Jar file, and the package name in the package clauses if you've changed that. Add the applet tag to your webpage, making sure to reference the JNLP. An example is below - we assume the Jar files and JNLP file are all in the applet subdirectory.
    <applet code="org.faceless.pdf2.viewer2.PDFViewerApplet" name="pdfapplet" codebase="applet" archive="bfopdf.jar" width="900" height="500">
      <param name="jnlp_href" value="bfopdf-applet.jnlp"/>
      <param name="draggable" value="true"/>
      <param name="swing.defaultlaf" value="javax.swing.plaf.nimbus.NimbusLookAndFeel">
    </applet>
    
    Note that at the time of writing, the separate_jvm parameter for applets results in a gray square instead of the applet on OS X. This is a fault with the Java Plugin in Apple's JVMs since sometime in 1.6, so it will affect all browsers using that plugin. The Oracle Java Plugin is due to replace Apple's but hasn't appeared yet - we will update this section when it does.