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:
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:<applet code="net.jimmc.stringart.Main" archive="stringart.jar,scala-lib-stringart.jar" width="660" height="500" > Sorry, this browser does not understand applets or they are not enabled. </applet>
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 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}" fork="true" failonerror="true" > <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"/> </java>
Updated 2009-01-07: added links to initial bullet list, changed Parser Combinator link to point to my blog entry.
1 comment:
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.
Post a Comment