Saturday, December 6, 2008

Java Enum relational modelling

As I have blogged about before, the Java Enum is almost perfect. I say almost because of the annoying restriction that its constructor cannot include a relation to itself, due to forward reference limitations when using the Sun compiler. The issue came up again recently when I wanted to model authority roles in Wicket, so I spent a bit more time with the issue and the following is what I've learned. First though, a recap of the original problem. Consider the following attempt at modeling states of a CD player:



public class IlligalForwardReference {
   
enum Player{
       
PLAYING(Player.PAUSED, Player.STOPPED),
       
PAUSED(Player.PLAYING, Player.STOPPED),
       
STOPPED(Player.PLAYING);

       
private  final EnumSet<Player> transitionStates;

       
Player(final Player... states){
           
this.transitionStates = EnumSet.copyOf( Arrays.asList(states));
       
}

       
public EnumSet<Player> getTransitionStates(){
           
return transitionStates;
       
}
   
}

   
public static void main(String... args){
       
System.out.println(Player.PLAYING);
   
}
}


The compiler will complain with:

IlligalForwardReference.java:18: illegal forward reference
PLAYING(Player.PAUSED, Player.STOPPED),
IlligalForwardReference.java:18: illegal forward reference
PLAYING(Player.PAUSED, Player.STOPPED),
IlligalForwardReference.java:19: illegal forward reference
PAUSED(Player.PLAYING, Player.STOPPED),
3 errors
BUILD FAILED (total time: 0 seconds)


This problem comes up as soon as you need to reference an Enum value which has not yet been initialized. Often you can avoid this if no circular relationship exists, by declaring the ones with no relations first and then adding related ones below. That is not an option however in the above example.

Passing ordinals instead
One could imagine then passing in the ordinal value or String representation instead, as a workaround to this limitation. It's less type safe and somewhat reminiscent of what an Enum is supposed to solve in the first place. The following is an attempt at this, using ordinal values:



public class ExceptionInInitializerError {
   
enum Player{
       
PLAYING(1, 2),
       
PAUSED(0, 2),
       
STOPPED(0);

       
private final EnumSet<Player> transitionStates;

       
Player(final int... ordinals){
           
Collection<Player> states = new ArrayList<Player>();

           
for(int ordinal : ordinals)
               
states.add( Player.values()[ordinal]);

           
transitionStates = EnumSet.copyOf(states);
       
}

       
public EnumSet<Player> getTransitionStates(){
           
return transitionStates;
       
}

       
@Override
       
public String toString(){
           
return name() + " -> " + transitionStates;
       
}
   
}

   
public static void main(String... args){
       
System.out.println(Player.PLAYING);
   
}


But unfortunately not. Although the code now compiles, we have effectively just traded the compile time error with a runtime InitializerError:

Exception in thread "main" java.lang.ExceptionInInitializerError
at ExceptionInInitializerError.main(ExceptionInInitializerError.java:42)
Caused by: java.lang.NullPointerException
at ExceptionInInitializerError$Player.values(ExceptionInInitializerError.java:15)
at ExceptionInInitializerError$Player.(ExceptionInInitializerError.java:26)
at ExceptionInInitializerError$Player.(ExceptionInInitializerError.java:16)
... 1 more
Java Result: 1
BUILD SUCCESSFUL (total time: 0 seconds)


That's because we cannot access the ordinals before the Player Enum has been initialized.

Deferred initialization
Hmm ok, but we can defer this step until we actually start querying for transition states. The following is an attempt at this, using String representations this time around:


public class DeferredStringBased {
   
enum Player{
       
PLAYING("PAUSED", "STOPPED"),
       
PAUSED("STOPPED", "PLAYING"),
       
STOPPED("PLAYING");

       
private final String[] transitionStateStrings;
       
private volatile EnumSet<Player> transitionStates;

       
Player(final String... transitionStateStrings){
           
this.transitionStateStrings = transitionStateStrings;
       
}

       
public EnumSet<Player> getTransitionStates(){
           
if(transitionStates == null){
               
synchronized(this) {
                   
if(transitionStates == null){
                       
transitionStates = EnumSet.noneOf(Player.class);
                       
for(String transitionStateString : transitionStateStrings){
                           
transitionStates.add( Player.valueOf(transitionStateString));
                       
}
                   
}
               
}
           
}
           
return transitionStates;
       
}

       
public String toStringWithTransitions(){
           
return toString() + " -> " + getTransitionStates();
       
}
   
}

   
public static void main(String... args){
       
System.out.println(Player.PLAYING.toStringWithTransitions());
       
System.out.println(Player.PAUSED.toStringWithTransitions());
       
System.out.println(Player.STOPPED.toStringWithTransitions());
   
}
}


Halleluja, it's working, we get:

PLAYING -> [PAUSED, STOPPED]
PAUSED -> [PLAYING, STOPPED]
STOPPED -> [PLAYING]
BUILD SUCCESSFUL (total time: 0 seconds)


Note that if you are modelling something where it makes sence to have transitivity (say authority roles where an administrator automatically assumes the role of user, as long as administrator declares a transition to superuser, and superuser declares a transition to user) then in the inner loop of the getTransitionStates() method, add the line:



transitionStates.addAll( Player.valueOf(transitionStateString).getTransitionStates() );



Modelling transitions instead
It was suggested to me in my last entry about the subject, that I could model the transitions instead of the states, as Josh Bloch recommends in Effective Java SE, item 33. This is true, if you do not need symmetric relations (if playing can go to stopped, then stopped must also go to playing) and is fine with adding another layer consisting of the transition states too. By focusing on modelling transitions rather than states (which makes sence in Bloch's phase change example), you arrive at something like this:


public class Bloch{
   
public enum Player {
       
STOPPED, PLAYING, PAUSED;

       
public enum PlayerTransition {
           
STOPFROMPLAYING(PLAYING, STOPPED), PLAYFROMSTOPPED(STOPPED, PLAYING),
           
PAUSEFROMPLAYING(PLAYING, PAUSED), PLAYFROMPAUSED(PAUSED, PLAYING),
           
STOPFROMPAUSED(PAUSED, STOPPED);

           
private final Player src;
           
private final Player dst;

           
PlayerTransition(Player src, Player dst) {
               
this.src = src;
               
this.dst = dst;
           
}

           
private static final Map<Player, Map<Player, PlayerTransition>> m =
                   
new EnumMap<Player, Map<Player, PlayerTransition>>(Player.class);

           
static {
               
for (Player p : Player.values()) {
                   
m.put(p, new EnumMap<Player, PlayerTransition>(Player.class));
               
}
               
for (PlayerTransition trans : PlayerTransition.values()) {
                   
m.get(trans.src).put(trans.dst, trans);
               
}
           
}

           
public static PlayerTransition from(Player src, Player dst) {
               
return m.get(src).get(dst);
           
}
       
}
   
}

   
public static void main(String... args)
   
{
       
System.out.println( Player.PlayerTransition.from(Player.STOPPED, Player.PLAYING) );
       
System.out.println( Player.PlayerTransition.from(Player.PLAYING, Player.PAUSED) );
       
System.out.println( Player.PlayerTransition.from(Player.PAUSED, Player.STOPPED) );
   
}
}


Which yields the output:

PLAYFROMSTOPPED
PAUSEFROMPLAYING
STOPFROMPAUSED
BUILD SUCCESSFUL (total time: 0 seconds)


While I'm sure there are things you can model with this approach, I would much rather be modelling the states rather than the transitions. There are however an idea that comes to mind from seeing this.

Getting rid of the circular dependency
If the whole problem stems from the fact that we cannot have circular relations, then perhaps we can split it in two such that only one relation is modelled statically, while the other is modelled dynamically - but setup statically. It's hard to explain, but take a look at this code:



public class MirrorEnum {
   
public enum TransitionTo{
       
PLAYING, PAUSED, STOPPED;

       
Player state;

       
private void setState(Player state){
           
this.state = state;
       
}

       
private Player getState(){
           
return state;
       
}

       
public enum Player{
           
PLAYING(TransitionTo.STOPPED, TransitionTo.PAUSED),
           
PAUSED(TransitionTo.PLAYING, TransitionTo.STOPPED),
           
STOPPED(TransitionTo.PLAYING);

           
private final EnumSet<TransitionTo> transitionToSet;
           
private volatile EnumSet<Player> transitionStates;

           
Player(final TransitionTo... transitionToArray){
               
this.transitionToSet = EnumSet.copyOf( Arrays.asList( transitionToArray ) );
           
}

           
public EnumSet<Player> getTransitionStates(){
               
if(transitionStates == null){
                   
synchronized(this){
                       
if(transitionStates == null){

                           
// Make sure each Transition state is in sync with each PlayerState
                           for(TransitionTo transition : TransitionTo.values()){
                               
transition.setState( Player.values()[transition.ordinal()]);
                           
}

                           
// Construct TransitionState's by mapping from Transition to PlayerState
                           transitionStates = EnumSet.noneOf(Player.class);
                           
for(TransitionTo transition : this.transitionToSet){
                               
transitionStates.add( transition.getState() );
                           
}
                       
}
                   
}
               
}
               
return transitionStates;
           
}

           
public String toStringWithTransitions(){
               
return toString() + " -> " + getTransitionStates();
           
}
       
}
   
}

   
public static void main(String... args){
       
System.out.println(TransitionTo.Player.PLAYING.toStringWithTransitions());
       
System.out.println(TransitionTo.Player.PAUSED.toStringWithTransitions());
       
System.out.println(TransitionTo.Player.STOPPED.toStringWithTransitions());
   
}
}


When run, the above results in:

PLAYING -> [PAUSED, STOPPED]
PAUSED -> [PLAYING, STOPPED]
STOPPED -> [PLAYING]
BUILD SUCCESSFUL (total time: 0 seconds)


The whole idea is to have two Enum's which mirror eachother yet are distinct to the Java compiler - not unlike when aliasing a column in SQL. You have to deal with two Enum's (Player and TransitionTo) at compile time, but at runtime only Player comes into play. It's not a solution I will actually use however, for that it is too complex and not DRY enough.

Conclusion
In practice I'll go for the ordinal approach when modeling relations. While there is no code completion to be had and only limited type-safety, it stands out as the cleanest and most elegant solution. It's also relatively easy to add some assertions and unit testing to improve on this a bit.

The best would obviously be for Sun to simply fix this in the compiler. The Eclipse compiler have already shown it is possible to do this and make it more lenient. Until then, if you have a more elegant solution to the stated problem, I'd love to hear about it.

Saturday, November 8, 2008

Jasper Reports and legacy formats

I'm not sure when exactly or between which versions, but at some point Jasper Report designs moved from being based on legacy DTD to the more modern XSD schema. It turns out that most tools today, i.e. the iReport plugin for NetBeans, are now overwriting the DTD information of legacy designs in favor of XSD information.
This can cause problems if and when you are not able to change the Jasper engine itself that fills out the template designs. It also did not seem possible to simply plug-in another XML parser and I found it rather hard in general to find information about this and support forums to turn to. So I sought another solution.

If you try to run an XSD based design against the Jasper engine that can only deal with DTD, you will get an error along the line of this:

net.sf.jasperreports.engine.JRException: org.xml.sax.SAXParseException: Document root element "jasperReport", must match DOCTYPE root "null".


By using a legacy designer which favors DTD's (iReport 3.0 standalone) and the recent NetBeans plugin which favors XSD's, it becomes apparent what the exact differences are. Obviously the DTD version has a doctype clause:

<!DOCTYPE jasperReport PUBLIC "//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">


Furthermore, the jasperReport root element has the extra attributtes:

xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd


One could just edit the files manually, but seriously, what fun is that. Instead, we can create a decorator to perform this job on-the-fly as we send the design off to the Jasper engine to be compiled. We would need to do the following:
  1. Read the InputStream into a DOM tree
  2. Add the missing DTD doctype
  3. Remove the undesired attributtes
  4. Write the dom back out to an InputStream
The result of doing the above I've placed in LegacyJasperInputStream.java.

An example on how to use it:


JasperDesign design = JRXmlLoader.load(
       
new LegacyJasperInputStream(new FileInputStream("MyXsdBasedDesign.jrxml"))
       
);



Probably I am not the only one facing this issue, which is why I chose to write a small entry about it and hopefully helping others. Be advised, you're going to need the slf4j logging facility, or modify the source slightly to make use of the standard java.util.Logger.

Friday, November 7, 2008

Congratulations America!

I think it's only in order to congratulate the USA for what has happened. While oddly 47% of Americans still haven't noticed, the last 8 years of an illiterate republican doctrine "if you're not with us, you're against us" has hurt their country incredibly much. As evident right from the beginning to most of us, Bush will go over in history as the worst president of all time - only elected for his second term I suppose due to that fuzzy term called "American patriotism".

The only sad part will be missing someone to laugh at when watching Letterman, The Colbert Report or The Daily Show. Ah for a while at least, we'll always have Gucci Palin. ;)



Best of luck Obama, you sure have your work cut out for you! The future just got a lot brighter in the prospect of regaining an America so many of us used to admire once! With a new dawn near and a fresh breeze blowing from the west, let the healing begin.

Saturday, September 27, 2008

Groupwise 7 on Ubuntu 8.04

While web applications in browsers are continuing to improve, they still can't quite compete with desktop applications. One of the examples of this is the Novell Groupwise client. Running 64bit Ubuntu poses a bit of a challenge, since Novell only offer prepackaged rpm bundles for 32bit Red Hat and Suse systems. Note, some of this stuff is inspired by Scott's blog entry earlier this year, however I could never get his howto to work for me.

Start by downloading the Groupwise client RPM onto a 32bit version of Ubuntu (I use a 32bit image in VirtualBox). I'm not sure from where I found mine, but it's out there if you search a little. You should end up having a file called something like novell-groupwise-gwclient-7.0.3-20080310.i386.rpm

To convert this into a debian package, you are going to need alien. Get this by typing:


sudo apt-get install alien


With alien, you can convert the RPM into DEB by typing:


sudo alien -c novell-groupwise-gwclient-7.0.3-20080310.i386.rpm


Now you're done with the 32bit stuff. Move your novell-groupwise-gwclient_7.0.3-20080310.deb to your target 64 bit box. Before installing the DEB, make sure you have the nessesary dependencies. On my system I needed lib std. C++ 5 (you might need others as well) so I had to do:


sudo apt-get install libstdc++5


Install the Groupwise client itself by issuing:


sudo aptitude install novell-groupwise-gwclient_7.0.3-20080310_i386.deb


You should now have Groupwise installed in the /opt/novell/groupwise folder and shortcuts created on your desktop and in the Ubuntu Internet application menu. Groupwise is actually written (mostly) in Java, which it bundles (in my case the Sun JRE 1.5.0_06-B05). This works fine except if you are using Compiz, but I am so I needed to replace the 32bit slightly old bundles JRE with a newer one. Download a .bin from SUN's site, be sure to only download a JRE (not a JDK) and a 32bit (i586, not a amd64) one. Once downloaded, you can extract it to a folder by running:


sh jre-6u10-rc2-bin-b32-linux-i586-12_sep_2008.bin


Finally, we need to replace the JRE Groupwise is bundled with, with this new one you just extracted into the folder /jre1.6.0_10. So delete the folder /opt/novell/groupwise/client/jre/:


sudo rm -R -f /opt/novell/groupwise/client/jre


Then copy the new JRE in folder jre1.6.0_10/ to /opt/novell/groupwise/client/jre/:


sudo cp jre1.6.0_10 /opt/novell/groupwise/client/jre -R


There you go. That should give you Groupwise 7.03 running on the latest Ubuntu 8.04.

Friday, September 26, 2008

Java 6 update 10 on Ubuntu 8.04

The official Ubuntu 8.04 repositories comes with a slightly outdated version of Java, namely 1.6.0 update 6. If you issue a java -version you can assert this is the case:


Unfortunately, if you are running Compiz, you are likely to then suffer the notorious gray rectangle syndrome as described in 6429775, 6434227 and 6632124 among others. The good news, the problem appears to be fixed in update 10.

Installing the latest JDK
Start by downloading the JDK for your architecture from SUN. Take the .bin file, extract it by running it as a shell script:


sh jdk-6u10-rc2-bin-b32-linux-amd64-12_sep_2008.bin


This will create a new folder called /jdk1.6.0_10. Rename this to java-6-sun-1.6.0.10 (just to remain consistent with how Debian/Ubuntu refers to JDK's) and move this folder to /usr/lib/jvm:


sudo mv jdk1.6.0_10 java-6-sun-1.6.0.10
sudo mv jdk1.6.0_10/ /usr/lib/jvm


Officially you are suppose to use the update-java-alternatives command when using a Debian distro, but frankly I find it easier to do this manually. We need to update the /usr/lib/jvm/.java-6-sun.jinfo, so type:


gksu gedit .java-6-sun.jinfo


This will open up a hidden configuration file. The first line likely shows:


name=java-6-sun-1.6.0.06
...

Simply change this to point to the new version:


name=java-6-sun-1.6.0.10
...


Also, update the java 6 symlink to point to the one we just installed:

sudo rm /usr/lib/jvm/java-6-sun
sudo ln -s /usr/lib/jvm/java-6-sun-1.6.0.10/ /usr/lib/jvm/java-6-sun



This should actually work for most applications, but to be sure I always like to add a few common environment variables. Do this by typing:


gksu gedit $HOME/.bashrc


Scroll down to the end of the file and add two new lines:


export JDK_HOME=/usr/lib/jvm/java-6-sun
export JAVA_HOME=$JDK_HOME


That should do it. Issuing a java -version command now yields the latest version:


Remember the above isn't necessarily the official sanctioned way to do it, but it worked for me. Good luck to you!

Update:
It has come to my attention that Ubuntu 8.04 updates to the JVM overrides/reverts the modifications above. Keep an eye on this, I noticed it when starting seeing Compiz grey-box frames again in NetBeans.


Wednesday, September 3, 2008

@SuppressWarnings completion

Why it was made
Although NetBeans is capable of suggesting and auto-inserting @SuppressWarnings, it doesn't actually provide code completion or documentation for these values. Indeed, as I've blogged about before, it is tricky to track down the exact enumeration and semantics of these magic values. This is due to the fact that they are entirely dependent on the compiler and IDE. This plugin adds support for the values currently supported by NetBeans 6.1, namely "cast", "deprecation", "divzero", "empty-statement", "empty", "fallthrough", "finally", "serial" and "unchecked". It also tries to explain how and when to use them.



How it was made
Creating plugins for NetBeans is relatively easy if you start by grabbing existing code and have invested in the RCP book. Especially blogs like Geertjan's and Sandip's are virtual goldmines. So for this one, I used the Geertjan's blog entry entitled Caret Aware Code Completion. I had some trouble hooking into the Java parser as the tutorial shows, whenever invoking completion, there would be a lag between obtaining the caret position and resolving the current element. I did not figure out why this is, other than for some reason, the mapping from caret position to the underlying AST went out of sync. So instead I ended up relying only on the build-in Java lexer, which did not exhibit the same lag. This means there were a little bit of manual parsing to do, to assert if indeed the context invites for @SuppressWarnings values. The following scenarios had to be accounted for:


@SuppressWarnings(STRING_LITERAL
@SuppressWarnings({STRING_LITERAL
@SuppressWarnings({STRING_LITERAL, STRING_LITERAL
@SuppressWarnings(value=STRING_LITERAL
@SuppressWarnings(value={STRING_LITERAL, STRING_LITERAL



In other words, we have to consume the Java token stream in reverse, from any open of closed STRING_LITERAL, and pass the following finite-state machine:



This is implemented by a simple recursive descent parser, which looks like this. Should you care to use it for your own parsing of a DSL inside Java String tokens, you can download the complete source code here.


Where to get it
There might still be some bugs and perhaps I a missing something, but if you want to give it a spin, you can download the plugin from NetBeans plugin central.

The hardest thing was to gather information regarding the valid values, since it differs from compiler to compiler and the various IDE's. If you do happen to find a bug or something incorrect, please let me know so I can correct it.

Friday, August 29, 2008

Myth: Java widely used in the browser

Yes I aspire to be a MythBuster, don't we all? For a while now there has been some concern over Apple's apparent lack of love for Java. Lately the debate has shifted over to the iPhone and the Java blogosphere is doing its best to send a message to Apple. While I am far from a supporter of the policies and practices of Apple, I do think they are right in determining Java as legacy or an anomaly on the web of 2008.

A sniffing bot
It turns out it's hard to find concrete evidence of how widespread the use of Java client technology is on the internet. What we can do however, is simulate a web user, randomly browsing around and taking note of how often we bump into Java.
The first issue is, how can we come up with truly random URL's? Three different approaches comes to mind:

  1. Use some existing randomizer service

  2. Methodically run through IP ranges

  3. Screen scrape Google results

I actually implemented all 3. Of those both item 2 and 3 potentially raises some ethical questions, consistent scanning of remote systems can be considered a hostile activity as can web scraping. Thus the one presented here uses the 1'st approach only and is quite slow due to the fact that the random function of the site I am using seems to be bound by timestamp data (probably works by hashing the hh:mm:ss and looking up an index in a table). For that reason, and out of politeness, I've inserted a 1 sec delay between each attempt.

The scent of Java
Due to Java's age, there are quite a few ways it can be embedded onto a page depending on both HTML standards and individual browsers. There the APPLET tag, there's the OBJECT tag and then there's the EMBED tag. This should also include the recent iteration of Java client technology, namely JavaFX, since they appear to be reusing the APPLET tag for this - odd actually, considering W3C/HTML4.x deprecated this tag 9 years ago now.

Apart from these tags, JavaScript and frames can further hide the fact that Java technology is being used. This is not very common from what I've experienced though, much more common with Flex and especially Silverlight. Also we need to take into consideration Java Web Start, we do this by capturing links which points at *.jnlp files.

Implementation
Was pretty trivial, thanks to Java Swing's old but readily at hand HTMLEditorKit. The hardest part was coming up with a simple yet functional query language as I did not want to hardwire these rules nor depend on any 3'rd part library. The essence of the markup matching is modeled by an enum (I love Java enum's) and it looks like this:


enum TagMatch{
   
JAVA(// Most Java applets exist inside an APPLET tag
        Tag.create(HTML.Tag.APPLET, Attr.create(HTML.Attribute.CODE)),
        
// Java Web Start will be anchors ending in .jnlp
        Tag.create(HTML.Tag.A, Attr.create(HTML.Attribute.HREF, ".*\\.jnlp$")),
        
// OBJECT and EMBED tags pass in class or jar file
        Tag.create(HTML.Tag.PARAM, Attr.create(HTML.Attribute.VALUE, ".*\\.(class|jar)$"))
   
);
    
   
private List<Tag> tagMatches = new ArrayList<Tag>();

   
TagMatch(Tag... tagVararg){
       
tagMatches = Arrays.asList(tagVararg);
   
}

   
public List<Tag> getTagMatches(){
       
return tagMatches;
   
}
}


If you want to take a look at the whole source file, it's available righ here.

Enough with the talk
The following is the result of scanning 1.000 random sites:


Found 7 sites matching JAVA
+--[www.gailzavala.com/]
+--[www.nationalhealthcouncil.org/]
+--[web2c.com]
+--[armadaweb.com/Anime/Sailor/]
+--[www.gloverpainting.com]
+--[www.theschrodts.com]
+--[members.shaw.ca/micheil/burns/burns.htm]



Well well, 7 sites out of 1000 were identified as using Java on the client tier. I have of course run it a number of times, each resulting in roughly the same numbers somewhere between 5 and 10. This leaves me to conclude that the amount of sites using Java is less than 1%. And how about those that do? If you take a look at some of the links, at least half of them are webcam viewers, scrollers and counters reminiscent of the 90's.

Conclusion
Can't say I was surprised by the result. I am aware of a few things that relies on Java (home banking comes to mind) but then again, I also know of minority sites that use ActiveX. So I still think we can not slam Apple for this one. Certainly iPhone users would be missing Flash much more than Java. Of course, let's hope the new JavaFX stuff and the forthcoming update 10 will pan the way for a renascence.

Update:
People don't seem to like this entry very much. I'm not sure whether its because I am speaking negative about Java in the browser or if I simply didn't convey the message good enough. So to rephrase, this is a simple test to show that Java applets and web start applications are in such low use on websites that to most people (and by inference browser vendors) not having support for these doesn't matter much. Contrast this to the results I get when running another test, this time searching 1000 random sites for for Flash/Flex usage (tags with a SRC attribute containing ".swf"). Such a query results in the following matches:


Found 179 sites matching FLASH
+--[www.countrybumpkin.com.au/]
+--[www.chrissmithphoto.com]
+--[www.bostonbw.com]
+--[www.lipseylipsey.com]
+--[www.zewa.com]
+--[www.webguide.com/attractions.html]
+--[www.socaldelivery.com]
+--[www.lincolnspeedway.com]
+--[www.redcliffslodge.com]
+--[www.baldwin-school.org/]
+--[www.bdainc.com]
+--[www.hydraulicpumps.co.uk/]
+--[www.mainstreetcottonshop.com]
+--[www.yellowstonevacations.com]
+--[www.acmotorcars.com]
+--[www.deadwooddicks.com]
+--[www.judycarter.com]
+--[biz.yahoo.com/n/l/s/sun.html]
+--[www.siasat.com]
+--[www.jackand.co.jp/]
+--[www.meritax.com]
+--[www.westpasco.com]
+--[www.calpers-governance.org/]
+--[www.paradiseranch.com]
+--[www.tamilnaduwomen.org/]
+--[www.dynamitedetail.com]
+--[sportsillustrated.cnn.com/basketball/ncaa/men/teams/oregon/]
+--[www.glenfir.com]
+--[www.marquette.org/]
+--[www.uwwsports.com/index.asp?path=football]
+--[www.sfballoonguy.com]
+--[www.alliedmodular.com]
+--[www.helenelliott.com]
+--[www.nationwidemortgage.com.au/]
+--[www.admissions.umich.edu/]
+--[www.globalcredit.com]
+--[www.godinos.com]
+--[www.leonidas-chocolate.com]
+--[www.01-flash-web-templates.com]
+--[www.gwmcnamara.com]
+--[www.HiDoctor.com]
+--[www.metromayors.org/]
+--[www.vigorita.com]
+--[www.goforth.org/]
+--[www.reitdesign.com]
+--[www.brentlmiller.com]
+--[www.abingdon.com]
+--[www.ravenwoodgolf.com]
+--[www.goldner.com]
+--[www.sarasota.k12.fl.us/brookside/]
+--[www.craigsmithrv.com]
+--[www.labelexpress.com.au/]
+--[www.bagsoflove.co.uk/]
+--[www.var-provence.com/web_anglais/]
+--[www.santafe.cc.fl.us/]
+--[www.terrybryant.com]
+--[www.philanthropy.iupui.edu]
+--[www.kkla.com]
+--[www.iconicimaging.co.uk/]
+--[www.christielodge.com]
+--[www.waynegoodwin.org/]
+--[www.livingdesert.org/]
+--[www.scblues.com]
+--[www.skippinggirl.com]
+--[www.thebusinessgarden.co.uk/]
+--[www.armbrusterrealty.com]
+--[www.butterfly-kisses.com]
+--[www.acceptancefinancial.com]
+--[www.nvhomes.com]
+--[www.gerlinglaw.com]
+--[www.aweuk.com]
+--[www.uintagolf.com]
+--[www.lawinc.com]
+--[www.neteffects.com]
+--[www.coe.ufl.edu/]
+--[www.key2holidays.co.uk/]
+--[www.sachem.k12.ny.us/]
+--[asia.weather.yahoo.com/asia/China/]
+--[www.ecofurn.com.au/]
+--[www.breckenridgeskishop.com]
+--[www.camroadproperties.com]
+--[www.saassociates.com]
+--[www.cabrillo-inn.com]
+--[www.wwf.org.nz/]
+--[www.kenilworthinn.com]
+--[www.marinedivers.com]
+--[wagner.rivals.com]
+--[www.kanales.gr/]
+--[www.tourinfo.ru]
+--[www.krischs.com]
+--[www.visitlaportecounty.com]
+--[www.roninyachtcharters.com]
+--[www.utp.ac.pa/]
+--[www.colmet.com]
+--[www.rolltidebama.com]
+--[www.automotivecad.co.uk/]
+--[www.titancomics.com]
+--[www.breakfastclub80.com]
+--[www.arowanaclub.com]
+--[www.parachuteschool.com]
+--[www.kraft.com.au/]
+--[www.insuranceoffice.com]
+--[www.sbpolo.com]
+--[www.jlg.com/jlg/products/product-center.html]
+--[www.whmis.net/]
+--[www.theharleystreetclinic.com]
+--[www.rimatravel.co.uk/]
+--[www.uppercaperealty.com]
+--[www.cranecare.ltd.uk/]
+--[www.hostingpartners.co.uk/]
+--[www.jawfixer.com]
+--[www.telemoneyworld.com]
+--[www.minaret.vic.edu.au]
+--[www.houstontexaslaw.com]
+--[www.noisepop.com/2003/]
+--[www.crosswind.ms/]
+--[www.sweethomestexas.com]
+--[www.kahaku.go.jp/]
+--[www.keyualcohol.com]
+--[www.sdb.com]
+--[www.fgcswim.org/]
+--[www.quesnelcurlingclub.com]
+--[www.eggsolutions.ca/]
+--[www.msmedia.com.au]
+--[www.revesbypress.com.au/]
+--[www.strategicevents.com]
+--[www.anabenitez.com]
+--[www.ldai.com]
+--[www.c21capital.ca/]
+--[www.olympic.si/]
+--[www.mma.org.my/]
+--[www.markspencer.com]
+--[www.tdfund.com]
+--[www.residence-hotel.com]
+--[www.loanguy.com]
+--[www.middletonmann.freeserve.co.uk/]
+--[www.dallashomesales.com]
+--[www.firstcolorado.com]
+--[www.predatorsoftheheart.com]
+--[www.adtechintl.com]
+--[www.bleibtreu.com]
+--[www.kurt-darla.com]
+--[www.dreamkitchens.com]
+--[www.artsaloft.com]
+--[www.Theoakridgeschool.org/]
+--[www.maharashtratourism.gov.in/]
+--[www.lampson.com]
+--[www.token.com.tw/]
+--[www.panoptic-online.com]
+--[www.cbpacific.com]
+--[www.stricklandchevrolet.com]
+--[www.jamesnewhouselaw.com]
+--[www.vlada.hr/]
+--[www.calc.com]
+--[www.mustakbil.com]
+--[southwestutahfilm.com]
+--[www.sackville.com]
+--[www.bonbonniere.nl/]
+--[www.lizzydesign.com]
+--[www.ambientjobs.com]
+--[www.nola.com/lsu/]
+--[www.greenbergchiropractic.com]
+--[www.donsphoto.com]
+--[sports.espn.go.com/ncf/clubhouse?teamId=324]
+--[www.dangleterrehotel.com]
+--[www.mcsellshomes.com]
+--[www.driversity.com]
+--[www.gbm.net/]
+--[www.ingraham.net]
+--[www.bib-aveiro.rcts.pt]
+--[www.carlos-restaurant.com]
+--[sanbruno.ca.gov/]
+--[www.tufts.edu/as/astronomy/]
+--[www.ci.superior.wi.us/]
+--[www.mitchellrepublic.com]
+--[www.utcc.ac.th/amsar/about/document24.htm]
+--[www.nonchalance.org/]
+--[www.pelicancruiser.com]
+--[www.rayhana.com]



With 179 vs. 7 hits, obviously one must conclude that, at present time at least, having Flash support in the browser matters a great deal more to an average user than having Java support does.

Saturday, August 23, 2008

NetBeans on speed

In the post from yesterday I was a little harsh regarding the performance of recent NetBeans versions, which seems to have taken a toll for the worse. One of the most annoying things is how much scanning and processing is going on all the time which harms responsiveness and the overall user experience. The NetBeans mailing list yielded responses like "more features requires more work" which is fair enough. However, I decided I wanted to have my cake (speed) and eat it too (features).

Memory mapping to the rescue
It's not exactly new. So called "RAM drives" have been used for ages to speed things up, some Linux distro's even mount the temporary files location into RAM to improve performance. We can do the same to our source checkouts to gain massive increase in throughput as well as access time. There are two easy ways to archive this out of the (Linux) box, mount a dedicated RAM drive or use the TMPFS filesystem. Sorry, Windows users will have to look elsewhere for info about how to set up a RAM drive.

RAM drive
In this approach, you allocate a part of the memory and map it to a mount point. The following will create a 64MB RAM disk and let you access it under /media/ramdisk/:

dd if=/dev/zero of=/dev/ram1 bs=4096 count=16k
mke2fs /dev/ram1
mkdir /media/ramdisk
mount /dev/ram1 /media/ramdisk


It turns out that the kernel decides how much memory you can mount like this, on Ubuntu 8.04 the limit is 64MB. You can however change this to your liking by adding a kernel option before it loads:

ramdisk_size= XX


Note however that while you can unmount these again, the physical memory it occupied will not be released to the system. Another limitation of this approach is that whether you need it or not, the RAM disk will permanently occupy whatever you allocated to it - no more and no less. So you should probably only use this approach if you need a small RAM drive or you are just testing the technique out. The only benefit of this approach I can think of, is that your mounted partition will be listed and can be monitored from the system monitor.

Temporary filesystem
TMPFS is rather different than the traditional block oriented RAM disk. It turns out it's actually even easier to deal with, just try this:

mkdir /media/ramdisk
mount tmpfs /media/ramdisk -t tmpfs


Not only is it easier, it's much more flexible. As you no doubt noticed, we did not actually specify any capacity. TMPFS will use half of your RAM as default. Although you can also specify it directly, there's really no need to since TMPFS is not only able to dynamically decrease but also increase its capacity on demand. This means that if you do not use the partition, you won't pay any memory penalty. Apparently TMPFS is even able to use swap file so that IF you should go overboard in space consumption, you won't suffer loss of data or the like.

You can have your system automatically create and mount a TMPFS partition for you, by modifying your /etc/fstab file. Simply add the following to it:

none /media/ramdisk tmpfs defaults,user,size=1G,mode=0777 0 0


Now I don't actually want to run into scenarios where my system has to start using virtual memory, so I limited the size of the temporary partition to 1GB, that leaves me with at least 3GB for the OS and applications.

Applied to a NetBeans project
It's rather hard to measure and express something as subjective as responsiveness and general feeling of an IDE with and without a RAM drive. Suffices to say it feels like it's running at a blazing speed! Nodes expand at once, tabs activate immediately and it's just an overall impressive experience - it feels like you're back to writing C code rather than Java. Let me try and prove this to you the best I can with just static images. The below screen shots both show the result of running the same simple Ant build target on a small NetBeans module project, first on a normal disk drive and then from of a RAM drive.



It took 10 sec to build on my 10.000rpm VelociRaptor disc, the fastest desktop drive available today. An average disc would require around 12-13 sec I suspect.


Running from of the RAM drive it took just 1 sec, an amazing 10x faster. It's the kind of thing you have to see in order to believe.

I also tried placing NetBeans itself as well as its user directory directly on the RAM drive, but without noticeable difference at run time. It did start up 40-50% faster and became responsive right away, but this is probably not an optimization worth going for.


Applied in practice
So there you have it. No magic involved, all you need is enough RAM which is dirt cheap* and some consistent working habits. You do have to manually copy your project onto the RAM drive as well as remember to move it to a non-volatile partition before you shut your computer down. However given the fact that we usually always work out of CVS/SVN/HG this should not be much of an issue, in fact you could claim it encourages healthier check-in habits.

As a cautionary measure though, I have invested in a cheap UPS in case of power loss and when I shut down my system, a script will automatically copy the content of the RAM drive to a persistent backup partition. For convenience, when I start up, another script will automatically copy the most recent backup back onto to the RAM drive. Thus, rather than having to do a full checkout/clone in the morning, I only have to issue an update. If you really wanted to minimize risks, you could also have a timed cron job or a low-priority synchronization deamon running.

In conclusion
Of course, this technique really doesn't have anything to do with NetBeans per se, so any IDE would benefit from it as well as freeform/Ant/Maven projects. If you feel uneasy about working entirely in volatile memory, I suppose it would be possible to simply modify your build scripts such that only the build artifacts are emitted this way, leaving the actual source code intact on a fully journaled non-volatile file system.

I had fun setting this up, though I am somewhat surprised of how I/O-bound NetBeans appears to be. In a related exercise with Visual Studio and C# a few years back I did not experience anywhere near this speed-up, possibly because the compiler and tool chain is multi-threaded unlike javac and Ant.
So go ahead and give it a try, you might like it too. I obviously take no responsibility for whatever data loss you may experience yada yada. You have been warned!

Update:
Someone asked me how to put the automatic copy mechanism I mentioned above into effect. We are going to need 2 scripts as well as installing these at the appropriate life cycle hooks of Linux. In the following I assume you have created a directory at /ramdisk_archives with root access permissions only and that you are using a Debian derived distro.

Put the below script in a file called ramdisk_to_archive.sh, give it execute rights (chmod +x ramdisk_to_archive.sh) and save it under the /etc/init.d/ directory which is usually where service scripts are put.


#!/bin/sh
#/etc/init.d/ramdisk_to_archive.sh
a=$(date +%T-%d-%m-%Y)

mkdir /ramdisk_archives/$a
cp -i -p -R /media/ramdisk/* /ramdisk_archives/$a
rm /ramdisk_archives/latest
ln -s /ramdisk_archives/$a /ramdisk_archives/latest



This script will create a new subdirectory under /ramdisk_archives based on the current timestamp and copy all content of the RAM disk (with file permissions intact), to this newly created folder. Then it will delete any eventual existing link called latest and finally, it will create this new link again and point to the directory we just created. Finally, you need to install it so it runs at system halt (runlevel 0) as well as reboot (runlevel 6) which you can do by typing this:


ln -s /etc/init.d/ramdisk_to_archive.sh /etc/rc0.d/ramdisk_to_archive.sh
ln -s /etc/init.d/ramdisk_to_archive.sh /etc/rc6.d/ramdisk_to_archive.sh



Next time you reboot or shut down your system, the script above will be run, thus saving the content of the RAM disk onto /ramdisk_archives/ somewhere. Now, to have your RAM disk restored again at boot time, write another script:


#!/bin/sh
#/etc/init.d/ramdisk_to_archive.sh
cp -i -p -R /ramdisk_archives/latest/* /media/ramdisk/



Again, remember to give it execute permission and copy it to the services directory like the prior script. This time we're going to hook into all the other remaining runlevels 2-5. To do this, issue the following command:


update-rc.d /etc/init.d/ramdisk_to_archive.sh defaults



That's it. Remember to clean up the /ramdisk_archive folder from time to time as there's no purge or rotation scheme in place. You are of course free to add this yourself, if you do, drop me a line. :)

*You can have 4GB DDR2 for less than US$ 100. Even older consumer motherboards supports 4GB, newer ones 16-32GB.

Wednesday, August 20, 2008

NetBeans: Pretty Much Unusable

NetBeans Pretty Much Unusable
If you care about NetBeans and its performance, an interesting development occurred over the last 24h on the nbusers mailinglist. It seems the upcoming 6.5 release sparked a debate claiming that it's pretty much unusable. While I wouldn't go that far, there is actually some truth to the matter as experienced by my colleagues and I.

A little bit of history
I've been back and forth between various IDE's over the years, coming from Visual Studio (VB/C/C#) I've been rather spoiled in regard to syntax highlighting, code completion and debugging. In fact I remember back in 2001 how I was convinced into doing a college project in Java, primarily because of the assistance offered by Visual Studio J++. At that time, no tool provided these kind of features (although Forte and JBuilder tried).
In this day and age, things looks quite different of course. SUN finally realized that it's worth having great tools (some would argue they have to solve the problems at the tooling level, since the language has effectively gone stale). In any event, todays Java developers can rely on pretty much the same features as Visual Studio. However, for this there still is a price to pay.

The paradox of choice
Having choices is generally a positive thing. But when it comes to software, having too many choices quickly becomes a burden - less is sometimes more. The problem with having multiple IDE's is that, apart from handling of java source files, they aren't really compatible. They all use each their own build system, supports different layout managers etc. so in reality you can't just switch from one IDE to the other. Given the love for committees in the Java space, I do not understand why a project standard was never established through a JSR. In light of this, it becomes rather important you and your project team settle on one IDE that hopefully satisfies everyone.

We chose NetBeans
After initially having used Oracle's JDeveloper for a number of years, we got fed up with the complex stack that Oracle pushed, as well as the semi-yearly releases. We had to live with bugs for a very long time and newer versions of the IDE could never really cope with migrating from the existing code base.
We chose NetBeans 5.5 after having had some success with Swing UI work (Matisse) as well as noticing how SUN were pushing for standards as well as de-facto standards such as Ant etc. Swing now also seemed to be better able to deliver a UI experience where you did not curse constantly at the lack of responsiveness or mismatched L&F. The best part of NetBeans is probably how we constantly get new versions (release, beta, milestone or daily) as well as the plugin community around it. However, not everything is rosy.

Why we might not stay
Since SUN decided to focus their attention on a polyglot of languages, performance seems to be hurting. Java and Swing always felt slow and overly complex to me... but that's subject for another blog post. In NetBeans 6.0 it really did get worse though. Often you would experience lag as you were typing, clicking tab or invoking a menu. It may very well only be 200-300ms but it is enough to be perceived as annoying. I believe it was in 6.1 where they introduced massive Action lazy-loading, with the end result that after NetBeans had started, the whole UI was effectively still unusable for 3-5 seconds.

Having tried 6.5 beta, I can't say I've experienced the gain in speed the new compile-on-save feature promised. Instead, NetBeans feels* even more busy and hence, less responsive to what I am actually trying having it do. I hope this will be sorted out because otherwise I might be compelled to try and move on to Eclipse or IntelliJ. The former always did run fast and smooth but I never really committed to it fully. The latter I have absolutely no idea about, except I keep hearing it's the best.

Is there a point to this?
No not really, apart from documenting the dilemma of choice and warn about the negative developing performance trend of NetBeans. I love NetBeans and the potential I've seen over the last year but productivity comes first. For SUN's sake I hope it will all be resolved, seeing as NetBeans 6.5 is now in beta 2, I doubt it will happen in the 6.5 time frame though.

*I should probably note that I by no means rely on slow hardware (Core II 2.66GHz/4GB Ram/10.000rpm HD) nor outdated software (Ubuntu 8.04 64bit with Sun's Java6u10rc JDK).

Friday, August 15, 2008

JSF and binary content

So I have been learning JSF and JPA at work recently. While I can't say those are the most exciting technologies to me, at least they appear to be (or become) de-facto standards and that means more leverage over the long term (something not always the case when dealing with Java technologies). Using JSF with Facelets can be a little challenging, the tools are only so-so and frankly it bugs me to have to type so much type-unsafe stuff in XML, EL and annotations. Anyway, what this blog entry is about is how you can bridge the gab between JSF page rendering and binary content delivery.

JSF and PDF
Corporate applications often need to deliver a lot of PDF content, and it is no different where I work. Static PDF documents as well as dynamic ones. Even if frames and iframes are generally frowned upon these days, I find then to be the best way to embed PDF documents inside the browser. However, with JSF you can't just make an xhtml page and expect to be able to stream binary content from it, since the HTTP protocol only allows you to send the header once. Indeed, JSF contains assertions to try and catch this and will complain:


java.lang.IllegalStateException: getWriter() has already been called for this response


You can always use an ActionListener or even an accessor on the backing bean if you just want a link to download a PDF, but embedding one inside a frame or iframe is an entirely different matter. I tried many things, most of them felt like fragile hacks. I will now present the best solution in my opinion, it's generic, it's KISS and it solves the problem.

Back to basics: HttpServlet

It turns out that it is possible, inside a processRequest method of a vanilla HttpServlet, to access session scoped backing beans. This means we just need some way of telling the Servlet about where it can uptain the binary data from and various other meta-data. So I came up with the following simple "API" using HTTP GET parameters:


ServletName?BackingBeanName.MethodName[;ContentType][;FileName]


The Servlet then has to do the following:
  1. Parse and validate parameters
  2. Locate the specified backing bean and call the method to uptain the binary content
  3. Stream the result back to the browser using the specified parameters
The complete source code for the Java Servlet that perform this job, you can get here. To use it, you would add it to your web.xml configuration and then simply call it from your JSF or Facelet files like this:



<iframe name ="frame" scrolling="auto" align="center"
src="BinaryJSFServlet?myBackingBean.createPDF;application/pdf" width="100%" height="700" >
</iframe>




This way, you can still keep your state and logic in the backing beans without having to mess around with JavaScript, ActionListeners or PhaseListeners. Just generate your URL in the *.jsp or *.xhtml file such that it specifies a backing bean, a method name to invoke as well as optional MIME type and file name.

Update:
Tim Boudreau mentions a cool technique the code above could make good use of, by relying on a general key/value parser based on dynamic proxies.

Tuesday, July 1, 2008

The Enum is perfect... well almost.

Ok so its time to admit to something. I'm deeply in love with Java's Enum! In my opinion it was the best part of Java 5 and I still wonder why it took Sun 10 years to add this powerful static modeling construct.

The power of the Enum
Although there certainly are known limitations of the Enum, i.e. how you can't extend it, I have never run into a practical limitation myself. Until now that is. The issue I want to raise is that of Enum forward referencing.

The great Alan Turing taught us that at the end of the day, everything can be modeled by a Turing Machine and finite automata. We may not often consciously operate at this level, but many things still makes sense to model this way being it a regular expression matcher, navigation rules or similar. The Java Enum appears to be a perfectly simple, fast and type-safe way of modeling this... or does it?

State machine with an Enum
Since the Java Enum effectively is just a group of static class instances, it allows us to attach methods and state to each static instance. We should be able to use this fact to express a transitive relationship with outself. To model a CD player, we'd write something like this:



public enum PlayerState {
STOPPED(PlayerState.PLAYING),
PLAYING(PlayerState.STOPPED, PlayerState.PAUSED),
PAUSED(PlayerState.PLAYING, PlayerState.STOPPED);

private final EnumSet<PlayerState> transitions;

PlayerState(final PlayerState... transitions){
this.transitions = EnumSet.copyOf( Arrays.asList(transitions));
}

// State transition and query logic below...
}


Except that won't compile! The compiler complains with an "illegal forward reference" error. I can understand why it's the easiest to simply not allow this circular use case, but considering everything else in Java allows forward referencing (i.e. we don't need header files as in C++) it would've been nice if that was the case with Enum's too.
The JLS simply states:
"It is a compile-time error for the constructors, instance initializer blocks, or instance variable initializer expressions of an enum constant e to refer to itself or to an enum constant of the same type that is declared to the right of e"
Granted, it has been some years since my last compiler course but I can't see any reason not to allow forward referencing in this particular case where everything is known. I suspect the culprit simply being that Enum, as with so many other things, was added later using only existing features such as not to change existing initialization rules of the JVM.

Wednesday, May 14, 2008

NetBeans plugin: Special copy/paste

The very little I've had to paste code into this fairly new blog, was enough to give me the idea of creating an extension to NetBeans which would help to remedy this formatting nightmare. Now, in my world, there's approximately a 100:1 ratio between idea and actual realization, so I'd have to salute the awesome OpenIDE API's for making this possible spending just a few late nights and taking just 200 lines of code. Without further ado, I present, my first NetBeans plugin.


Special copy/paste
The plugin will add a couple of new actions to the context-sensitive popup-menu of the source editor, which will allow you to copy the selected text as preformatted HTML, as well as a CSS version which will preserve the formatting used in NetBeans.



Using the "Copy as HTML and CSS" menu as in the above screen dump will result in you being able to paste it directly into a website/wiki/blog and have it display like this:


/**
* @param args the command line arguments
*/

public static void main(String[] args) {
// TODO code application logic here
}



For a larger example of what the output of the plugin looks like, take a look at the plugin's own source code.

It's supposed to be language agnostic, in that it should work with all types of sources NetBeans is able to tokenize. The plugin will try to mimic the formatting of the tokens in regard to font family, keyword and literal colors. It will however *not* be able to show member-wide variables in green as NetBeans does nor show method and class decelerations in bold. This is a limitation caused by the fact that I am only using the NetBeans lexer API rather than doing any kind of semantic analysis or parsing.

If you want to give the plugin a try, download it from NetBean's Plugin Portal. Source code is available here.


Update:
It has been made clear to me through the comments, that NetBeans already contains functionality to extract source code as HTML. You can find the feature in the "File" menu under the ill named item "Print to HTML". Since it would appear other people than just me did not associate "print" with that of exporting HTML content, I have filed an issue for the relocation and/or renaming of this.

Friday, May 9, 2008

Keurig Platinum B70

Time to try to live more up to the name of this blog. As most developers, I have a passion for coffee and coke and consume it in quantities which are probably not on the healthy side. Anyway, when I saw the Keurig Platinum B70 coffee maker in action, I absolutely had to own one. Why? Take a look below.



Yup, it takes 45 sec. for it to brew a large cup (4 sizes to choose from) of fresh programmer oil, has a very gadgety blue light and is ready to serve its master 24/7. It probably isn't the most economical way to get your caffeine dose, but it does allow for a small filter so that you can use normal coffee beans rather than the convenient K-cups. Also, it came with samples of Van Houtte's Kenyan Kilimandjaro roast, so now I know why Stephen Colebourne named his OpenJDK sandbox Kijaro. ;)