Monday, August 18, 2008

StringArt Scala Applet

After writing a Scala application from scratch and converting a Java application over to Scala, I wanted to see how easy it would be to write a Scala applet. It turned out to be very easy.

For my applet, I selected a small project I had wanted to do for a long time: a string art drawing program. My StringArt program is a simple applet that recreates an art form I did as a little kid: pound a bunch of nails into a board in a simple pattern, then stretch pieces of string between the nails. In order to allow maximum flexibility, I wanted to allow the user to enter functions describing the endpoints of the lines. Scala made this particularly easy to do with Parser Combinators in the standard library, but that's a different issue.

Creating an applet in Scala was exactly like creating an applet in Java with the exception that I needed to provide access to the standard Scala library. There were thus three steps involved:
  1. Create a suitable HTML file.
  2. Write the Scala code.
  3. Provide access to the Scala library.

The HTML File

Setting up an applet requires adding an <applet> element to an HTML file. Here's the <applet> element as used in the HTML file for my StringArt applet:
  Sorry, this browser does not understand applets or they are not enabled.
As with any applet, the "code" attribute specifies the name of the main class in the applet, and the "archive" attribute specifies the jar file containing the applet code. In this case the "archive" attribute additionally specifies a second jar file that contains the standard Scala library classes used by my applet. More on this below.

The Scala Code

My main applet class extends javax.swing.JApplet. I chose to implement the body of the applet in a separate class (StringArt) that extends JPanel to make it simpler to use it later as a standalone app.

Here is the main applet class:
package net.jimmc.stringart

import javax.swing.JApplet

class Main extends JApplet {
    val sa = new StringArt()

    override def init() {
        val pane = getContentPane()
        pane.add("Center", sa)
The StringArt class and everything from there on down uses standard Swing components. My Main applet class also includes the standard applet start() and stop() methods, but they don't do anything other than print out a status message, so I did not include them in the above listing.

The Scala Library

When I first started working on my StringArt applet, the "archive" attribute in my <applet> entity referenced the standard Scala library jar file, scala-library.jar. But that jar file is 3.3MB, and my little applet jar file was only 200KB, so the initial download of the applet was much slower than it needed to be. To improve on this situation, I created a custom version of the Scala library jar file that contained only the classes used by my applet. I had come across a Minesweeper applet written in Scala that used this idea. It referenced a scala-minimal.jar that was only 220KB, but the author did not say how he created it.

I posed the question to the Scala mailing list and was directed to the free program ProGuard that, among many other things, can remove unused classes from jar files. My build.xml file for the StringArt applet now includes a target that produces my minimal Scala library jar file with this ant command:
    <java   jar="${proguard.jar}"
        <arg line="-injars ${stringart.jar}"/>
        <arg line="-outjars ${stringart.jar}.ignore"/>
        <arg line="-injars ${scala.library.jar}"/>
        <arg line="-outjars ${scala-lib-stringart.jar}"/>
        <arg line="-libraryjars ${java-rt.jar}"/>
        <arg line="-keep public class ${main.class}"/>
        <arg line="-keep public class scala.ScalaObject"/>
        <arg line="-keep class net.jimmc.**"/>
        <arg line="-keep public class java.**"/>
        <arg line="-keep public class javax.**"/>
        <arg line="-keepnames class **"/>
        <arg line="-dontoptimize -dontobfuscate -dontpreverify"/>
After building my applet stringart.jar file, I run the above command, throw away the processed stringart.jar.ignore file and use the processed scala-library.jar file as my scala-lib-stringart.jar file. The reduced library is under 290KB, less than 1/10th the size of the standard scala-library.jar. The total jar file download size (stringart.jar plus scala-lib-stringart.jar) is about 500KB, rather than 3.5MB as it was with the complete scala-library.jar, so there is only 1/7th as much data to download to be able to run the applet.

Updated 2009-01-07: added links to initial bullet list, changed Parser Combinator link to point to my blog entry.

1 comment:

philho said...

Aha! It was one of my worries, to load a 3MB library (be it Groovy, Scala, Fan or any other) just to run a small applet. 3MB isn't that much on today's Internet, but still a bit of overkill, and people get quickly bored of waiting...

Using ProGuard to slim down the library is just brilliant.

Now, I wonder if that's such a good idea... :-) I mean, if I make several applets, if I used standard library, it should be shared / cached by Java, right? While custom libraries will be different each time, so always have to be downloaded.
Now, it is too easy to reset the cache, and it takes 10 applets to balance the advantage, so it is a bit of void concern... ;-)

Anyway, thanks for sharing the tip.