<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-27876765</id><updated>2012-01-19T13:23:47.126+01:00</updated><category term='radiant'/><category term='ruby'/><category term='programming philosophy'/><category term='portlets'/><category term='scala'/><category term='javascript'/><category term='java'/><category term='swing'/><category term='mule'/><category term='ajax'/><category term='camping'/><category term='monitoring'/><category term='conference'/><category term='open source'/><category term='concurrency'/><category term='networking'/><category term='radiant-postfix-extension'/><category term='wicket'/><category term='opinion'/><category term='unix'/><category term='spring'/><category term='rails'/><category term='mac'/><category term='book review'/><category term='fun'/><category term='jruby'/><category term='testing'/><category term='version99'/><title type='text'>Day to day stuff</title><subtitle type='html'>Experiences from a hard core JVM programmer. Likes to keep things simple.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default?start-index=101&amp;max-results=100'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>108</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-27876765.post-7557209537817749164</id><published>2012-01-16T09:38:00.003+01:00</published><updated>2012-01-16T09:50:01.117+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><title type='text'>X forwarding as root</title><content type='html'>&lt;p&gt;Often is useful to run jconsole on a remote (production) machine. One basically has 2 options to do so. First, you can pierce your firewall and let your application listen to the appropriate JMX and RMI ports. This however always tricky (in particular the RMI). In addition, creating the connection string that jconsole accepts is not nice at all.&lt;/p&gt;

&lt;p&gt;The other option, is to do X forwarding. I'll describe here how to do this on MacOS X, with jconsole on any Unix server with, ssh-server, no root password and sudo installed.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start X11 (you'll need to install it from the installation CDs).&lt;/li&gt;
&lt;li&gt;Start iterm2.&lt;/li&gt;
&lt;li&gt;Login to the target system with &lt;code&gt;ssh -X remote_user@system&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Switch to root with &lt;code&gt;sudo -s&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Get X authorities with &lt;code&gt;xauth merge ~remote_user/.Xauthority&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Start &lt;code&gt;jconsole&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-7557209537817749164?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/7557209537817749164/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2012/01/x-forwarding-as-root.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7557209537817749164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7557209537817749164'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2012/01/x-forwarding-as-root.html' title='X forwarding as root'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-3383054662335388053</id><published>2011-12-13T12:03:00.004+01:00</published><updated>2011-12-13T13:57:50.807+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Breaking a Java HashSet</title><content type='html'>&lt;p&gt;Can it be?&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;Set&lt;String&gt; set1 = new HashSet&lt;String&gt;(5);
Set&lt;String&gt; set2 = new HashSet&lt;String&gt;(5);
// add of bunch of strings to both sets
assert set1.equals(set2) == false;
&lt;/code&gt;&lt;/div&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Yes it can!&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;We actually had this problem in an integration test. The cause was that the strings to one set were added concurrently. Interestingly, the sets &lt;span style="font-style:italic;"&gt;seemed&lt;/span&gt; to be the same, when printed they contained the same strings, just in a different order (which is also interesting). However, the internals were apparently so damaged that an equals invocation return false.&lt;/p&gt;

&lt;p&gt;We replaced the HashSet with a ConcurrentSkipSet, and all was fine again.&lt;/p&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Conclusion&lt;/span&gt;: Okay, so this is a boring conclusion, but just be careful with using normal collections in concurrent situations.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-3383054662335388053?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/3383054662335388053/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2011/12/breaking-java-hashset.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3383054662335388053'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3383054662335388053'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2011/12/breaking-java-hashset.html' title='Breaking a Java HashSet'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-6988679067923977176</id><published>2011-08-15T20:14:00.007+02:00</published><updated>2011-08-15T20:54:39.066+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='version99'/><title type='text'>DNS for Version 99 is off-line</title><content type='html'>&lt;p&gt;To alleviate some pain with using the commons-logging framework, I created &lt;a href="http://day-to-day-stuff.blogspot.com/2007/10/announcement-version-99-does-not-exist.html"&gt;version 99&lt;/a&gt;. It was hosted at the hostname &lt;tt&gt;no-commons-logging.zapto.org&lt;/tt&gt; courtesy of no-ip.com. Unfortunately, due to lack of traffic I had to affirm usage of the  the hostname once per month. Though this was slightly annoying, a complete pain is that I missed the last deadline, and that recreation is out as dashes in hostnames are no longer allowed.&lt;/p&gt;

&lt;p&gt;As of 2011-08-15 16:11:00 GMT no-ip did credit to its name; no ip for &lt;tt&gt;no-commons-logging.zapto.org&lt;/tt&gt;.&lt;/p&gt;

&lt;p&gt;As 1) the current situation is disruptive for any version-99 user anyway, 2) there is &lt;a href="http://www.slf4j.org/faq.html#excludingJCL"&gt;a better workaround&lt;/a&gt; using the &lt;tt&gt;provided&lt;/tt&gt; scope, I decided to not put back version 99 online under another hostname.&lt;/p&gt;

&lt;p&gt;If you really need to use version 99 for some more time, just add an entry to your &lt;tt&gt;/etc/hosts&lt;/tt&gt; file:&lt;/p&gt;&lt;div class="codeblock"&gt;&lt;code class=""&gt;83.163.41.27 no-commons-logging.zapto.org&lt;/code&gt;&lt;/div&gt;

&lt;p&gt;As promised, I'll keep version 99 for 5 years, that is until October 2012. Please check back here for IP address changes.&lt;p/&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-6988679067923977176?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/6988679067923977176/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2011/08/version-99-dns-off-line.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6988679067923977176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6988679067923977176'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2011/08/version-99-dns-off-line.html' title='DNS for Version 99 is off-line'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-2826013984759313459</id><published>2011-06-16T12:26:00.004+02:00</published><updated>2011-06-18T20:31:22.542+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Lock-less singleton pattern</title><content type='html'>&lt;p&gt;Although the singleton pattern is now know as an anti-pattern, I think it still is a valid choice when you only need one instance &lt;span style="font-style:italic;"&gt;in a particular context&lt;/span&gt;. Anyway, in Java there are several ways to implement a singleton.&lt;/p&gt;

&lt;p&gt;For example this one uses a synchronized block:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;private Singleton singleton = null;

protected Singleton createSingleton() {
  synchronized (this) {  // locking on 'this' for simplicity
    if (singleton == null) {
      singleton = new Singleton();
    }
    return singleton;
  }
}&lt;/code&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;To prevent locking all the time you can use the double-check idiom on a modern JVM. But I just thought about another implementation that switches locking for preventing code re-ordering, making it a &lt;span style="font-style:italic;"&gt;lock-less&lt;/span&gt; implementation:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;private AtomicReference&lt;Singleton&gt; singletonRef = new AtomicReference(null);

protected Singleton createSingleton() {
  Singleton singleton = singletonRef.get();
  if (singleton == null) {
    singleton = new Singleton();
    if (!singletonRef.weakCompareAndSet(null, singleton)) {
      singleton = singletonRef.get();
    }
  }
  return singleton;
}&lt;/code&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;There is only one catch to this implementation: although &lt;tt&gt;createSingleton()&lt;/tt&gt; will always return the same instance, it must be allowed to call the constructor of Singleton twice without side-effects.&lt;/p&gt;

&lt;p&gt;&lt;span style="font-style:italic;"&gt;Update 2011-06-18&lt;/span&gt;: changed &lt;tt&gt;compareAndSet&lt;/tt&gt; to &lt;tt&gt;weakCompareAndSet&lt;/tt&gt; as suggested by Adriano.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-2826013984759313459?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/2826013984759313459/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2011/06/lock-less-singleton-pattern.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2826013984759313459'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2826013984759313459'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2011/06/lock-less-singleton-pattern.html' title='Lock-less singleton pattern'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-7288074598967722200</id><published>2011-05-19T21:35:00.004+02:00</published><updated>2011-05-19T21:46:49.768+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='opinion'/><category scheme='http://www.blogger.com/atom/ns#' term='book review'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Apache Wicket Cookbook — book review</title><content type='html'>&lt;p&gt;Some time ago I reviewed the drafts of the new book from Wicket rockstar programmer Igor Vaynberg: &lt;a href="http://www.packtpub.com/apache-wicket-cookbook/book"&gt;Apache Wicket Cookbook&lt;/a&gt;. If you are serious about using Wicket, this book is for you. It is fast, to the point, has very clear code samples and teaches you all the relevant (both clean and dirty) stuff you need and which &lt;a href="http://wicketinaction.com/"&gt;Wicket in Action&lt;/a&gt; could not cover.&lt;/p&gt;

&lt;p&gt;Conclusion: this is &lt;span style="font-style:italic;"&gt;the&lt;/span&gt; book to read after 'Wicket in Action'.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-7288074598967722200?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/7288074598967722200/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2011/05/apache-wicket-cookbook-book-review.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7288074598967722200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7288074598967722200'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2011/05/apache-wicket-cookbook-book-review.html' title='Apache Wicket Cookbook — book review'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-798289589743505068</id><published>2011-03-31T13:56:00.003+02:00</published><updated>2011-03-31T14:25:25.632+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>Virtualizing a PC</title><content type='html'>&lt;p&gt;All hardware is eventually decommissioned. It may be broken, stolen, or just too old and slow. However, the software on it might still be needed. Not all software is easily transferable to a new PC with the latest OS on it. This quick guide helps you convert the old machine to a virtual machine, so that you can run it in &lt;a href="http://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt;. The process is called physical to virtual (P2V).&lt;/p&gt;

&lt;p&gt;This article assumes you are at home in a Linux shell.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Boot the old hardware from a Ubuntu live CD or USB stick (or any other live cd, for example the gparted cd).&lt;/li&gt;
&lt;li&gt;Prepare copying each file system with the following steps:
  &lt;ol&gt;
  &lt;li&gt;Mount the filesystem&lt;/li&gt;
  &lt;li&gt;Remove the contents of /tmp and trashcans in the home directories.&lt;/li&gt;
  &lt;li&gt;Clear free space with the following commands:
&lt;div class="codeblock"&gt;&lt;code class="shell"&gt;sudo -s
dd if=/dev/zero of=/usr/bigfile; rm -f /usr/bigfile&lt;/code&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/li&gt;
  &lt;/ol&gt;
&lt;li&gt;Now copy each physical disk to the target host computer (you could also do this per partition). With nc (netcat) and dd this is easy. Netcat essentially opens a pipe between two computers. We then use dd to stream the entire disk to and from the pipe. One of the two netcat's are in listening mode, the other connects to that one. It doesn't matter which one does the listening, except I found that netcat in MacOSX does not properly support listening. To speed up the transfer, gzip is used. Because we cleared empty sections of the disk, gzip is a quite efficient addition.&lt;br/&gt;
Here are the two examples of copying the disk &lt;code&gt;/dev/sda&lt;/code&gt;. The first puts netcat in listen mode on the target, the second on the source system (execute in the given order):
&lt;div class="codeblock"&gt;&lt;code class="shell"&gt;on target host: nc -l 19001 | gzip -d -c | dd of=hd-copy.raw
on source machine: dd if=/dev/sda | gzip -c --fast | nc &lt;target&gt; 19001&lt;/code&gt;&lt;/div&gt;

&lt;div class="codeblock"&gt;&lt;code class="shell"&gt;on source machine: dd if=/dev/sda | gzip -c --fast | nc -l 19001
on target host: nc &lt;source&gt; 19001 | gzip -d -c | dd of=hd-copy.raw&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;Make sure you have a decent network connection, 1 Gbit/s is fine.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;The next step is to convert the raw image to a VirtualBox image. Do this with the following command (tested with VirtualBox 4.0.4):
&lt;div class="codeblock"&gt;&lt;code class="shell"&gt;VBoxManage convertfromraw hd-copy.raw hd-copy.vdi --variant Standard&lt;/code&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;If there really was a lot of empty space on the disk, you can compact it with the following command:
&lt;div class="codeblock"&gt;&lt;code class="shell"&gt;VBoxManage modifyhd hd-copy.vdi --compact&lt;/code&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;References&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://www.virtualbox.org/manual/ch08.html"&gt;Chapter 8 of the VirtualBox manual&lt;/a&gt;&lt;br/&gt;&lt;a href="http://conshell.net/wiki/index.php/Linux_P2V"&gt;Another P2V technique which also works in VMWare.&lt;/a&gt;



&lt;div class="codeblock"&gt;&lt;code class="java"&gt;&lt;/code&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-798289589743505068?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/798289589743505068/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2011/03/virtualizing-pc.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/798289589743505068'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/798289589743505068'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2011/03/virtualizing-pc.html' title='Virtualizing a PC'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-8678989278886714289</id><published>2011-03-24T08:59:00.007+01:00</published><updated>2011-03-27T14:44:57.238+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>On making a custom Ubuntu Bootable USB stick</title><content type='html'>&lt;p&gt;In my upcoming Wicket course the students will use a rental laptop. How do we guarantee that they will be up and running in no time? Colleague Jason had the solution: burn a Ubuntu Live CD to a USB stick!&lt;/p&gt;

&lt;p&gt;Though simple this may sound, in the end it took us almost 3 days to put it together. This article gives an overview of the steps I took to create the USB sticks and how I tested them with VirtualBox.&lt;/p&gt;

&lt;h3&gt;Preparations&lt;/h3&gt;

&lt;p&gt;For starters you need a fast PC with about 15 GB free diskspace. I used Ubuntu 10.10, but other version probably work similarly. You'll need sudo rights as well. If your PC is somewhat older building the image might take quite some time.&lt;/p&gt;

&lt;p&gt;Secondly, install the Ubuntu Customization Kit (package name &lt;code&gt;uck&lt;/code&gt;). If you are on Ubuntu 10.10, make sure package &lt;code&gt;gfxboot-dev&lt;/code&gt; is installed as well.&lt;/p&gt;

&lt;p&gt;Finally you will need a clean &lt;a href="http://www.ubuntu.com/desktop/get-ubuntu/download"&gt;Ubuntu iso image&lt;/a&gt;. Any Ubuntu, Kubuntu or Xubuntu image will do. This will be your starting point so choose carefully. I took &lt;code&gt;ubuntu-10.10-desktop-i386.iso&lt;/code&gt; as I wanted to be sure it ran on older (32 bit) PCs as well.&lt;/p&gt;

&lt;p&gt;Be prepared to repeat the whole customization process: log carefully what you do and keep a copy of all content you change or add.&lt;/p&gt;

&lt;h3&gt;Customizing the CD, first build&lt;/h3&gt;

&lt;p&gt;Uck puts everything in &lt;code&gt;$HOME/tmp&lt;/code&gt; so I created that directory first. Then I simply executed &lt;code&gt;uck-gui&lt;/code&gt;. The questions that are asked are quite straight-forward to answer. Don't select too much; to make the resulting image fit on a 1Gb USB stick you will need all the space you have. The last question makes you choose between running a packet manager, running a command shell, or continuing the build. Choose the command shell as it makes it a lot easier to keep track of your changes. Tip: use the &lt;code&gt;history&lt;/code&gt; command to see what you did.&lt;/p&gt;

&lt;h3&gt;Removing packages&lt;/h3&gt;

&lt;p&gt;In the command shell you can remove packages and make other customizations. The &lt;a href="https://help.ubuntu.com/community/LiveCDCustomizationMaverick"&gt;Ubuntu wiki&lt;/a&gt; gives much information. The shell is chrooted to the new filesystem, so everything you do in that shell is restricted to the image you are creating. To be extra clear: if you remove a package in the UCK shell, you remove it from the live cd image and not from the rest of your system.&lt;/p&gt;

&lt;p&gt;I purged lots of packages I didn't expect my students would need. Comments with more large packages that are probably not needed in a course are appreciated.&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="shell"&gt;
apt-get purge ubuntu-docs openoffice.org-core openoffice.org-java-common openoffice.org-common openoffice.org-writer openoffice.org-help-en-us openoffice.org-calc evolution evolution-common libevolution evolution-data-server evolution-webcal gbrainy gnome-games-common gwibber-service telepathy-gabble telepathy-gabble libtelepathy-glib0 python-telepathy empathy-common hplip-data hpijs hplip-cups hplip
&lt;/code&gt;&lt;/div&gt;
&lt;br/&gt;

&lt;h3&gt;Installing packages&lt;/h3&gt;

&lt;p&gt;Then I installed Java and cleaned up:&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="shell"&gt;
sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"
sudo apt-get update
sudo apt-get install sun-java6-jdk
apt-get clean
&lt;/code&gt;&lt;/div&gt;

&lt;p&gt;I also removed some example content:&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="shell"&gt;
rm -rf /usr/share/backgrounds/*
rm -rf /usr/share/example-content/Ubuntu_Free_Culture_Showcase
&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;and placed my own background. See the ubuntu wiki link above for more details.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;/etc/skel&lt;/code&gt; I removed the link to the example contents and I added the following to &lt;code&gt;.profile&lt;/code&gt;:&lt;/p&gt;

&lt;div class="codeblock"&gt;&lt;code class="shell"&gt;
# Java
export JAVA_HOME=/usr/lib/jvm/java-6-sun

# Maven
export M2_HOME=/opt/maven3
export PATH=${PATH}:${M2_HOME}/bin
export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=512m"
&lt;/code&gt;&lt;/div&gt;

&lt;p&gt;The next step was to unzip recent Maven, Eclipse and IntelliJ distributions in &lt;code&gt;/opt&lt;/code&gt;. The easiest way I found to do this is open a new shell, switch to root with &lt;code&gt;sudo -s&lt;/code&gt; and start a &lt;code&gt;nautilus&lt;/code&gt;. This gives you convenient access to &lt;code&gt;$HOME/tmp/remaster-root&lt;/code&gt;. Make sure all placed files have &lt;code&gt;root:root&lt;/code&gt; as owner.&lt;/p&gt;

&lt;p&gt;Close the shell with &lt;code&gt;exit&lt;/code&gt; (if another shell opens, close this one as well), and the Uck menu will appear again. Select &lt;code&gt;Continue build&lt;/code&gt; from the Uck menu. And within a few minutes you have your first &lt;code&gt;$HOME/tmp/remaster-new-files/livecd.iso&lt;/code&gt;!&lt;/p&gt;

&lt;h3&gt;Using the live cd&lt;/h3&gt;

&lt;p&gt;To test the live cd I installed &lt;a href="http://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt; (I installed 4.0.4 from the Oracle repository). I created a new machine with 8Gb of virtual harddisk space and mounted the new live cd iso file as virtual cd. That's it, just start the machine and play with it.&lt;/p&gt;

&lt;p&gt;Once the virtual machine was fully started I selected 'Try Ubuntu'. I used Maven to make sure all required dependencies were sucked in to &lt;code&gt;$HOME/.m2&lt;/code&gt;. Hint: I used the following commands in the largest example project:&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="shell"&gt;
mvn install
mvn eclipse:eclipse -DdownloadSources=true
mvn jettty:run
&lt;/code&gt;&lt;/div&gt;

&lt;p&gt;To ease starting Eclipse and IntelliJ I created a launcher on the desktop. Just right-click the desktop and select 'create launcher'. This will create two &lt;code&gt;.desktop&lt;/code&gt; files on the desktop.&lt;/p&gt;

&lt;p&gt;Tip: check the settings of the background image.&lt;/p&gt;

&lt;p&gt;Now open a new Nautilus window from the 'Places' menu and open a sftp connection to the guest system (do Ctrl-L and enter something like &lt;code&gt;sftp://host/home/user&lt;/code&gt;). This allows you to copy all changed and new content to the guest system for inclusion in the next build. I used this to copy the seeded maven repository and the two new launchers.&lt;/p&gt;

&lt;p&gt;Satisfied I had a record of all my changes, I shutdown the virtual host, and applied the changes during a second run of &lt;code&gt;uck-gui&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Making a bootable USB stick&lt;/h3&gt;

&lt;p&gt;After a few builds, I was confident the live cd was ready. The next step is to create a bootable USB stick. This is made easy with the 'Startup Disk Creator' you will find in the 'System/Administration' menu. If you want the USB stick to be useful without installing Ubuntu to the guest computer, make sure there is plenty of space for writing changes. You will probably need a 2GB stick, unless you remove many more packages a 1Gb stick does not leave enough space for any serious development.&lt;/p&gt;

&lt;p&gt;Test the USB stick by booting your computer from it.&lt;/p&gt;

&lt;p&gt;Testing the installation from the USB stick turned out to be much hard then expected. I didn't have a spare laptop, and VirtualBox's BIOS does not support booting from a USB device. The easiest way I found to circumvent this was to create a vdi image from the stick in the following way:&lt;/p&gt;

&lt;div class="codeblock"&gt;&lt;code class="shell"&gt;
sudo dd if=/dev/sdb of=raw-usb-image.bin
sudo chown user:user raw-usb-image.bin
VBoxManage convertfromraw raw-usb-image.bin usb-image.vdi
&lt;/code&gt;&lt;/div&gt;

&lt;p&gt;You can now mount the vdi file to your virtual machine (make sure its the first drive) and boot from it.&lt;/p&gt;

&lt;h3&gt;Cloning the USB&lt;/h3&gt;

&lt;p&gt;Once you have created the USB stick, you can clone it easily with the &lt;code&gt;dd&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;em style="color:red;"&gt;Be careful!&lt;/em&gt; Selecting the wrong device may kill your harddisk! You have been warned.&lt;/p&gt;

&lt;div class="codeblock"&gt;&lt;code class="shell"&gt;
# Copy the USB to an image on disk
sudo dd if=/dev/sdb of=raw-usb-image.bin
# Write it out the another USB image
sudo dd if=raw-usb-image.bin of=/dev/sdc
&lt;/code&gt;&lt;/div&gt;

&lt;p&gt;A USB 2.0 hub with many ports helps because the throughput of the USB channel is much higher then the write speed of the sticks. For me, writing 8 USB sticks took the same time as a single stick.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.grons.nl/day2daystuff/20110324_cloning-usb-sticks.jpg" alt="USB cloning in action" style="vertical-align:text-top;"/&gt; USB cloning in action&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 2011-03-27&lt;/strong&gt; One problem with cloning disks like this is that they all get the same UUID. In some situations this may give problems. I have yet to find a solution for this. I did not find a tools for just changing the UUID of a FAT file system.&lt;/p&gt;

&lt;h3&gt;Conclusions&lt;/h3&gt;

&lt;p&gt;With the help of the Ubuntu Customization Kit, creating a customized Ubuntu startup USB stick becomes a surprisingly straight forward -though lengthy- process. Make sure you can repeat your steps as you probably need to. I found VirtualBox to be an excellent tool to test both the Live CD and the bootable USB sticks.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-8678989278886714289?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/8678989278886714289/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2011/03/on-making-custom-ubuntu-bootable-usb.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8678989278886714289'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8678989278886714289'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2011/03/on-making-custom-ubuntu-bootable-usb.html' title='On making a custom Ubuntu Bootable USB stick'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-7498033563175873770</id><published>2010-10-05T20:56:00.005+02:00</published><updated>2010-10-05T21:24:40.026+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Why Functional Programming Matters - An exploration of functional Scala - Part 2</title><content type='html'>package nl.grons.whyfp&lt;br/&gt;
import scala.math.abs&lt;br/&gt;

/**
&lt;p&gt;A translation of Miranda examples in chapter 4 of the paper
    &lt;a href="http://www.cse.iitb.ac.in/~as/fpcourse/whyfp.ps"&gt;Why Functional Programming Matters&lt;/a&gt;
    by John Hughes, 1984.&lt;/p&gt;
&lt;p&gt;
    This article is the continuation of
    &lt;a href="http://day-to-day-stuff.blogspot.com/2010/09/why-functional-programming-matters.html"&gt;part 1 - Chapter
    3 examples&lt;/a&gt;. I will show you lazy evaluation and how this can be useful.&lt;/p&gt;
&lt;p&gt;
@author Erik van Oosten&lt;br/&gt;
*/&lt;/p&gt;
&lt;h3&gt;object Chapter4_LazyList {&lt;/h3&gt;
/*
&lt;p&gt;Chapter 4&amp;#8217;s examples make heavy use of Miranda&amp;#8217;s lazy evaluation. In particular, it uses the list as
    defined in chapter 3 in a lazy way. This allows the creation of lists of unbounded length. Scala supports
    lazy lists as well. You can find an implementation in the &lt;code&gt;Stream&lt;/code&gt; class. However, as I want
    to stay close to the original, I redefine the list from chapter 3 as follows:
&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;/** The list from chapter 3, redefined to make it a lazy list. */
sealed abstract class List[+A] {
  def head: A
  def tail: List[A]
}

/** Lazyly evaluate the argument of the constructor AND the value of tail. */
final class Cons[A](hd: A, tl: =&gt; List[A]) extends List[A] {
  override val head: A = hd
  override lazy val tail: List[A] = tl
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;If you compare this to the List implementation in the previous article, you note that
    parameter &amp;#8216;tl&amp;#8217; of &amp;#8216;Cons&amp;#8217; is passed as an by-name parameter (because of the &lt;code&gt;=&gt;&lt;/code&gt;).
    This means that the given expression is only evaluated when the parameter is used.
    Too prevent the tail expression to be evaluated again and again, the &amp;#8216;tail&amp;#8217; value
    is declared as a &lt;code&gt;val&lt;/code&gt;. Now &amp;#8216;tl&amp;#8217; is evaluated exactly once. It is also
    declared as &lt;code&gt;lazy&lt;/code&gt; so that the evaluation of the expression is delayed until
    the first time it is needed instead of during construction time.&lt;/p&gt;
&lt;p&gt;These 3 things together allow the list to behave lazily.&lt;/p&gt;
&lt;p&gt;You might also notice that &amp;#8216;Cons&amp;#8217; is no longer a case class (not possible with
    by-name parameters). Therefore I define the following:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;object Cons {
  def apply[A](hd: A, tl: =&gt; List[A]) = new Cons(hd, tl)
  // Warning: using unapply leads to evaluation of the tail.
  def unapply[A](c: Cons[A]) = Some(c.head, c.tail)
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;&amp;#8216;Nil&amp;#8217; did not change:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;/** The empty list. */
case object Nil extends List[Nothing] {
  override def head: Nothing = {
    throw new NoSuchElementException("head of empty list")
  }
  override def tail: List[Nothing] = {
    throw new NoSuchElementException("tail of empty list")
  }
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Just for fun: here is an example of how the list can be used to construct an infinite list.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;/** A list of all integer numbers starting at x and higher. */
// Miranda: nat x = cons x nat(x+1)
def nat(x: Int): List[Int] = Cons(x, nat(x + 1))&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;The Miranda version of &amp;#8216;nat&amp;#8217; is limited by the available memory, the Scala version is limited
    to &lt;code&gt;scala.Int.MaxValue&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Function &amp;#8216;map&amp;#8217; from the previous chapter won&amp;#8217;t work. It will try to completely
    iterate the list which will obviously fail for unbounded lists. Lets implement
    it again with some simple pattern matching:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;/** Create a new lazy list where each element a is replaced by f(a). */
def map_wrong[A, B](f: A =&gt; B)(l: =&gt; List[A]): List[B] = l match {
  case Nil =&gt; Nil
  case Cons(head, tail) =&gt; Cons(f(head), map(f)(tail))
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;This implementation seemed to work for quite some time, even though everything was slow.
    If you look carefully, you will see that it evaluates &amp;#8216;tail&amp;#8217; in &amp;#8216;Cons.unapply&amp;#8217; which is invoked to match
    the pattern &lt;code&gt;Cons(head, tail)&lt;/code&gt;. The result is that exactly one more item in the list is
    evaluated then necessary. Though this is merely wasteful for most algorithms, it is catastrophic
    (as in stack overflow) when that evaluation contains another recursive call to &amp;#8216;map&amp;#8217;. This actually
    happens in the last example of this article.
&lt;/p&gt;
&lt;p&gt;Here is an implementation that does not have this problem:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;/** Create a new lazy list where each element a is replaced by f(a). */
def map[A, B](f: A =&gt; B)(l: =&gt; List[A]): List[B] = l match {
  case Nil =&gt; Nil
  case c: Cons[A] =&gt; Cons(f(c.head), map(f)(c.tail))
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Notice that the recursive expression, which includes &lt;code&gt;c.tail&lt;/code&gt;, is now used in a place that is lazily evaluated.&lt;/p&gt;
&lt;p&gt;On the console:&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;scala&gt; import nl.grons.whyfp.Chapter4_LazyList._

scala&gt; def printFirst[A](count: Int, l: List[A]) {
  if (count &gt; 0 &amp;&amp; l != Nil) { println(l.head); printFirst(count - 1, l.tail) } }
printFirst: [A](count: Int,l: nl.grons.whyfp.Chapter4_LazyList.List[A])Unit

scala&gt; printFirst(3, map[Int,Int](_ * 2)(nat(3)))
6
8
10&lt;/code&gt;&lt;/div&gt;
*/&lt;br/&gt;
}
&lt;h3&gt;object Chapter4_1_NewtonRaphsonSquareRoots {&lt;/h3&gt;
import Chapter4_LazyList._&lt;br/&gt;
/*
&lt;p&gt;Here&amp;#8217;s an imperative Scala implementation that does a numerical calculation of the square root of a number.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;/** Approximate the square root of n to within eps
 * starting with approximation a0. */
def imperative_squareroot(a0: Double, eps: Double, n: Double): Double = {
  // Warning: imperative scala!
  var x = a0
  // The initial value of y does not matter so long as abs(x-y) &gt; eps
  var y = a0 + 2 * eps
  while (abs(x - y) &gt; eps) {
    y = x
    x = (x + n/x) / 2
  }
  x
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Here are my translations of the rest:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;/** Next approximation of the square root of n,
 * based on the previous approximation x. */
// Miranda: next N x = (x + N/x) / 2
def next(n: Double)(x: Double): Double = (x + n/x) / 2

/** Produce an infinite list that starts with a and where all other values
 * are the result of applying f to the previous value. */
// Miranda: repeat f a = cons a (repeat f (f a))
def repeat[A](f: A =&gt; A, a: A): List[A] = Cons(a, repeat(f, f(a)))

/** Give the first value of the list that is within eps of its preceding value. */
// Miranda:
// within eps (cons a (cons b rest)) =
//         = b,                        if abs(a-b) &amp;lt;=  eps
//         = within eps (cons b rest), otherwise
def within(eps: Double, l: List[Double]): Double = {
  val a = l.head
  if (a.isNaN) throw new RuntimeException("nan")
  val rest = l.tail
  val b = rest.head
  if(abs(a - b) &amp;lt;= eps) b else within(eps, rest)
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;When a calculation overflow or underflow occurs (not unlikely when working with
    approximations), &amp;#8216;within&amp;#8217; goes amok and will loop forever. This is prevented
    by validating the head of the list against NaN, Not A Number.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;/** Approximate the square root of n to within eps
 * starting with approximation a0. */
// Miranda: sqrt a0 eps N = within eps (repeat (next N) a0)
def sqrt(a0: Double, eps: Double, n: Double) = within(eps, repeat(next(n), a0))

/** Gives the first value of the list that of which the ratio of change
 * with its preceding value is lower then eps. */
// Miranda:
// relative eps (cons a (cons b rest)) =
//         = b,                          if abs(a-b) &amp;lt;= eps*abs b
//         = relative eps (cons b rest), otherwise
def relative(eps: Double, l: List[Double]): Double = {
  val a = l.head
  if (a.isNaN) throw new RuntimeException("nan")
  val rest = l.tail
  val b = rest.head
  if(abs(a - b) &lt;= eps * abs(b)) b else relative(eps, rest)
}

/** Approximate the square root of n to within ratio eps starting with
 * approximation a0. */
// Miranda: relativesqrt a0 eps N = relative eps (repeat (next N) a0)
def relativesqrt(a0: Double, eps: Double, n: Double) =
  relative(eps, repeat(next(n), a0))
&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;On the Console:&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;scala&gt; import nl.grons.whyfp.Chapter4_LazyList._
scala&gt; import nl.grons.whyfp.Chapter4_1_NewtonRaphsonSquareRoots._

scala&gt; relativesqrt(0.1, 0.01, 4)
res0: Double = 2.000010925778043&lt;/code&gt;&lt;/div&gt;
*/&lt;br/&gt;
}
&lt;h3&gt;object Chapter4_2_NumericalDifferentiation {&lt;/h3&gt;
import Chapter4_LazyList._&lt;br/&gt;
import Chapter4_1_NewtonRaphsonSquareRoots._&lt;br/&gt;
/*
&lt;p&gt;This chapter is about numerical differentiation.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;/** An approximation of the differentiation of f for x over h. */
// Miranda: easydiff f x h = (f(x+h)-f x) / h
def easydiff(f: Double =&gt; Double, x: Double)(h: Double): Double =
  (f(x + h) - f(x)) / h

// Miranda:
// differentiate h0 f x = map (easydiff f x)(repeat halve h0)
// halve x = x/2
def differentiate(h0: Double, f: Double =&gt; Double, x: Double) =
  map(easydiff(f, x))(repeat(halve, h0))
def halve(x: Double) = x / 2&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;On the console:&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;scala&gt; import nl.grons.whyfp.Chapter4_LazyList._
scala&gt; import nl.grons.whyfp.Chapter4_1_NewtonRaphsonSquareRoots._
scala&gt; import nl.grons.whyfp.Chapter4_2_NumericalDifferentiation._

scala&gt; val f: Double =&gt; Double = math.pow(_, 3.0)
f: (Double) =&gt; Double = &amp;lt;function1&gt;

scala&gt; within(0.001, differentiate(1, f, 2))
res0: Double = 12.000732436776161&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;Some more examples:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda:
// elimerror n (cons a (cons b rest)) =
//        = cons ((b*(2**n)-a)/(2**n-1)) (elimerror n (cons b rest))
def elimerror(n: Double, l: List[Double]): List[Double] = {
  val a = l.head
  val bandrest = l.tail
  val b = bandrest.head
  Cons(
    (b * math.pow(2.0, n) - a) / (math.pow(2.0, n) - 1.0),
    elimerror(n, bandrest))
}

/** Calculate the order of n to be used in elimerror. */
// Miranda:
// order (cons a (cons b (cons c rest))) =
//         = round(log2( (a-c)/(b-c) - 1 ))
// round x = x rounded to the nearest integer
// log2 x = the logarithm of x to the base 2
def order(l: List[Double]): Double = {
  val LOG2: Double = math.log(2.0)
  def log2(x: Double) = math.log(x) / LOG2

  val a = l.head
  val b = l.tail.head
  val c = l.tail.tail.head
  val o = math.round(log2((a-c)/(b-c) - 1))
  if (o.isNaN || o == 0) 1 else o
}

// Miranda: improve s = elimerror (order s) s
def improve(s: List[Double]): List[Double] = elimerror(order(s), s)&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Note that my implementation of &amp;#8216;order&amp;#8217; is not only real (no vague descriptions of &amp;#8216;round&amp;#8217;
    and &amp;#8216;log2&amp;#8217;), but it is more robust then the original. First, it protects against
    &lt;code&gt;NaN&lt;/code&gt; for the case that &lt;code&gt;b-c&lt;/code&gt; reaches &lt;code&gt;0&lt;/code&gt;. Secondly,
    it prevents a result of 0 as that leads to a division by 0 in &amp;#8216;elimerror&amp;#8217;.&lt;/p&gt;
&lt;p&gt;On the console:&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;scala&gt; within(0.001, improve(differentiate(1, f, 2)))
res0: Double = 11.9998779296875

scala&gt; // Improve can be improved recursively

scala&gt; within(0.001, improve(improve(improve(differentiate(1, f, 2)))))
res1: Double = 12.0&lt;/code&gt;&lt;/div&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda:
// super s = map second (repeat improve s)
// second (cons a (cons b rest)) = b
def superimprove(s: List[Double]): List[Double] = map(second)(repeat(improve, s))
def second(l: List[Double]): Double = l.tail.head

def superdifferentiate(f: Double =&gt; Double, x: Double): Double = {
  val eps: Double = 0.00000001
  within(eps, superimprove(differentiate(x + 2 * eps, f, x)))
}&lt;/code&gt;&lt;/div&gt;
}
&lt;h3&gt;object Chapter4_3_NumericalIntegration {&lt;/h3&gt;
import Chapter4_LazyList._&lt;br/&gt;
/*
&lt;p&gt;This chapter is about numerical integration.&lt;/p&gt;
    */
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda: easyintegrate f a b = (f a + f b)*(b-a)/2
def easyintegrate(f: Double =&gt; Double, a: Double, b: Double) =
  (f(a) + f(b)) * (b-a) / 2

// Miranda: integrate f a b = cons (easyintegrate f a b)
//                                 (map addpair (zip (integrate f a mid)
//                                                   (integrate f mid b)))
//                  where mid = (a+b)/2
def integrate_simple(f: Double =&gt; Double, a: Double, b: Double): List[Double] = {
  def addpair(pair: (Double, Double)) = pair._1 + pair._2
  val mid = (a + b) / 2
  Cons(
    easyintegrate(f, a, b),
    map(addpair)(zip(integrate(f, a, mid), integrate(f, mid, b)))
  )
}

/** Convert two lists to a list of pairs. */
// Miranda: zip (cons a s) (cons b t) = cons (pair a b) (zip s t)
def zip[A,B](as: =&gt; List[A], bs: =&gt; List[B]): List[(A,B)] = as match {
  case Nil =&gt; Nil
  case a: Cons[A] =&gt; bs match {
    case Nil =&gt; Nil
    case b: Cons[B] =&gt; Cons((a.head, b.head), zip(a.tail, b.tail))
  }
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Again note that &amp;#8216;zip&amp;#8217; was defined such that &lt;code&gt;a.tail&lt;/code&gt; and &lt;code&gt;b.tail&lt;/code&gt; are defined as a lazily
  evaluated argument.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda:
// integrate f a b = integ f a b (f a) (f b)
// integ f a b fa fb = cons ((fa+fb)*(b-a)/2)
//                          (map addpair (zip (integ f a m fa fm)
//                                            (integ f m b fm fb)))
//         where m = (a+b)/2
//               fm = f m
def integrate(f: Double =&gt; Double, a: Double, b: Double): List[Double] = {
  def integ(a: Double, b: Double, fa: Double, fb: Double): List[Double] = {
    def addpair(pair: (Double, Double)) = pair._1 + pair._2

    val mid = (a + b) / 2
    val fm = f(mid)
    Cons(
      (fa + fb) * (b-a) / 2,
      map(addpair)(zip(integ(a, mid, fa, fm), integ(mid, b, fm, fb)))
    )
  }

  integ(a, b, f(a), f(b))
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;And of course, seeing is believing:&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;scala&gt; import nl.grons.whyfp.Chapter4_LazyList._
scala&gt; import nl.grons.whyfp.Chapter4_1_NewtonRaphsonSquareRoots._
scala&gt; import nl.grons.whyfp.Chapter4_2_NumericalDifferentiation._
scala&gt; import nl.grons.whyfp.Chapter4_3_NumericalIntegration._

scala&gt; def f(x: Double): Double = 1/(1+x*x)
f: (x: Double)Double

scala&gt; within(0.001, integrate(f, 1, 2))
res0: Double = 0.3218612363325556

scala&gt; relative(0.001, integrate(f, 1, 2))
res1: Double = 0.3217782239721789

scala&gt; relative(0.001, superimprove(integrate(f, 1, 2)))
res2: Double = 0.3217525935931843&lt;/code&gt;&lt;/div&gt;
*/&lt;br/&gt;
}&lt;br/&gt;
/*
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Scala is an excellent language for functional style programs and I really like that static typing prevents
  tons of errors. My only problem with Scala &amp;mdash; in terms of the &amp;#8216;Why Functional Programming matters&amp;#8217;
  paper &amp;mdash; is that lazy evaluation is not the natural way of working. The way expressions are evaluation is more
  closely to the Java world. To make use of lazy evaluation you have to be very much aware of what your doing
  and carefully make use of the lazy keyword and by-name parameters. With Miranda this comes naturally though
  probably at the cost of some performance for the case laziness is not needed. Perhaps that the standard
  collections library (scala.collection.immutable.Stream) hides this sufficiently and makes life easy.&lt;/p&gt;
&lt;p&gt;A more fundamental way to fix this problem would be to express the laziness nature of an expression in its
   type. I am out of the academic world for a looong time, so I have no idea if this is being researched at all.
   It will take some hard thoughts to make this practical and simple to use though.&lt;/p&gt;
&lt;p&gt;The next challenge would be to repeat this experiment with the standard collections. But I&amp;#8217;ll leave that
   to someone with more time...&lt;/p&gt;
*/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-7498033563175873770?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/7498033563175873770/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2010/10/why-functional-programming-matters.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7498033563175873770'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7498033563175873770'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2010/10/why-functional-programming-matters.html' title='Why Functional Programming Matters - An exploration of functional Scala - Part 2'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-7894133147803399811</id><published>2010-09-21T22:04:00.012+02:00</published><updated>2010-09-23T09:44:06.947+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Why Functional Programming Matters - An exploration of functional Scala.</title><content type='html'>package nl.grons.whyfp&lt;br/&gt;
/**
&lt;p&gt;A translation of Miranda examples in the paper
    &lt;a href="http://www.cse.iitb.ac.in/~as/fpcourse/whyfp.ps"&gt;Why Functional Programming Matters&lt;/a&gt;
    by John Hughes, 1984.&lt;/p&gt;
&lt;p&gt;
@author Erik van Oosten&lt;br/&gt;
*/&lt;/p&gt;
&lt;span style="font-weight:bold;"&gt;object WhyFp_Abstract {&lt;/span&gt;&lt;br/&gt;
/*
&lt;p&gt;Scala is in the focus of attention because it allows a smooth migration from one
    of the most popular programming languages ever to a language that supports
    (among other things) a functional style of programming.&lt;/p&gt;
&lt;p&gt;In this article I explore the functional aspects of Scala based on a very
    influential paper on &lt;a href="http://en.wikipedia.org/wiki/Functional_programming"&gt;functional
    programming&lt;/a&gt;: &amp;#8216;Why Functional Programming matters&amp;#8217;. To do so I will translate
    the &lt;a href="http://en.wikipedia.org/wiki/Miranda_(programming_language)"&gt;Miranda&lt;/a&gt; examples
    of chapter 3 and 4 (next article) to Scala. To keep things pure, the standard Scala libraries are avoided.&lt;/p&gt;
&lt;p&gt;I will conclude that Scala has sufficient features to live up to Hughes&amp;#8217; standards with the added
    bonus of being statically type safe.&lt;/p&gt;

&lt;p&gt;Just like the paper I just assume you know a bit about functions, partial functions and partially
    applied functions. In addition I assume you know basic Scala syntax. However, as I am still a
    Scala newby myself, any comments that will improve this article are welcomed.&lt;/p&gt;
&lt;p&gt;This article focusses on the Miranda -&gt; Scala translation and will
    &lt;span style="font-style:italic;"&gt;not&lt;/span&gt; repeat the message of the paper. So go ahead,
    and read the paper first.&lt;/p&gt;

&lt;p&gt;All text in this article is properly commented, so the whole article can be copy pasted into your
    favourite text-editor.&lt;/p&gt;
*/&lt;br/&gt;
&lt;span style="font-weight:bold;"&gt;object Chapter3_GlueingFunctionsTogether {&lt;/span&gt;&lt;br/&gt;
import RichFunction2._&lt;br/&gt;
/*
&lt;p&gt;The original article completely defines a list right there in just one line:
&lt;div class="codeblock"&gt;&lt;code class="miranda"&gt;listof X ::= nil | cons X (listof X)    (Miranda)&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;Scala is not so powerful and this mainly because we need to translate Miranda&amp;#8217;s &amp;#8216;|&amp;#8217;  operator to a class hierarchy.&lt;/p&gt;
&lt;p&gt;Lets define List more or less as the Scala library does it (as &lt;a href="http://day-to-day-stuff.blogspot.com/2010/09/why-functional-programming-matters.html?showComment=1285227074480#c2851697984306424900"&gt;anonymous&lt;/a&gt; points out, it can be done much shorter).&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;/** The base type for our list. */
sealed abstract class List[+A] {
  def head: A
  def tail: List[A]
}

/** Cons represents an element and the rest of the list. */
case class Cons[A](hd: A, tl: List[A]) extends List[A] {
  override def head: A = hd
  override def tail: List[A] = tl
  override def toString = "Cons(" + hd + "," + tl + ")"
}

/** Nil represents the empty list. */
case object Nil extends List[Nothing] {
  override def head: Nothing = {
    throw new NoSuchElementException("head of empty list")
  }
  override def tail: List[Nothing] = {
    throw new NoSuchElementException("tail of empty list")
  }
  override def toString = "Nil"
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Note that Cons is a case class, therefore we can use &lt;code&gt;Cons(x,y)&lt;/code&gt; as synonym for the constructor and
    also as extractor during pattern matching.&lt;/p&gt;

&lt;p&gt;We can now use the list as follows:&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;[]         =&gt; Nil
[1]        =&gt; Cons(1, Nil)
[1,2,3]    =&gt; Cons(1, Cons(2, Cons(3, Nil)))&lt;/code&gt;&lt;/div&gt;

&lt;p&gt;As in the original article, what follows is a number of definitions of &amp;#8216;sum&amp;#8217;. I name each
    definition differently so that this whole article can be compiled as is.&lt;/p&gt;
&lt;p&gt;My first approach mimics the Miranda approach with 2 partial functions. Scala does
    not automatically combine partial functions se we&amp;#8217;ll need to do ourselves.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda:
// sum nil = 0
// sum (cons num list) = num + sum list
//
val sum_nil: PartialFunction[List[Int], Int] = { case Nil =&gt; 0 }
val sum_cons: PartialFunction[List[Int], Int] =
       { case Cons(num, list) =&gt; num + sum_combinedpartials(list) }
def sum_combinedpartials = sum_nil orElse sum_cons
&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;The second, more idiomatic Scala, version wraps this into a single method:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;def sum_idiomatic(li: List[Int]): Int = li match {
  case Nil =&gt; 0
  case Cons(num, list) =&gt; num + sum_idiomatic(list)
}
&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Note that both approaches use pattern matching, just like the Miranda original.&lt;/p&gt;

&lt;p&gt;The following definitions of &amp;#8216;sum&amp;#8217; are more interesting. They are very close to the original example.
    Note that this actually defines an immutable reference (a &lt;code&gt;val&lt;/code&gt;) to a function. In case you are
    wondering, &lt;code&gt;List[Int] =&gt; Int&lt;/code&gt; is the type of the function: it accepts one argument,
    &lt;code&gt;List[Int]&lt;/code&gt;, and has &lt;code&gt;Int&lt;/code&gt; as return type.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda: sum = reduce add 0
val sum: List[Int] =&gt; Int = reduce(add, 0)&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Note that nowadays most languages instead of &amp;#8216;reduce&amp;#8217;, use the term &amp;#8216;foldr&amp;#8217; (fold-right).&lt;/p&gt;

&lt;p&gt;Scala has type inference, so if we let the compiler derive the return type, its even closer. Note that
    we need to tuck an underscore to the end to let the compiler know we really want a partially applied function.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;val sum_noTypes = reduce(add, 0) _&lt;/code&gt;&lt;/div&gt;

/*
&lt;p&gt;And of course we need a definition for &amp;#8216;add&amp;#8217;:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda: add x y = x + y
def add(x: Int, y: Int): Int = x + y
&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;For function &amp;#8216;reduce&amp;#8217;, I again use pattern matching just like the Miranda original. Notice that &amp;#8216;reduce&amp;#8217;
    defines 2 parameter lists. One with parameters &lt;code&gt;f&lt;/code&gt; and &lt;code&gt;x&lt;/code&gt;, and the second with
    parameter &lt;code&gt;l&lt;/code&gt;. This allows for easy creation of partially applied functions. And this is exactly
    what happened in the definitions of &amp;#8216;sum&amp;#8217; and &amp;#8216;sum_noTypes&amp;#8217; above.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda
// (reduce f x) nil = x
// (reduce f x) (cons a l) = f a ((reduce f x) l)
//
/**
 * Right fold a list with a function f and a constant x.
 *
 * @param f a function to apply
 * @param x the result for an empty list
 * @param l the list to fold
 * @param [A] the type of list elements
 * @param [B] the result type
 * @return the result of the expression where in l,
 *   each Cons is replaced by f, and Nil by x
 */
def reduce[A, B](f: (A, B) =&gt; B, x: B)(l: List[A]): B = l match {
  case Nil =&gt; x
  case Cons(head, tail) =&gt; f(head, reduce(f, x)(tail))
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;More examples:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda: product = reduce multiply 1
def product: List[Int] =&gt; Int = reduce(multiply, 1)&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Just for fun I define &amp;#8216;multiply&amp;#8217; a little bit different then &amp;#8216;add&amp;#8217;. Where &amp;#8216;add&amp;#8217; is a method (which can be used
    as a function), &amp;#8216;multiply&amp;#8217; is directly defined as a function. Each underscore declares an anonymous parameter.
    To avoid the ugly &lt;code&gt;(_: Int)&lt;/code&gt; syntax, the type of these anonymous parameters are derived form the
    return type of &amp;#8216;multiply&amp;#8217;.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;val multiply: (Int, Int) =&gt; Int = _ * _&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;We can also inline these functions. If you do so the compiler still needs some hints to infer the types
    of the anonymous parameters. In the following definitions of &amp;#8216;anytrue&amp;#8217; and &amp;#8216;alltrue&amp;#8217; I solve that by adding
    the generic types of &amp;#8216;reduce&amp;#8217; (the &lt;code&gt;[Boolean,Boolean]&lt;/code&gt; part).&lt;/p&gt;
&lt;p&gt;With &amp;#8216;alltrue&amp;#8217; the return type is inferred by the compiler (again, this requires an underscore to confirm
    we want partial application of &amp;#8216;reduce&amp;#8217;). In the definition of &amp;#8216;alltrue&amp;#8217;, the return type is defined and
    therefore the underscore is not needed.&lt;/p&gt;
&lt;p&gt;Personally I prefer the explicit return type as the function is too complex to just see what it should be.
    My rule is omit the return type only when it is blindingly obvious (as in &amp;#8216;add&amp;#8217; above).&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda
// anytrue = reduce or false
// alltrue = reduce and true
//
def anytrue: List[Boolean] =&gt; Boolean = reduce[Boolean, Boolean](_ || _, false)
def alltrue                           = reduce[Boolean, Boolean](_ &amp;&amp; _, true) _&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Lets also explore how &amp;#8216;reduce&amp;#8217; works and calculate the sum of &lt;code&gt;[1,2,3]&lt;/code&gt;. Note how we replace
    &lt;code&gt;Cons&lt;/code&gt; with the given function &amp;#8216;add&amp;#8217; and &lt;code&gt;Nil&lt;/code&gt; with the given constant &amp;#8216;0&amp;#8217;.&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;
Starting point:       Cons(1,     Cons(2,     Cons(3, Nil)))
Reduce applied:        add(1,      add(2,      add(3,  0 ))) == 6
And for multiply: multiply(1, multiply(2, multiply(3,  1 ))) == 6&lt;/code&gt;&lt;/div&gt;

&lt;p&gt;Here is my translation of &amp;#8216;append&amp;#8217;.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda: append a b = reduce cons b a
def append[A](a: List[A], b: List[A]): List[A] = reduce[A, List[A]](Cons[A], b)(a)&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Note that the first parameter we pass to &amp;#8216;reduce&amp;#8217; is actually the Cons object (with generic type
    parameter &lt;code&gt;[A]&lt;/code&gt;). This is possible because anything with an &amp;#8216;apply&amp;#8217; method is
    recognized as a Scala function.&lt;/p&gt;
&lt;p&gt;The generic type &amp;#8216;[A]&amp;#8217; on Cons is essential; without it the compiler generates nothing but misleading
    error messages. It took me many hours to figure this one out.&lt;/p&gt;

&lt;p&gt;If you are new to this complex kind of type declarations (like I am), you&amp;#8217;ll find that careful reading
    is in order. I am afraid this is just something you need to practice. Also, it helps greatly when you wrote
    this kind of stuff yourself.&lt;/p&gt;

&lt;p&gt;Lets skip ahead to &amp;#8216;doubleall&amp;#8217;.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda:
//        doubleall = reduce doubleandcons nil
// where  doubleandcons num list = cons (2*num) list
def doubleall_1: List[Int] =&gt; List[Int] = {
  def doubleandcons(num: Int, list: List[Int]) = Cons(2 * num, list)
  reduce(doubleandcons, Nil)
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;As you can see Miranda&amp;#8217;s &lt;code&gt;where&lt;/code&gt; keyword nicely translates to nested methods.&lt;/p&gt;
&lt;p&gt;Next version:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda:
//        doubleandcons = fandcons double
// where  double n = 2*n
//        fandcons f el list = cons (f el) list
def doubleandcons: (Int, List[Int]) =&gt; List[Int] = {
  def double(n: Int) = 2 * n
  def fandcons[A, B](f: A =&gt; B)(el: A, list: List[B]) = Cons(f(el), list)
  fandcons(double)
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;So far, I had little problem with translating this to Scala. However, the next line needed
    quite a bit more thought:&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="miranda"&gt;fandcons f = cons . f&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;Scala (Miranda as well) only defines the &amp;#8216;compose&amp;#8217; method on functions of arity 1. For Miranda this is no problem
as all functions written down with multiple arguments are actually syntactic sugar for functions with a single
argument that produce further functions with 1 argument. So the type of &amp;#8216;add&amp;#8217; in&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="miranda"&gt;add x y = x + y&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;is not &lt;code&gt;(Int, Int) =&gt; Int&lt;/code&gt; as it would be in Scala, but &lt;code&gt;Int =&gt; Int =&gt; Int&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In our Scala code Cons.apply has 2 arguments and thus arity 2. To be precise, the type of
    &lt;code&gt;Cons[B] _&lt;/code&gt; is &lt;code&gt;(B, List[B]) =&gt; List[B]&lt;/code&gt;. (Note in Scala we write
    &lt;code&gt;Cons[B] _&lt;/code&gt; to indicate we need it as a partially applied function).&lt;/p&gt;
&lt;p&gt;In order to apply &amp;#8216;compose&amp;#8217; we first need to convert Cons[B].apply to something of type
    &lt;code&gt;B =&gt; List[B] =&gt; List[B]&lt;/code&gt; (a unary function that accepts a B, and gives another unary function
    that accepts a List[B], and gives a List[B]). This is called &lt;span style="font-style:italic;"&gt;currying&lt;/span&gt;
    and is performed with the &amp;#8216;curried&amp;#8217; function.&lt;/p&gt;
&lt;p&gt;The result of currying is a unary function that we can compose with
    &amp;#8216;f&amp;#8217;. The next step is to &lt;span style="font-style:italic;"&gt;uncurry&lt;/span&gt; back to a function of
    arity 2. This is done with &amp;#8216;uncurried&amp;#8217;. Not lets put this in code:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda: fandcons f = cons . f
def fandcons[A, B](f: A =&gt; B): (A, List[B]) =&gt; List[B] =
       Function.uncurried( (Cons[B] _).curried.compose(f) )&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;However, this is very a unwieldy syntax and I am going to need it a lot in the upcoming examples. Therefore, I&amp;#8217;ll
&lt;a href="http://scala.sygneca.com/patterns/pimp-my-library"&gt;pimp&lt;/a&gt; the Scala library to include &amp;#8216;compose&amp;#8217;
    also on functions of arity 2 (see &lt;code&gt;RichFunction2&lt;/code&gt; below).&lt;br/&gt;
    We can now condense the code to:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda: fandcons f = cons . f
def fandcons_short[A, B](f: A =&gt; B): (A, List[B]) =&gt; List[B] =
       (Cons[B] _).compose(f)&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;And then we try it out with some arguments to see it is correct:&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;fandcons(f, el)(list) = Cons.compose(f)(el)(list)
                      = Cons(f(el), list)&lt;/code&gt;&lt;/div&gt;

&lt;p&gt;And finally (no longer defining &amp;#8216;double&amp;#8217; as a nested function):&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda: doubleall = reduce (cons . double) nil
def double(n: Int) = 2 * n
def doubleall: List[Int] =&gt; List[Int] =
       reduce((Cons[Int] _).compose(double), Nil) _
&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;The next step is to extract &amp;#8216;map&amp;#8217;:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda:
// doubleall = map double
// map f = reduce (cons . f) nil
def doubleall_map: List[Int] =&gt; List[Int] = map(double)
def map[A, B](f: A =&gt; B): List[A] =&gt; List[B] = 
      reduce((Cons[B] _).compose(f), Nil) _&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Map could for example be used to calculate the sum in a matrix, a list of list of int.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda: summatrix = sum . map sum
val summatrix: List[List[Int]] =&gt; Int = sum.compose(map(sum))&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Here is a fragment of the scala console in which I try out summatrix:&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;scala&gt; import nl.grons.whyfp.Chapter3_GlueingFunctionsTogether._
scala&gt; val a: List[List[Int]] = Cons(Nil, Cons(Cons(1, Cons(2, Nil)), Cons(Cons(4, Nil), Nil)))
a: nl.grons.whyfp.Chapter3_GlueingFunctionsTogether.List[nl.grons.whyfp.Chapter3_GlueingFunctionsTogether.List[Int]] = Cons(Nil,Cons(Cons(1,Cons(2,Nil)),Cons(Cons(4,Nil),Nil)))

scala&gt; summatrix(a)
res0: Int = 7
&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Lets move on and create a tree.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;//Miranda: treeof X ::= node X (listof (treeof X))
sealed abstract class Tree[+A] {
  def label: A
  def children: List[Tree[A]]
}

case class Node[A](l: A, c: List[Tree[A]]) extends Tree[A] {
  override def label: A = l
  override def children: List[Tree[A]] = c
}
&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;The tree &lt;code style="white-space:pre;"&gt;
             1 o
              / \
             /   \
          2 o     o 3
                  |
                  |
                  o 4
&lt;/code&gt; in Scala would look like:&lt;/p&gt;
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;Node(1,
     Cons( Node(2, Nil),
           Cons(Node(3,
                Cons(Node(4,
                     Nil),
                Nil)),
           Nil)))&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;Here is my version of &amp;#8216;redtree&amp;#8217;:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda:
// redtree f g a (node label subtrees) =
//         f label (redtree' f g a subtrees)
// redtree' f g a (cons subtree rest) =
//         g (redtree f g a subtree) (redtree' f g a rest)
// redtree' f g a nil = a
/**
 * Reduce a tree with functions f and g and constant a.
 * @param f a function to apply to a node label and
 *   the result of g on the node's subtree
 * @param g a function to apply to a subtree and
 *   the result of g on the following sibling subtrees
 * @param a the result for an empty tree
 * @param tree the tree to reduce
 * @param [F] the return type of f and redtree itself
 * @param [G] the return type of g
 * @param [A] the type of the tree labels
 * @return the result of the expression where in tree,
 *   each Node is replaced by f, each Cons by g, and Nil by x
 */
def redtree[F, G, A](f: (A, G) =&gt; F, g: (F, G) =&gt; G, a: G)(tree: Tree[A]): F = {
  def redchildren(children: List[Tree[A]]): G = children match {
    case Cons(subtree, rest) =&gt; g(redtree(f, g, a)(subtree), redchildren(rest))
    case Nil =&gt; a
  }

  f(tree.label, redchildren(tree.children))
}&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Notice how Scala&amp;#8217;s nested functions are more elegant then Miranda&amp;#8217;s top level function
    &amp;#8216;redtree'&amp;#8239;&amp;#8217; (renamed to &amp;#8216;redchildren&amp;#8217; here);
    in Scala the arguments of &amp;#8216;redtree&amp;#8217; are not passed to &amp;#8216;redchildren&amp;#8217; as the nested function can
    access them directly.&lt;/p&gt;
&lt;p&gt;Lets use redtree to sum all integer labels in a tree of type &lt;code&gt;Tree[Int]&lt;/code&gt;:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda: sumtree = add add 0
val sumtree: Tree[Int] =&gt; Int = redtree(add, add, 0)&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;Next: collecting all the labels in a list:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda: labels = redtree cons append nil
def labels[A]: Tree[A] =&gt; List[A] =
     redtree(Cons[A], append[A], Nil)&lt;/code&gt;&lt;/div&gt;
/*
&lt;p&gt;And the last example: create a new tree by applying a function to each label:&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;// Miranda: maptree f = redtree (node . f) cons nil
def maptree[A, B](f: A =&gt; B): Tree[A] =&gt; Tree[B] =
     redtree((Node[B] _).compose(f), Cons[Tree[B]], Nil)&lt;/code&gt;&lt;/div&gt;
}
*/&lt;br/&gt;
&lt;h3&gt;Parting thoughts&lt;/h3&gt;

&lt;p&gt;Although it took quite some time to write the code and then translate this to my 100th article, I had great fun doing
    so. In addition, I learned quite a bit on writing more complex Scala code.&lt;/p&gt;
&lt;p&gt;The code for chapter 4 has already been translated, so expect another article soon.&lt;/p&gt;

&lt;h3&gt;P.S. RichFunction2&lt;/h3&gt;
&lt;p&gt;As was written above, Scala does not define &amp;#8216;compose&amp;#8217; on Function2 (the class representing
    functions with 2 parameters), only on Function1 &amp;#8216;compose&amp;#8217; is available. To remedy this,
    we&amp;#8217;ll define it ourselves with the
    &lt;a href="http://scala.sygneca.com/patterns/pimp-my-library"&gt;pimp-my-library pattern&lt;/a&gt;.&lt;/p&gt;
*/
&lt;div class="codeblock"&gt;&lt;code class="scala"&gt;class RichFunction2[T1, T2, R](f: Function2[T1, T2, R]) {
 /**
  * (f compose g)(x, y) == f(g(x), y)
  */
 def compose[A](g: (A) =&gt; T1): (A, T2) =&gt; R = {
   (x: A, y: T2) =&gt; f(g(x), y)
 }
}
object RichFunction2 {
  // Note: complete return type is required here because the method
  // is used before it is declared.
  implicit def richFunction2[T1, T2, R](f: Function2[T1, T2, R]):
       RichFunction2[T1, T2, R] = new RichFunction2(f)
}
&lt;/code&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-7894133147803399811?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/7894133147803399811/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2010/09/why-functional-programming-matters.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7894133147803399811'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7894133147803399811'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2010/09/why-functional-programming-matters.html' title='Why Functional Programming Matters - An exploration of functional Scala.'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-4696658217318491088</id><published>2010-08-12T14:02:00.001+02:00</published><updated>2010-08-22T14:25:24.534+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='opinion'/><category scheme='http://www.blogger.com/atom/ns#' term='programming philosophy'/><title type='text'>Open source - why bother with anything else?</title><content type='html'>Since I work in a fine small company where we are breathing open source for at least a decade, it is sometimes weird to be confronted again by open source adversaries or agonists. For example, a colleague wrote a fine technical design based on Mule. All of a sudden we're asked to compare this to BEA AquaLogic and see whether we could implement the project with that. Now this is probably possible, and AquaLogic is probably a fine product family, but &lt;span style="font-style:italic;"&gt;why bother?&lt;/span&gt;
&lt;p/&gt;
Since there was already a Mule prototype, I found it a cumbersome idea. To get up to speed with AquaLogic you first have to find out what products in the AquaLogic family you need, and of course you only need a tiny bit of most of them. Secondly you have to go into a trajectory to get the software, including development licenses. Somehow money is not always a problem, but the time to get the products and start working always is, and the deadline won't move. Did I already mention I really hate bureaucracy?
&lt;p/&gt;
Starting with open source often just takes 5 lines in a pom.xml and a few minutes of download time.
&lt;p/&gt;
Okay, well suppose this was all taken care of and you are happily underway with development. You then run into a problem. Yes, you will, this is no different from open source. Now lets see how I typically deal with problems with using open source and see how this applies to closed source software.
&lt;p/&gt;
&lt;span style="font-weight: bold;"&gt;Finding a solution to a problem with an open source product&lt;/span&gt;&lt;br/&gt;
1) (Re-)read the documentation&lt;br/&gt;
The first step is always to read the documentation. With the source jars attached in your favorite IDE, the javadoc is one key-press away.
&lt;p/&gt;
2) Debugging&lt;br/&gt;
In step 2 we'll do some debugging. Again, with the source jars attached, tracing through your own code is as easy as tracing the open source code. I may not understand all the code, but I know what I need and I can read the JavaDoc of code encountered underway.&lt;br/&gt;
More often then not tracing leads to a deeper understanding of the used products, and I frequently find things that are useful for other parts of the project. The deeper understanding helps to form a solution. This can be changing your code, or patching the open source product. Otherwise it helps you formulate a more precise problem statement for the next steps.
&lt;p/&gt;
3) Read more documentation&lt;br/&gt;
As I have now seen the code, I can search the available documentation more efficiently. So I do this first before going into the next step. Documentation in this phase can be anything, from manuals to blogs and forums.
&lt;p/&gt;
4) Post questions&lt;br/&gt;
The last step is to post a question on a forum or mailing list. If you get this far, you are either lazy, you just missed something, or you found out that the library has a bug. The results of this step are not always satisfactory; it really depends on the community around the product. At least you can always change the open source product yourself.
&lt;p/&gt;
&lt;span style="font-weight: bold;"&gt;Finding a solution to a problem with a closed source product&lt;/span&gt;
&lt;p/&gt;
1) (Re-)read the documentation&lt;br/&gt;
Again, the first step is to read the documentation. However, javadoc is not always available. Rarely is it available in the form of a source jar.
&lt;p/&gt;
2) Debugging&lt;br/&gt;
Oops, we can only trace our own code. Perhaps you can use de-compilation. Note that this might actually be illegal in your country (not in The Netherlands luckily). Secondly, the source might be obfuscated. And of course, I am not even talking about products that run as a complete separate program.
&lt;p/&gt;
3) Read more documentation&lt;br/&gt;
As it is unlikely that debugging gave us more insight, we skip this step.
&lt;p/&gt;
4) Post questions&lt;br/&gt;
In rare cases there are user communities around closed source. These are very valuable! However, usually you just have to ask the manufacturer. By lack of insight, the question won't be as detailed as with open source. And then we rely on the manufacturer. Some react quick and accurate, some don't even give you the the ability to ask questions. More often then not it costs lots of money and time. And of course meanwhile you will have to code workarounds yourself.
&lt;p/&gt;
&lt;span style="font-weight: bold;"&gt;Conclusions&lt;/span&gt;
&lt;p/&gt;
Despite the title I am not at all against closed and commercial software. Its just that &lt;span style="font-style:italic;"&gt;as a programmer&lt;/span&gt; I find it hardly ever worth the bother.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-4696658217318491088?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/4696658217318491088/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2010/08/open-source-why-bother-with-anything.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4696658217318491088'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4696658217318491088'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2010/08/open-source-why-bother-with-anything.html' title='Open source - why bother with anything else?'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-8711711376540029444</id><published>2010-04-01T10:29:00.004+02:00</published><updated>2010-04-01T10:41:56.020+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Another Wicket course coming up</title><content type='html'>I'll be doing the public &lt;a href="http://jteam.nl/training/apache-wicket-training.html"&gt;Wicket introduction course&lt;/a&gt; from &lt;a href="http://jweekend.co.uk/dev/JW703"&gt;jweekend&lt;/a&gt; again on May 27 and 28. For more information see the link above or drop me a note.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-8711711376540029444?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/8711711376540029444/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2010/04/another-wicket-course-coming-up.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8711711376540029444'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8711711376540029444'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2010/04/another-wicket-course-coming-up.html' title='Another Wicket course coming up'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-8324888867249992692</id><published>2010-04-01T10:14:00.003+02:00</published><updated>2010-04-01T10:28:09.227+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Wicket root mounts</title><content type='html'>Just in case you missed it: I recently wrote an article that shows how to mount pages on the root in Wicket. As a lot of time went into creation the example application I posted the article on &lt;a href="http://blog.jteam.nl/2010/02/24/wicket-root-mounts/"&gt;my employers blog&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-8324888867249992692?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/8324888867249992692/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2010/04/wicket-root-mounts.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8324888867249992692'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8324888867249992692'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2010/04/wicket-root-mounts.html' title='Wicket root mounts'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-6517881008987010441</id><published>2009-10-30T19:21:00.006+01:00</published><updated>2010-08-22T14:27:59.422+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>Backup home directory to USB harddisk</title><content type='html'>As I am keen to install Ubuntu 9.10 on my work laptop, its time to do an extra backup of my home directory. My mp3 player has about 30 Gb free so I'll use that.
&lt;p/&gt;
First attempt:&lt;div class="codeblock"&gt;&lt;code class="sh"&gt;cp -R /home/erik /media/H300/Backup/&lt;/code&gt;&lt;/div&gt;

You wont believe how slow this is! All those little pesky files, we'll need to aggregate them. 
&lt;p/&gt;
Second attempt:&lt;div class="codeblock"&gt;&lt;code class="sh"&gt;tar -cjf /media/H300/Backup/erik.tar.bz2 /home/erik&lt;/code&gt;&lt;/div&gt;
Waiting ... waiting ... Oops, that fails! The FAT file system on the mp3 player only supports files up to 2Gb.
&lt;p/&gt;
After a long but fruitless search in the documentation of tar (and zip) for multi-volume archives options, I suddenly thought of the simplest thing that could work:
&lt;div class="codeblock"&gt;&lt;code class="sh"&gt;tar -cj /home/erik | \
split -d -b 1G - /media/H300/Backup/erik.tar.bz2.part&lt;/code&gt;&lt;/div&gt;
This creates a bunch of 1Gbyte files ending in &lt;tt&gt;part00&lt;/tt&gt;, &lt;tt&gt;part01&lt;/tt&gt;, etc.
&lt;p/&gt;
You've got to love unix!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-6517881008987010441?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/6517881008987010441/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/10/backup-home-directory-to-usb-harddisk.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6517881008987010441'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6517881008987010441'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/10/backup-home-directory-to-usb-harddisk.html' title='Backup home directory to USB harddisk'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-8605085292765205693</id><published>2009-09-16T09:57:00.002+02:00</published><updated>2009-09-16T10:04:54.233+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Wicket do's and dont's</title><content type='html'>Just published an article on my employer's blog: &lt;a href="http://blog.jteam.nl/2009/09/16/wicket-dos-and-donts/"&gt;Wicket do's and dont's&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-8605085292765205693?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/8605085292765205693/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/09/wicket-dos-and-donts.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8605085292765205693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8605085292765205693'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/09/wicket-dos-and-donts.html' title='Wicket do&apos;s and dont&apos;s'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-2401429074625835492</id><published>2009-06-12T21:18:00.007+02:00</published><updated>2010-08-22T14:29:45.349+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>Extending my e-mail stack with Roundcube</title><content type='html'>I don't trust anyone with my most precious data: e-mail. That is why I run my own e-mail server. The server runs Ubuntu, Postfix, Dovecot and several tools for spam interception. I access my e-mail from several machines through the IMAP protocol (with TLS). Though any good IMAP client would do it is always Thunderbird (yes, even on my Mac).
&lt;p/&gt;
It is however not always feasible to have Thunderbird around. Time to add an IMAP web client! Candidates are &lt;a href="http://www.horde.org/imp/"&gt;IMP&lt;/a&gt;, &lt;a href="http://squirrelmail.org/"&gt;Squirrelmail&lt;/a&gt; and &lt;a href="http://roundcube.net/"&gt;Roundcube&lt;/a&gt;. All of these projects continuously release security updates. So unless you go for the next (beta) Ubuntu release, the packaged version is almost always a few versions behind.
&lt;p/&gt;
In the past I have already used IMP and Squirrelmail. IMP has a nice UI but was difficult to install. Squirrelmail looks really old, but has a huge amount of nice plugins.
&lt;p/&gt;
So it became Roundcube this time. Roundcube is not out of beta for that long. But it does look really slick, almost as if it is a desktop app. I did go with the Ubuntu package after all. Lacking more documentation I had to figure out for myself that I needed to use &lt;tt&gt;/etc/roundcube/apache.conf&lt;/tt&gt; as the basis for a virtual host file in the &lt;tt&gt;/etc/apache2/sites-available&lt;/tt&gt; directory. After some smaller configuration tweaks in &lt;tt&gt;/etc/roundcube/main.inc.php&lt;/tt&gt; I had the application as I wanted it.
&lt;p/&gt;
One more thing: Roundcube (like IMP and Squirrelmail) continuously open and close new IMAP connections. This makes the site amazingly slow. The fix: package imapproxy. This package starts an IMAP server that caches IMAP connections to another IMAP server. Some small config changes, and Roundcube was a lot quicker.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-2401429074625835492?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/2401429074625835492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/06/extending-my-e-mail-stack-with.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2401429074625835492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2401429074625835492'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/06/extending-my-e-mail-stack-with.html' title='Extending my e-mail stack with Roundcube'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-1548747502920430663</id><published>2009-05-25T10:54:00.007+02:00</published><updated>2010-08-22T14:30:17.048+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>More Wicket filter options</title><content type='html'>Wicket has this very clever idea to serve requests from a servlet &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/servlet/Filter.html"&gt;Filter&lt;/a&gt; instead of a &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/servlet/Servlet.html"&gt;Servlet&lt;/a&gt;. The brilliance of it is that you can serve pages on the root of your context, but still allow the servlet container to process requests that Wicket has nothing to do with.
&lt;p/&gt;
By default this works correct automatically. Incoming requests that are not recognized by Wicket are just passed through.
&lt;p/&gt;
However, there is an optimization that is not very well documented. When you already know that certain paths are never going to be handled by Wicket (for example because you configured a servlet for them), you can tell the Wicket filter to ignore those paths in the same file as where you define the servlets: in the &lt;tt&gt;web.xml&lt;/tt&gt;.
&lt;p/&gt;
Example &lt;tt&gt;web.xml&lt;/tt&gt; configuration: &lt;div class="codeblock"&gt;&lt;code class="xml"&gt;&amp;lt;filter&amp;gt;
 &amp;lt;filter-name&amp;gt;wicket.filter&amp;lt;/filter-name&amp;gt;
 &amp;lt;filter-class&amp;gt;org.apache.wicket.protocol.http.WicketFilter&amp;lt;/filter-class&amp;gt;
 &amp;lt;init-param&amp;gt;
  &amp;lt;param-name&amp;gt;ignorePaths&amp;lt;/param-name&amp;gt;
  &amp;lt;param-value&amp;gt;images/,rest/&amp;lt;/param-value&amp;gt;
 &amp;lt;/init-param&amp;gt;
&amp;lt;/filter&amp;gt;&lt;/code&gt;&lt;/div&gt;This will let the Wicket filter immediately pass through to the servlet container for all URLs that start with &lt;tt&gt;http://host/app-context/images/&lt;/tt&gt; and &lt;tt&gt;http://host/app-context/rest/&lt;/tt&gt;.
&lt;p/&gt;
Note: make sure the paths are comma separated without additional whitespace.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-1548747502920430663?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/1548747502920430663/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/05/more-wicket-filter-options.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1548747502920430663'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1548747502920430663'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/05/more-wicket-filter-options.html' title='More Wicket filter options'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-3739768968965086096</id><published>2009-04-13T12:44:00.003+02:00</published><updated>2009-04-13T12:49:06.513+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='monitoring'/><title type='text'>Simon 2 in beta</title><content type='html'>Java Simon, simple java monitoring, version 2 is in beta. I am quite proud of this because firstly the major change in the version was a result of my performance investigations, and secondly it contains my Spring integration code.

See the &lt;a href="http://code.google.com/p/javasimon/"&gt;java simon&lt;/a&gt; pages for news, downloads, etc.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-3739768968965086096?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/3739768968965086096/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/04/simon-2-in-beta.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3739768968965086096'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3739768968965086096'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/04/simon-2-in-beta.html' title='Simon 2 in beta'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-4819030954188364547</id><published>2009-04-06T15:16:00.006+02:00</published><updated>2010-08-22T14:31:10.409+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Wicket course preparations</title><content type='html'>Soon I will start teaching Wicket courses in The Netherlands. To prepare I spend some days in London with &lt;a href="http://www.jweekend.com/dev/JW703/"&gt;jWeekend&lt;/a&gt; teacher Cemal Bayramoglu (also know for organizing the &lt;a href="http://code.google.com/p/londonwicket/"&gt;London Wicket meetups&lt;/a&gt;). Most of our time we were at jWeekend's nice classroom, right in the middle of London, with close access to a very nice Thai restaurant, several coffee corners, etc.
&lt;p/&gt;
The first day we went through the entire course that &lt;a href="http://herebebeasties.com/"&gt;Al Maw&lt;/a&gt; and Cemal created. We had great fun discussing all the ins-and-outs. The second day we focused more on how the course should be taught.
&lt;p/&gt;
I am really glad my company &lt;a href="http://www.jteam.nl"&gt;JTeam&lt;/a&gt; also thought it was a good idea to team up with jWeekend. The course is excellent and as the number of eyes on the material has only increased, will be even more excellent in the future.
&lt;p/&gt;
Please contact as at &lt;a href="mailto:info@jteam.nl"&gt;info@jteam.nl&lt;/a&gt; if you are interested in getting a Wicket course.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-4819030954188364547?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/4819030954188364547/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/04/wicket-course-preparations.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4819030954188364547'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4819030954188364547'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/04/wicket-course-preparations.html' title='Wicket course preparations'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-2870364728379846595</id><published>2009-03-17T13:09:00.004+01:00</published><updated>2010-08-22T14:31:25.646+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='conference'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Amsterdam Wicket meetup March 24 2009</title><content type='html'>&lt;img align="right" src="http://www.eu.apachecon.com/page_attachments/0000/0160/MeetUps.gif"/&gt;The Wicket meetup has finally been given a date and time! March 24 2009, 19:00 - 22:00 in the Mövenpick hotel Amsterdam.
&lt;p/&gt;
&lt;a href="http://cwiki.apache.org/confluence/display/WICKET/Wicket+Community+meetups+-+Amsterdam"&gt;Presentations&lt;/a&gt;
&lt;p/&gt;
&lt;a href="https://spreadsheets.google.com/viewform?formkey=cDFlMTdSV3dKT1lkYUlVa2lWUFdkQXc6MA"&gt;Registration&lt;/a&gt; (do not register on the page above)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-2870364728379846595?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/2870364728379846595/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/03/amsterdam-wicket-meetup-march-24-2009.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2870364728379846595'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2870364728379846595'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/03/amsterdam-wicket-meetup-march-24-2009.html' title='Amsterdam Wicket meetup March 24 2009'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-6214354822319842397</id><published>2009-01-20T12:02:00.009+01:00</published><updated>2010-08-22T14:33:30.665+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='networking'/><title type='text'>Reliably sending email with Spring</title><content type='html'>&lt;span style="font-size:120%;font-weight:bold;"&gt;&lt;span style="font-style:italic;"&gt;Update 2009-09-12&lt;/span&gt;: I no longer recommend this library. Please see the comments.&lt;/span&gt;
&lt;p/&gt;
My colleague &lt;a href="http://www.gridshore.nl/author/allard/"&gt;Allard&lt;/a&gt; just pointed me to an old but very useful library: &lt;a href="http://ha-javamail.sourceforge.net/"&gt;HA-JavaMail&lt;/a&gt;.
&lt;p/&gt;
The email sender that the JVM provides has some serious shortcomings. It does not automatically open a new connection when the connection was closed and you can forward your e-mail to 1 SMTP server only. Furthermore, it is not so fast. HA-JavaMail circumvents these problems by wrapping the JVM implementation.
&lt;p/&gt;
In this tiny article I explain how you configure HA-JavaMail from Spring. First make sure you have the HA-JavaMail jar, the Spring jar, the JavaMail &lt;tt&gt;mail.jar&lt;/tt&gt; and the JAF &lt;tt&gt;activation.jar&lt;/tt&gt; library on your classpath. The latter two are available by default in full JEE containers.
&lt;p/&gt;
Convert your mail sender bean declaration from this:&lt;div class="codeblock"&gt;&lt;code class="xml"&gt;&amp;lt;bean id="mailSender"
    class="org.springframework.mail.javamail.JavaMailSenderImpl"&gt;
  &amp;lt;property name="mail.host" value="localhost"/&gt;
&amp;lt;/bean&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
to something like this:&lt;div class="codeblock"&gt;&lt;code class="xml"&gt;&amp;lt;bean id="mailSender"
    class="org.springframework.mail.javamail.JavaMailSenderImpl"&gt;
  &amp;lt;property name="javaMailProperties"&gt;
    &amp;lt;value&gt;
      mail.transport.host=localhost,mail.example.com
      mail.transport.pool-size=1
      mail.transport.connect-timeout=0
      mail.transport.connect-retry-period=60
      mail.transport.sender-strategy=net.sf.hajavamail.SimpleSenderStrategy
    &amp;lt;/value&gt;
  &amp;lt;/property&gt;
&amp;lt;/bean&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
Note that the example adds a second SMTP server that will be used when the first is unreachable. Only a value for &lt;tt&gt;mail.transport.host&lt;/tt&gt; is required. The other values shown here are also the defaults.
&lt;p/&gt;
That’s it. You now have a much more reliable and faster email service.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-6214354822319842397?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/6214354822319842397/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/01/reliably-sending-email-with-spring.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6214354822319842397'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6214354822319842397'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/01/reliably-sending-email-with-spring.html' title='Reliably sending email with Spring'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-3731218577052692129</id><published>2009-01-12T11:20:00.007+01:00</published><updated>2010-08-22T14:36:25.349+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='monitoring'/><title type='text'>Runtime monitoring libraries for Java</title><content type='html'>Followers of this blog may have noticed an emphasis on monitoring lately. This is because &lt;a href="http://www.jteam.nl"&gt;my employer&lt;/a&gt; has decided to give me some time to investigate monitoring and create new components to ease the use of monitoring (in particular for Wicket applications). Blogging about the results is one of the requisites of this exercise.
&lt;p/&gt;
This article lists all open source Java runtime monitoring tools I could find. By runtime monitoring I mean something that can left active in a production environment and measures things that occur &lt;span style="font-style:italic;"&gt;inside&lt;/span&gt; the JVM.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;&lt;a href="http://jamonapi.sourceforge.net/"&gt;Jamon&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
Jamon started in 2003 and is probably the oldest monitoring framework listed here. Although Jamon’s last release is from 2007, it is quite stable and used a lot; Sourceforge statistics show almost a 1000 downloads per month. Jamon is the work of a single person.
&lt;p/&gt;
Features:&lt;ul&gt;&lt;li&gt;Programmatic access to start and stop timers, timers use milliseconds.&lt;/li&gt;&lt;li&gt;In memory database for aggregate statistics (no details are kept).&lt;/li&gt;&lt;/li&gt;Supports monitors that keep track of data in any unit (e.g. dollar value of a transaction).&lt;/li&gt;&lt;li&gt;Can aggregate statistics on data ranges to provide a more detailed view when this is necessary.&lt;/li&gt;&lt;li&gt;Extensions to easily configure monitoring in software such as JDBC drivers, several servlet containers/application servers.&lt;/li&gt;&lt;li&gt;A JSP page to view the realtime data.&lt;/li&gt;&lt;/ul&gt;
Jamon does not support a persistence mechanism and there is therefore no way to analyze historic data. To fill this gap you can use &lt;a href="https://sourceforge.net/projects/jarep"&gt;Jarep&lt;/a&gt; which provides several Jamon exports. Another way is to export the data through &lt;a href="http://tusharkhairnar.blogspot.com/2008/09/jamon-data-for-java-applications.html"&gt;JMX&lt;/a&gt;.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;&lt;a href="http://code.google.com/p/javasimon/"&gt;Java Simon&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
Simon started last year as a direct competitor of Jamon. It is much faster, measures in nanoseconds and has a hierarchy of monitors so that you can enable/disable a tree of monitors. Simon is still in active development. Be prepared for API changes in Simon 2.0 which is to be released somewhen before summer 2009.
&lt;p/&gt;
Features:&lt;ul&gt;&lt;li&gt;Programmatic access to start and stop timers, timers use nanoseconds.&lt;/li&gt;&lt;li&gt;In memory database for aggregate statistics (no details are kept).&lt;/li&gt;&lt;/li&gt;Supports stopwatches and counters.&lt;/li&gt;&lt;li&gt;Like Jamon, has a JDBC monitoring proxy driver&lt;/li&gt;&lt;/ul&gt;
The Simon team is working on a JMX export and has yet to be determined plans for a persistence mechanism.
&lt;p/&gt;
Currently Simon does not provide anything to view the statistics. Again you can use &lt;a href="https://sourceforge.net/projects/jarep"&gt;Jarep&lt;/a&gt; for this.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;&lt;a href="http://code.google.com/p/usemon/"&gt;Usemon&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
Usemon seems to target monitoring of large clusters with support for tracing messages through the cluster. Measurements are send to a collector which stores it in a database.
&lt;p/&gt;
Features:&lt;ul&gt;&lt;li&gt;Byte code manipulation to add timers to configured EJBs, Servlets, MDBs, custom defined POJOs, and classes implementing the SqlStatement, MessageSender and TopicSender interfaces.&lt;/li&gt;&lt;li&gt;Measures method invocation durations, exception counts and JVM information such as heap size.&lt;/li&gt;&lt;li&gt;Measurements are aggregated for 1 minute and then actively send to a central collector for storage.&lt;/li&gt;&lt;li&gt;A nice graphic interface for viewing the data (the project page is very vague about the details).&lt;/li&gt;&lt;li&gt;A clever database to handle huge amounts of data while still allowing efficient retrieval.&lt;/li&gt;&lt;/ul&gt;
The Usemon site does not offer a lot of information. You'll probably need to just try it out to get more information.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;&lt;a href="http://moskito.anotheria.net/"&gt;Moskito&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
I found it a bit difficult to quickly grap Moskito. The website only shows a few use cases and leaves the rest undocumented.
&lt;p/&gt;
Features:&lt;ul&gt;&lt;li&gt;Easy monitoring of servlets.&lt;/li&gt;&lt;li&gt;Measurements are aggregated per interval (e.g. 5 miniutes). Afterwards you can do something with the data (e.g. log it or send to a central location).&lt;/li&gt;&lt;li&gt;A nice very complete looking web interface.&lt;/li&gt;&lt;/ul&gt;
A nice idea (that has not been implemented) is the support for virtual monitors, a monitor that shows the aggregated statistics for several other monitors.
&lt;p/&gt;
Unfortunately I could not figure out how to instrument code except for servlets (extending a base class). The project website suggest that the project has been abandoned, so Moskito is not likely a good choice.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;&lt;a href="http://commons.apache.org/sandbox/monitoring/"&gt;Commons monitoring&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
Commons monitoring is a Apache sandbox project. The website has good documentation but the newest information is from March 2008. SVN shows quite some activity so I am curious what the first release will bring.
&lt;p/&gt;
Features:&lt;ul&gt;&lt;li&gt;Programmatic access to start and stop timers, timers use nanoseconds.&lt;/li&gt;&lt;li&gt;Support for AOP instrumentation.&lt;/li&gt;&lt;li&gt;In memory database for aggregate statistics (no details are kept).&lt;/li&gt;&lt;li&gt;Supports timers, counter (only increases) and gauges (increases and decreases).&lt;/li&gt;&lt;li&gt;Extensions to export data through a servlet in several formats, including HTML.&lt;/li&gt;&lt;/ul&gt;
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;&lt;a href="http://jetm.void.fm/"&gt;JETM&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
JETM (Java™ Execution Time Measurement Library) is again a library for doing raw measurements.
&lt;p/&gt;
Features:&lt;ul&gt;&lt;li&gt;Programmatic access to start and stop timers, timers use nanoseconds.&lt;/li&gt;&lt;li&gt;Very nice: a Spring namespace to configure monitors on your beans.&lt;/li&gt;&lt;li&gt;In memory database for aggregate statistics (no details are kept).&lt;/li&gt;&lt;li&gt;In memory database for aggregate statistics (details are kept until it is time to calculate the aggregates).&lt;/li&gt;&lt;li&gt;Several renderers to export data.&lt;/li&gt;&lt;li&gt;Web interface to see current data.&lt;/li&gt;&lt;li&gt;Integrates with several projects as Spring, &lt;a href="https://rrd4j.dev.java.net/"&gt;RRD4J&lt;/a&gt; and JMX.&lt;/li&gt;&lt;/ul&gt;
Although JETM looks nice, it has been silent for more then a year, and simple questions on the mailing list are not answered.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;&lt;a href="http://code.google.com/p/broadway-monitor/"&gt;Project Broadway&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
Broadway provides an entirely different kind of monitoring. The idea of Broadway is to define events of interest (for example in Groovy) on which can be reacted with some action.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-3731218577052692129?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/3731218577052692129/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/01/runtime-monitoring-libraries-for-java.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3731218577052692129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3731218577052692129'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/01/runtime-monitoring-libraries-for-java.html' title='Runtime monitoring libraries for Java'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-2684790941784562755</id><published>2009-01-10T10:49:00.006+01:00</published><updated>2010-08-22T14:39:41.947+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='monitoring'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Improving Jamon’s performance</title><content type='html'>I was browsing through Jamon’s code to see why it is so much slower under high contention then Simon (see my &lt;a href="http://day-to-day-stuff.blogspot.com/2008/12/evaluating-simon.html"&gt;previous article&lt;/a&gt;). In this article I will present these differences and show you a way to speed up Jamon right now.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Differences&lt;/span&gt;&lt;br/&gt;
Jamon has more features then Simon like listeners and data ranges. This obviously need some computing, if only to see if they are used. Another interesting difference is that Jamon uses &lt;tt&gt;double&lt;/tt&gt;’s for aggregate statistics, whereas Simon uses &lt;tt&gt;long&lt;/tt&gt;’s. I don’t really see the advantage of &lt;tt&gt;double&lt;/tt&gt;’s (please surprise me) and it is probably a bit slower as well.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Synchronization&lt;/span&gt;&lt;br/&gt;
Jamon uses 5 synchronization points during a timing operation. There are 3 to get and create the monitor: on the MonitorFactory method, on the map of all monitor datas and on the map of all data range definitions. The last 2 synchronization points are for starting and stopping the timer (on the monitor data).
&lt;p/&gt;
Simon (does not support data ranges) uses only 3 synchronization points: one for getting the monitor (on the SimonManager method) and 2 for starting and stopping the timer (on the timer itself).&lt;br/&gt;
Simon 2.0 no longer needs synchronization on starting a timer and therefore only needs 2 synchronization points.
&lt;p/&gt;
Jamon defines its maps for monitors and data ranges as &lt;tt&gt;Collections.synchronizedMap(new HashMap(50))&lt;/tt&gt;. However, it is not necessary to synchronize on these maps when the code that uses these maps is already synchronized on the MonitorFactory (in method &lt;tt&gt;getMonitor&lt;/tt&gt;). Unfortunately when the synchronization wrapper is removed, all code that uses these maps will need to be analyzed to see if they properly synchronize.
&lt;p/&gt;
Another solution would be to use a map that needs less synchronization: Java 5’s ConcurrentHashMap! Luckily Jamon provides a method to change the map implementation for exactly this reason. I included the following line in my test application (from my previous post):&lt;div class="codeblock"&gt;&lt;code class="java"&gt;MonitorFactory.setMap(new ConcurrentHashMap(200));&lt;/code&gt;&lt;/div&gt;&lt;br/&gt;
And these are the results. The measurements are in ms, the scale is logarithmic.
&lt;img src="http://www.grons.nl/day2daystuff/20090110_SimonJamonMonitoringOverhead.png"/&gt;
&lt;p/&gt;
Quite astonishing: Jamon is no longer 5 up to 200 times slower, but just a bit slower for low contention, up to 15 times slower under heavy contention, and 2 times slower under extreme contention. Note that only the map for monitors was changed, the map for range definitions (always empty in my test) was not changed.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Conclusions&lt;/span&gt;&lt;br/&gt;
Even though Jamon is quite a bit slower under heavy contention, there is room for improvement. By calling a single method, you can make Jamon 20 times faster right now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-2684790941784562755?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/2684790941784562755/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/01/improving-jamons-performance.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2684790941784562755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2684790941784562755'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/01/improving-jamons-performance.html' title='Improving Jamon’s performance'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-3188476353738527901</id><published>2009-01-07T13:45:00.006+01:00</published><updated>2010-11-01T19:19:09.349+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='conference'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>‘Effective Wicket’ presentation now on Vimeo</title><content type='html'>My &lt;a href="http://www.nljug.org/pages/events/content/jfall_2008/sessions/00018/"&gt;Effective Wicket&lt;/a&gt; talk on &lt;a href="http://www.nljug.org/"&gt;NL-JUG&lt;/a&gt;’s J-Fall conference of 2008-11-12 is now online! Watch the presentation (Dutch spoken) on Vimeo: &lt;a href="http://vimeo.com/2747764"&gt;part 1&lt;/a&gt; and &lt;a href="http://vimeo.com/2747874"&gt;part 2&lt;/a&gt; (provided by &lt;a href="http://www.bachelor-ict.nl/erik-van-oosten"&gt;bachelor-ict.nl&lt;/a&gt;).
&lt;p/&gt;
Feel free to download the &lt;a href="http://www.grons.nl/~erik/pub/20081112%20Effective%20Wicket.pdf"&gt;slides of Effective Wicket&lt;/a&gt; (&lt;a href="http://dl.dropbox.com/u/14180038/20081112%20Effective%20Wicket.pdf"&gt;mirror&lt;/a&gt;) (PDF 4.1 Mb). If you want to see the notes skip to page 55.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-3188476353738527901?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/3188476353738527901/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/01/effective-wicket-presentation-now-on.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3188476353738527901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3188476353738527901'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2009/01/effective-wicket-presentation-now-on.html' title='‘Effective Wicket’ presentation now on Vimeo'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5654132183612571496</id><published>2008-12-24T13:54:00.032+01:00</published><updated>2010-08-22T14:51:59.544+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='monitoring'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Evaluating Simon - Java monitoring</title><content type='html'>Recently a new monitoring kid appeared on the block: &lt;a href="http://code.google.com/p/javasimon/"&gt;Java Simon&lt;/a&gt; (not to be confused with &lt;a href="http://www.dejal.com/simon/"&gt;Dejal's Simon&lt;/a&gt; &lt;a href="http://www.sanctuarysimon.org/index.php"&gt;or&lt;/a&gt; &lt;a href="http://www.chunkypig.com/games/free-simon-game-online-download.php"&gt;some&lt;/a&gt; &lt;a href="http://cbusters.com/index.php?option=com_content&amp;task=view&amp;id=14&amp;Itemid=27"&gt;other&lt;/a&gt; &lt;a href="http://www.palladiumtechs.com/simon"&gt;simons&lt;/a&gt;).
&lt;p/&gt;
Simon claims to be the successor of &lt;a href="http://jamonapi.sourceforge.net/"&gt;JAMon&lt;/a&gt;. As I just started a project to improve the documentation of Jamon, and integrate this better with Wicket projects, I thought this would be a good time to evaluate Simon and also to compare it to Jamon. Here are the results.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Features&lt;/span&gt;&lt;br/&gt;
Simon (only just started) can measure task durations (min, max, count, maximum concurrency and some timestamps) and has task counters. Other statistics can be added (supported now: standard deviation). The JDBC proxy driver can be used to monitor database usage. The monitors are grouped in a tree. You can disable each monitor individually, or per complete subtree.
&lt;p/&gt;
Jamon adds the ability to measure in any unit (for example euro value of a transaction) and has quite a few monitors for Tomcat, Jetty, etc. Jamon does not organize monitors in a tree, so monitors can be disabled individually, or all at the same time.
&lt;p/&gt;
So Jamon clearly wins on the feature front, but if you're just into the basic stuff Simon is fine.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Documentation&lt;/span&gt;&lt;br/&gt;
One of the weak points of Jamon is its documentation (hence my little project). Simon on the other hand starts with a good set of wiki pages. If these are maintained properly, Simon is the clear winner here.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Simon interface&lt;/span&gt;&lt;br/&gt;
Simon's interface is fairly simple. One gets a monitor through the singleton &lt;tt&gt;SimonManager&lt;/tt&gt;, and call &lt;tt&gt;start&lt;/tt&gt; and &lt;tt&gt;stop&lt;/tt&gt; on it:
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;Stopwatch simon =
    SimonManager.getStopwatch("simon.test").start();
// Do something
simon.stop();&lt;/code&gt;&lt;/div&gt;
(I will not discuss the Jamon interface further, but making a measurement is virtually the same.)
&lt;p/&gt;
Getting data out of a Simon monitor (a simon) is a bit more difficult. You can just get the measurements like this:
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;Stopwatch simon = SimonManager.getStopwatch("simon.test");
long hitCount = simon.getCounter();
long max = simon.getMax();
long total = simon.getTotal();
simon.reset();&lt;/code&gt;&lt;/div&gt;
But that would be very thread unsafe. As each method is synchronized separately, the three measurements may represent different moments in time.
&lt;p/&gt;
The idiomatic solution would be to use method &lt;tt&gt;sample&lt;/tt&gt;:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;Stopwatch simon = SimonManager.getStopwatch("simon.test");
Map&amp;lt;String, String&gt; sample = simon.sample(true);
long hitCount = Long.parseLong(sample.get("counter"));
long max = Long.parseLong(sample.get("max"));
long total = Long.parseLong(sample.get("total"));&lt;/code&gt;&lt;/div&gt;
This is more how it should be except for the boolean flag [F3, N7], and the return type of &lt;tt&gt;sample&lt;/tt&gt;: a map! First of all, maps are very memory hungry [G21], secondly I have to assume that the values are longs [CE6] and parse them back from a String [GE37], and thirdly the keys are magic values [CE6, G25].
&lt;p/&gt;
Here is another approach:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;Stopwatch simon = SimonManager.getStopwatch("simon.test");
long hitCount;
long max;
long total;

synchronized (simon) {
  hitCount = simon.getCounter();
  max = simon.getMax();
  total = simon.getTotal();
  simon.reset();
}&lt;/code&gt;&lt;/div&gt;
Ouch. This works but uses client-side locking. That this is possible at all is a mistake in the Java language (as admitted by James Gosling, sorry, I lost the reference). Even so, Simon should not make it possible for clients to get the same lock that is used to protect the monitor data. This should be the only way to get actual data.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Recommendation&lt;/span&gt;: move all data retrieval methods (&lt;tt&gt;getMax()&lt;/tt&gt;, &lt;tt&gt;getTotal()&lt;/tt&gt;, etc.) from the monitor to an immutable data class, e.g. &lt;tt&gt;StopwatchMeasurements&lt;/tt&gt;. Method &lt;tt&gt;sample&lt;/tt&gt; should return instances of this class, or subclasses when a different analyzer was configured.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2008-12-25&lt;/span&gt;: added following section on visiting all simons.&lt;br/&gt;
&lt;span style="font-style:italic;"&gt;Update 2009-01-0&lt;/span&gt;: the iterator now actually works.
&lt;p/&gt;
As I am using &lt;a href="http://sourceforge.net/projects/jarep"&gt;Jarep&lt;/a&gt; to browse through historic measurements, I want to expose the Simon data just like Jarep's servlet exposer for Jamon does. In other words: we need to visit each simon and extract the data as described above.
&lt;p/&gt;
There are 2 approaches to visit all nodes. First is to get all simon names from &lt;tt&gt;SimonManager&lt;/tt&gt; and then request them one by one. The other is to walk the tree of simons. I choose the latter. Unfortunately there is no tree iterator so I created one with commons-collections:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;Simon rootSimon = SimonManager.getRootSimon();
ObjectGraphIterator simonIterator =
  new ObjectGraphIterator(rootSimon, new Transformer() {
    private Object previous;
    public Object transform(Object input) {
      if (input == previous) {
        previous = null;
        return input;
      } else {
        previous = input;
        Simon currentSimon = (Simon) input;
        return new IteratorChain(
          new SingletonIterator(currentSimon),
          currentSimon.getChildren().iterator());
      }
    }});&lt;/code&gt;&lt;/div&gt;
Admittedly ugly, but it works. Still there is one glaring bug here. The children are stored in a regular TreeSet. When we request the children through &lt;tt&gt;getChildren&lt;/tt&gt; we get a collection that wraps the original set. Unfortunately that means that when an simon is added while we iterate the complete simon tree, we get a &lt;tt&gt;ConcurrentModificationException&lt;/tt&gt;! Not quite acceptable.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Recommendation&lt;/span&gt;: store children of a simon in a &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/CopyOnWriteArrayList.html"&gt;CopyOnWriteArrayList&lt;/a&gt; instead of a TreeSet. Another option is change the complete set of simons in the manager into a ConcurrentMap and provide an iterator over that.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Performance&lt;/span&gt;
To measure the performance of Simon and Jamon I wrote a &lt;a href="http://www.grons.nl/code/StopwatchOverheadPerformanceTest.html"&gt;test application&lt;/a&gt; that mimics a random web applications. It has a variable number of threads to mimic the number of concurrent requests that are being handled. There is one monitor to measure the duration of the entire request, one monitor to measure a specific request, one monitor to measure the service layer, and 0 to 3 monitors to measure the database layer.
&lt;p/&gt;
The test application was run from IntelliJ with Soy Latte 1.0.3 (a 1.6 JDK, part of the OpenJDK for Debian) on a 2.4GHz Intel Core 2 Duo.
&lt;p/&gt;
The measurements below display the time spend in obtaining, starting and stopping a single monitor. Measurements were done in nanoseconds, but are displayed here in milliseconds (logarithmic scale).
&lt;p/&gt;
&lt;img src="http://www.grons.nl/day2daystuff/20081224_SimonJamonMonitoringOverhead.png"/&gt;
&lt;p/&gt;
Simon's performance is pretty good! The overhead in my test application doesn't come anywhere near the millisecond range even in highly concurrent situations. Jamon is not so bad either, even though its 5 to 200 times slower, depending on thread contention. With 5 monitors in a request, the overhead per request slowly grows to 0.7 ms for Simon, and quickly grows to 12ms for Jamon.
&lt;p/&gt;
Note: the test application does nothing else but getting monitors and doing measurements. So the right side of the chart is unrealistic for most environments.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Improving the performance&lt;/span&gt;&lt;br/&gt;
Even though the performance is pretty good. I think it can be improved further. I investigated two potential performance improvements:&lt;ul&gt;&lt;li&gt;Remove the ThreadLocal variable in StopWatchImpl.&lt;/li&gt;&lt;li&gt;Replace synchronized HashMap with a ConcurrentHashMap in EnabledManager.&lt;/li&gt;&lt;/ul&gt;
&lt;span style="font-weight:bold;"&gt;Remove the ThreadLocal variable in StopWatchImpl&lt;/span&gt;
Stopwatch method &lt;tt&gt;start&lt;/tt&gt; stores the start time in a thread local. When you call &lt;tt&gt;stop&lt;/tt&gt; the start time is retrieved and used to calculate the duration.
&lt;p/&gt;
ThreadLocals have seen an enormous performance improvement in recent JVMs, but you are still better off without.
&lt;p/&gt;
The idea is as follows: method &lt;tt&gt;start&lt;/tt&gt; will no longer return the monitor itself but an object that contains only the start time and a &lt;tt&gt;stop&lt;/tt&gt; method. Lets call it a &lt;tt&gt;StopwatchMeasurement&lt;/tt&gt;. Only in the stop method will it be necessary to synchronize and update the counters.&lt;br/&gt;
The advantages are:&lt;ul&gt;&lt;li&gt;We no longer need to synchronize the &lt;tt&gt;start&lt;/tt&gt; method.&lt;/li&gt;&lt;li&gt;We no longer need the thread local variable to store the start time.&lt;/li&gt;&lt;li&gt;We no longer need the &lt;tt&gt;start(key)&lt;/tt&gt; and &lt;tt&gt;stop(key)&lt;/tt&gt; variants.&lt;/li&gt;&lt;li&gt;The stopwatch is no longer susceptible for memory leak when a malicious client keeps starting monitors in new threads, but never stops them (someone forgot a finally).&lt;/li&gt;&lt;li&gt;The stopwatch is no longer susceptible for memory leak when a malicious client keeps starting monitors with &lt;tt&gt;start(key)&lt;/tt&gt; with a new key, but never stops them (again, someone forgot a finally).&lt;/li&gt;&lt;/ul&gt;
Disadvantage is that we need to use something else for the &lt;tt&gt;active&lt;/tt&gt; counter. An &lt;tt&gt;AtomicLong&lt;/tt&gt; would be very suitable as it uses hardware assisted atomic operations (and therefore has no synchronization overhead).&lt;br/&gt;
If it is desirable to keep the &lt;tt&gt;firstUsage&lt;/tt&gt; and &lt;tt&gt;lastUsege&lt;/tt&gt; timestamps (I don't use them), these can lift along with the start time and be stored during the &lt;tt&gt;stop&lt;/tt&gt; method. Note: this may be unacceptable if you need information on long running operations.
&lt;p/&gt;
To test it out I &lt;a href="http://www.grons.nl/code/StopwatchImplImproved.html"&gt;hacked StopwatchImpl&lt;/a&gt; to return a &lt;tt&gt;StopwatchMeasurement&lt;/tt&gt; from the &lt;tt&gt;start&lt;/tt&gt; method. If you look at the implementation you see that &lt;tt&gt;StopwatchMeasurement&lt;/tt&gt; implements &lt;tt&gt;Stopwatch&lt;/tt&gt; so that the idiomatic usage still works:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;Stopwatch simon =
    SimonManager.getStopwatch("simon.test").start();
// Do something
simon.stop();&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
Of course this is a kludge that should be improved upon for a real implementation. In addition, I made no attempt to keep the active counter and the first/lastUsage timestamps.
&lt;p/&gt;
Here are the results (again in ms per measurement):
&lt;p/&gt;
&lt;img src="http://www.grons.nl/day2daystuff/20081224_SimonImprovedStopwatch.png"/&gt;
&lt;p/&gt;
Not bad! Although the results are quite dramatic for the higher thread counts, it still shaves off 20% in situations with less contention.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Recommendation&lt;/span&gt;: remove the thread local from &lt;tt&gt;StopwatchImpl&lt;/tt&gt; and change the API as described above.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Replace synchronized HashMap with a ConcurrentHashMap in EnabledManager&lt;/span&gt;&lt;br/&gt;
The second thing I noticed is that getting a monitor from the SimonManager (the &lt;tt&gt;SimonManager.getStopwatch("simon.test")&lt;/tt&gt; part) happens in a synchronized block and uses a plain HashMap as store. Since Java 5 there is a ConcurrentHashMap implementation which supposedly performs better in almost any circumstances.
&lt;p/&gt;
Lets see. I remove some synchronized keywords, changed the map type, and made the code to add new monitors thread safe. This resulted in a modified &lt;a href="http://www.grons.nl/code/SimonManagerEnabledImproved.html"&gt;EnabledManager&lt;/a&gt;.
&lt;p/&gt;
Here are the results. The graph displays a few attempts that differ in the concurrency level parameter to the &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ConcurrentHashMap.html#ConcurrentHashMap(int,%20float,%20int)"&gt;ConcurrentHashMap constructor&lt;/a&gt;.
For more accuracy this graph displays the &lt;span style="font-style:italic;"&gt;improvement&lt;/span&gt; in nanoseconds.
&lt;p/&gt;
&lt;img src="http://www.grons.nl/day2daystuff/20081224_SimonImprovedManager.png"/&gt;
&lt;p/&gt;
To my big surprise there is no measurable difference with modest contention. During high contention there is a reasonable positive effect that is suddenly converted to a negative effect for very high contention. Only when the currency level is set very high, does the effect stay positive.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Recommendation&lt;/span&gt;: don't change the manager.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Conclusions&lt;/span&gt;
Although Simon is only young, it is off with a good start. Its clearly faster then Jamon and also on the documentation front Simon wins. Jamon has much more features so it is still a valid choice. When the recommendations in this article are followed, Simon could be better still.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Discussion&lt;/span&gt;&lt;br/&gt;
Follow the discussion of this evaluation on the &lt;a href="http://groups.google.com/group/javasimon/t/93a8e7df71877856"&gt;Java Simon mailing list&lt;/a&gt;.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;References&lt;/span&gt;&lt;br/&gt;
Code smells from &lt;a href="http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882"&gt;Clean Code&lt;/a&gt; by &lt;a href="http://blog.objectmentor.com/articles/category/uncle-bobs-blatherings"&gt;Robin C. Martin&lt;/a&gt;:&lt;br/&gt;
[F3] Flag arguments&lt;br/&gt;
[N7] Names should describe side effects&lt;br/&gt;
[G14] Feature Envy&lt;br/&gt;
[G21] Understand the algorithm&lt;br/&gt;
[G25] Replace magic numbers with named constants&lt;br/&gt;
&lt;p/&gt;
Code smells by me:&lt;br/&gt;
[CE6] Incomplete contract&lt;br/&gt;
[GE37] Unnecessary conversions&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5654132183612571496?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5654132183612571496/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/12/evaluating-simon.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5654132183612571496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5654132183612571496'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/12/evaluating-simon.html' title='Evaluating Simon - Java monitoring'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-1420258568070421146</id><published>2008-12-15T15:06:00.004+01:00</published><updated>2010-08-22T14:52:17.677+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='opinion'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Don't use Intellij</title><content type='html'>IntelliJ is okay, but it has one bug (for more then 4 years) that absolutely drives me nuts: its steals focus, big time. And not just once either. It can steal the focus for at least 4 times within 30 seconds!
&lt;p/&gt;
If are thinking about using IntelliJ: don't do it!
&lt;p/&gt;
If you are already hooked up: please vote for &lt;a href="http://www.jetbrains.net/jira/browse/IDEABKL-3849"&gt;this bug&lt;/a&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-1420258568070421146?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/1420258568070421146/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/12/dont-use-intellij.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1420258568070421146'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1420258568070421146'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/12/dont-use-intellij.html' title='Don&apos;t use Intellij'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-216338703120538577</id><published>2008-12-06T22:19:00.004+01:00</published><updated>2010-08-22T14:52:34.174+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>Giving up on compiling a linux module</title><content type='html'>I recently build my new home server PC. The motherboard is a D94GCLF, a small mini-ITX (17x17cm) from intel with an Atom processor. Unfortunately, the on board ethernet card, a RTL8102EL is not supported by Ubuntu 8.10. The r8169 kernel module works but it drops most received packets so its not as fast as it should be.
&lt;p/&gt;
Realtek does provide the source to build the module yourself. Unfortunately, the instructions are a bit sparse. Too say the least. After a 7 hour struggle, installing kernel and header sources, lots of googling, making symbolic links here and there I finally compiled a .ko file. Insmod refused to load it.
&lt;p/&gt;
So I gave up.
&lt;p/&gt;
Now my new server is up to full speed again. If you can't solve your problem in software, add hardware! I put in an old 100Mbit/s ethernet card, and all is well again.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-216338703120538577?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/216338703120538577/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/12/giving-up-on-compiling-linux-module.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/216338703120538577'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/216338703120538577'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/12/giving-up-on-compiling-linux-module.html' title='Giving up on compiling a linux module'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5831016212020950185</id><published>2008-11-17T11:21:00.006+01:00</published><updated>2010-08-22T14:53:08.607+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><title type='text'>Making Mac's open/save dialog fast again</title><content type='html'>Out of nowhere I had to wait 20 to 40 seconds on my year old iMac before I could open or save a file. Some people reported that iDisk could be the problem, but I don't even have a mac-account! However, those reports gave me a hint.
&lt;p/&gt;
Indeed, a &lt;code&gt;ls /Volumes/&lt;/code&gt; showed this: &lt;div class="codeblock"&gt;&lt;code&gt;MacintoshHD    Wuala    ._Ipod_van_Corine&lt;/code&gt;&lt;/div&gt;

There it is! My girlfriend's IPod was not in sight, and I dumped Wuala because it stops working after a hibernate. A simple &lt;div class="codeblock"&gt;&lt;code&gt;sudo rm /Volumes/Wuala
sudo rm /Volumes/._Ipod_van_Corine&lt;/code&gt;&lt;/div&gt;
and all was well again.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5831016212020950185?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5831016212020950185/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/11/making-mac-fast-again.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5831016212020950185'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5831016212020950185'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/11/making-mac-fast-again.html' title='Making Mac&apos;s open/save dialog fast again'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-3413169730139163292</id><published>2008-11-13T10:14:00.009+01:00</published><updated>2010-08-22T14:53:24.703+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='conference'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Effective Wicket presentation</title><content type='html'>Yesterday was J-Fall, the fall conference of the &lt;a href="http://www.nljug.org/"&gt;NL-JUG&lt;/a&gt;. One of the presentations was my &lt;a href="http://www.nljug.org/pages/events/content/jfall_2008/sessions/00018/"&gt;Effective Wicket&lt;/a&gt; talk. Feel free to download the &lt;a href="http://www.grons.nl/~erik/pub/20081112%20Effective%20Wicket.pdf"&gt;slides of Effective Wicket&lt;/a&gt; (&lt;a href="http://content6.wuala.com/contents/%2Fbrio%2Fpublic%2F20081112%2BEffective%2BWicket.pdf?lang=en"&gt;mirror&lt;/a&gt;) (PDF 4.1 Mb). If you want to see the notes skip to page 55.
&lt;p/&gt;
If you want the original material (code/images etc.) for your own presentation, please drop me a note. Its all creative commons, copyright by JTeam.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 2009-01-07&lt;/span&gt;: Watch the presentation (Dutch spoken) on Vimeo: &lt;a href="http://vimeo.com/2747764"&gt;part 1&lt;/a&gt; and &lt;a href="http://vimeo.com/2747874"&gt;part 2&lt;/a&gt; (provided by &lt;a href="http://www.bachelor-ict.nl/erik-van-oosten"&gt;bachelor-ict.nl&lt;/a&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-3413169730139163292?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/3413169730139163292/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/11/effective-wicket-presentation.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3413169730139163292'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3413169730139163292'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/11/effective-wicket-presentation.html' title='Effective Wicket presentation'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-3668194188095716821</id><published>2008-10-15T10:49:00.023+02:00</published><updated>2010-08-22T20:26:07.663+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Wicket extreme consistent URLs</title><content type='html'>Setting up a page to be behind a particular URL (aka mounting) in Wicket is fairly easy. Daan recently wrote &lt;a href="http://stuq.nl/weblog/2008-06-20/create-restful-urls-with-wicket"&gt;REST like URLs for Wicket&lt;/a&gt; in which this is nicely explained. However, consistently keeping nice URLs, for example after a form submit, is a whole lot harder. So hard in fact, that even Wicket champion Igor believes it is currently &lt;a href="http://www.nabble.com/Can-I-get-a-%27Nice-URL%27-when-form-validation-fails--td11420378.html#a11440523"&gt;not possible&lt;/a&gt; (it is an old post). His reasoning is of course completely correct, but he forgot one thing. Let me explain.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;The problem&lt;/span&gt;&lt;br/&gt;
Many web applications have a login page. On it there is a form where you fill in your username and password. Suppose the page is mounted to:&lt;div class="codeblock"&gt;&lt;code&gt;http://example.com/login&lt;/code&gt;&lt;/div&gt; with code like this in the application's init method:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;mountBookmarkablePage("login", LoginPage.class);&lt;/code&gt;&lt;/div&gt;
When the user enters a wrong password, form validation (I assume you have a password validator) will fail and Wicket will redirect the user to a URL that points to the second version of the login page (the one with the error message). These URLs typically look like:&lt;div class="codeblock"&gt;&lt;code&gt;http://example.com/?wicket:interface=:0:::&lt;/code&gt;&lt;/div&gt; For many applications this is just fine, in some its just too ugly.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;A non solution&lt;/span&gt;&lt;br/&gt;
One of the proposed solutions is that you redirect to another page in the &lt;code&gt;onError&lt;/code&gt; method of the form. E.g.
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;add(new Form(...) {
  @Override
  protected void onError() {
    setResponsePage(LoginPage.class);
  }
}&lt;/code&gt;&lt;/div&gt;
&lt;br/&gt;
The redirection will happen, and the URL is indeed that of the login page, but you will have no error messages. Instead of going to the second version of the login page, you have created a new instance of the login page!
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Another attempt&lt;/span&gt;&lt;br/&gt;
We could pass the error code in the URL:
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;add(new Form(...) {
  @Override
  protected void onError() {
    PageParameters pp = new PageParameters();
    pp.setString("error", "wronglogin");
    setResponsePage(new LoginPage(pp));
  }
}&lt;/code&gt;&lt;/div&gt;&lt;br/&gt;

Unfortunately, with the default URL encoding stratey the URL will now be:&lt;div class="codeblock"&gt;&lt;code&gt;http://example.com/login/error/wronglogin&lt;/code&gt;&lt;/div&gt;
Not at all attractive.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Getting close&lt;/span&gt;&lt;br/&gt;
A fairly recent addition to Wicket is the &lt;a href="http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/request/target/coding/HybridUrlCodingStrategy.html"&gt;HybridUrlCodingStrategy&lt;/a&gt; (and subclasses). Let's mount the login page with one of these:
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;mount(new HybridUrlCodingStrategy("login", LoginPage.class));&lt;/code&gt;&lt;/div&gt;&lt;br/&gt;
If a user now enters wrong credentials, Wicket will redirect you to&lt;div class="codeblock"&gt;&lt;code&gt;http://example.com/login.2&lt;/code&gt;&lt;/div&gt;
The &lt;code&gt;.2&lt;/code&gt; means the second version of the login page for the current session. If the user would delete the &lt;code&gt;.2&lt;/code&gt; from the URL, the HybridUrlCodingStrategy will find the latest available version of the page and serve that. A whole lot nicer but still not perfect.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;The solution&lt;/span&gt;&lt;br/&gt;
If only if we could force Wicket to not display the version number. Well... we can! We'll have to do some coding first:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;// First attempt, DO NOT USE
public class NonVersionedHybridUrlCodingStrategy
        extends HybridUrlCodingStrategy {
  // ... trivial ctor

  @Override
  protected String addPageInfo(
        String url, PageInfo pageInfo) {
    // Do not add the version number as
    // super.addPageInfo would do.
    return url;
  }
}&lt;/code&gt;&lt;/div&gt;&lt;br/&gt;
And now mount with:
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;mount(new NonVersionedHybridUrlCodingStrategy(
         "login", LoginPage.class));&lt;/code&gt;&lt;/div&gt;
Indeed, the version number is gone! However, there is still a tweak to be done. In some circumstance Wicket will redirect you to the latest version of the page, but now that redirect will be to the same URL. This is no problem for Firefox and IE, but Safari, Opera and many tools do not allow this. Here is the final version that does not have the problem:
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;/**
 * UrlCodingStrategy that will give the same
 * URL for every version of a page.
 * @author Erik van Oosten
 */
public class NonVersionedHybridUrlCodingStrategy
        extends HybridUrlCodingStrategy {
  public NonVersionedHybridUrlCodingStrategy(
        String mountPath, Class&lt;? extends Page&gt; pageClass) {
    super(mountPath, pageClass, &lt;b&gt;false&lt;/b&gt;);
  }

  @Override
  protected String addPageInfo(
        String url, PageInfo pageInfo) {
    // Do not add the version number as
    // super.addPageInfo would do.
    return url;
  }
}&lt;/code&gt;&lt;/div&gt;&lt;br/&gt;
I named the URL strategy 'non versioned' as it no longer makes sense to have multiple versions of a page, just the last one will do. You should therefore also add the following fragment to each page constructor:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;setVersioned(false);&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Proof&lt;/span&gt;&lt;br/&gt;
Try it out! Go to &lt;a href="http://www.tipspot.com/"&gt;tipSpot.com&lt;/a&gt; and try to login.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-3668194188095716821?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/3668194188095716821/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/10/wicket-extreme-consistent-urls.html#comment-form' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3668194188095716821'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3668194188095716821'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/10/wicket-extreme-consistent-urls.html' title='Wicket extreme consistent URLs'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5205164441338925749</id><published>2008-09-29T14:41:00.003+02:00</published><updated>2010-08-22T20:26:46.495+02:00</updated><title type='text'>JAOO Observations</title><content type='html'>Here are my day 1 observations from the JAOO conference.
&lt;p/&gt;
Languages evolve slowly. Despite an 100000 increase in storage capacity, 10000 increase in memory and 1000 increase in processing capacity in the last 10 years, programming languages look and feel the same as 10 years ago. The only real difference is that the language has become only a small part of what you need to learn. The major part is now in the libraries, which did grow by 1000 increase.&lt;br/&gt;
For new languages, it becomes harder to have your own infrastructure. That is why many are written on the JVM or the CLR.
&lt;p/&gt;
If you are writing a social website, you must look at open social. The open social specification has been handed over to a foundation. It seems to have become really easy to incorporate it into your social website.
&lt;p/&gt;
A long discussion on the beautiful (yep, that´s really the best word) presentation ´Designed as designer´ gave the following somewhat disappointing results:
&lt;ul&gt;&lt;li&gt;´Designed as designer´, or by substitution ´Work of art as designer´ is the wrong title. It is true that great works of art (including software projects) write themselves after the first draft, but the real topic of the presentation was that &lt;span style="font-style:italic;"&gt;all&lt;/span&gt; works of art are produced by groups of people, not single individuals.&lt;/li&gt;&lt;li&gt;However, since we already operate in the group modus, our lives are not changed at all.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5205164441338925749?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5205164441338925749/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/09/jaoo-observations.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5205164441338925749'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5205164441338925749'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/09/jaoo-observations.html' title='JAOO Observations'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-3925319058596410285</id><published>2008-08-13T10:11:00.011+02:00</published><updated>2010-08-22T20:34:33.015+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>Java transaction boundary tricks</title><content type='html'>Controlling transactions is one of the fundamental things you must control well if your program is to be used by more then 1 user and has a database (as in about every web application). This article shows a little trick to get more out of transactions then most programs do.
Since I am using Spring to demonstrate the principle, I will introduce Spring's transactions first. Feel free to skip that section if you know all about Spring transactions.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Using transaction from Spring&lt;/span&gt;&lt;br/&gt;
With Spring, controlling where a transaction is started can be as simple as adding the &lt;a href="http://static.springframework.org/spring/docs/2.5.x/reference/transaction.html#transaction-declarative"&gt;&lt;tt&gt;@Transactional&lt;/tt&gt; annotation&lt;/a&gt; (and one extra line in the Spring configuration). The @Transactional is typically placed on the class that implements the interface of your service layer. For example you may have a &lt;tt&gt;MemberService&lt;/tt&gt;:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;public interface MemberService {
  List&lt;Member&gt; findMembers(MemberCriteria criteria);
  Member getMember(long id);
  Member updateMember(Member member);
}&lt;/code&gt;&lt;/div&gt;
with the following implementation: &lt;div class="codeblock"&gt;&lt;code class="java"&gt;&lt;b&gt;@Transactional&lt;/b&gt;
public class DefaultMemberService implements MemberService {
  public List&lt;Member&gt; findMembers(MemberCriteria criteria) { ... }
  public Member getMember(long id) { ... }
  public Member updateMember(Member member) { ... }
}&lt;/code&gt;&lt;/div&gt;
When you let Spring instantiate &lt;tt&gt;DefaultMemberService&lt;/tt&gt; as a bean, all of its public methods will be automatically proxied in such a way that the method is executed within a transaction.
&lt;p/&gt;
Java transactions are bound to a thread (think &lt;a href="http://java.sun.com/javase/6/docs/api/java/lang/ThreadLocal.html"&gt;&lt;tt&gt;ThreadLocal&lt;/tt&gt;&lt;/a&gt;), the proxy can therefore join an existing transaction in the current call stack, and by default that is what happens. As a result you can safely call other transactional service methods from within your own transaction methods.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Introducing the CommandService&lt;/span&gt;&lt;br/&gt;
Here is a kind of weird service that I came up with a couple of years ago, named after the Command pattern, the &lt;tt&gt;CommandService&lt;/tt&gt;:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;@Transactional
public class DefaultCommandService implements CommandService {
  public void inTransaction(Runnable command) {
    command.run();
  }

  public &amp;lt;T&gt; T inTransaction(Callable&amp;lt;T&gt; command) {
    try {
      return command.call();
    } catch (Exception e) {
      throw new RuntimeException(
          "CommandService#inTransaction("
          + command.toString() + ")", e);
    }
  }
}&lt;/code&gt;&lt;/div&gt;
(Download link at bottom of article.)
&lt;p/&gt;
As you can see it doesn't to anything except for executing the runnable or callable it is given. How can this be useful? Well, notice the &lt;tt&gt;@Transactional&lt;/tt&gt;. Your block of code is now executed within a transaction!
&lt;p/&gt;
Again, how can this be useful? Here are 3 use cases:
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Use case 1: prevent fine grained service methods&lt;/span&gt;&lt;br/&gt;
Suppose you have a webpage that will update only one property of an entity, e.g. the email address of a member, and another page to update the password. Now to prevent concurrent update problems you want to get and update the member within the same transaction. You could make a new method on the service interface for each property you want to change. However, it is a good practice to keep interfaces concise. So instead we do something like this:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;final long memberId = ...;
final String newEmail = ...;
Member freshMe = 
    commandService.inTransaction(new Callable&amp;lt;Member&gt;() {
  public Member call() throws Exception {
    Member freshMe = memberService.getMember(memberId);
    freshMe.setEmail(email.getNewEmail());
    return memberService.updateMember(freshMe);
  }
});&lt;/code&gt;&lt;/div&gt;
The member entity is retrieved and updated in the same transaction. This way we only need 2 methods in the member service interface to safely update any property of a member.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Use case 2: bundle operations for performance reasons&lt;/span&gt;&lt;br/&gt;
I once wrote some code that would keep thousands of LDAP records in sync with a relational database. In my test environment, a dodgy laptop, it would take 50 minutes to do the initial import of 15,000 records. Each record was persisted to the database in its own transaction.
After a small code change, I used the &lt;tt&gt;CommandService&lt;/tt&gt; to persist the records in groups of 20. What do you think happened? The total time to import these 15000 records dropped from 50 to 2 minutes! Not bad, 25 times faster through 4 lines of extra code.
&lt;p/&gt;
Careful positioning of transaction boundaries can have a dramatic effect on performance. &lt;tt&gt;CommandService&lt;/tt&gt; can help you do that.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Use case 3: transactions in unusual places&lt;/span&gt;&lt;br/&gt;
Let us look at a little bit more detailed example of the &lt;a href="http://day-to-day-stuff.blogspot.com/2008/08/asynchronous-cache-updates.html"&gt;PeriodicRetriever&lt;/a&gt; from my previous article (changes in italic).&lt;div class="codeblock"&gt;&lt;code class="java"&gt;public class CachingPostalCodeService {
  private final Object postalCacheLock = new Object();
  private List&amp;lt;PostalCode&amp;gt; postalCache;
  private PeriodicExecutor postalCacheReloader;
  &lt;i&gt;private HibernatePostalCodeDao hibernatePostalCodeDao;&lt;/i&gt;
  
  public CachingPostalCodeService() {
    postalCacheReloader = new PeriodicExecutor(
          TimeUnit.MINUTES.toMillis(10), new Runnable() {
      public void run() {
        refreshPostalCache();
      }
      
      public String toString() {
        return "Postal code cache reloader";
      }
    });
  }
  
  public List&amp;lt;PostalCode&amp;gt; getPostalCodes() {
    postalCacheReloader.requestStart();
    synchronized (postalCacheLock) {
      return postalCache;
    }
  }
  
  private void refreshPostalCache() {
    &lt;i&gt;List&amp;lt;PostalCode&amp;gt; newPostalCodes =&lt;/i&gt;
        &lt;i&gt;Collections.unmodifiableList(&lt;/i&gt;
          &lt;i&gt;hibernatePostalCodeDao.getAll()&lt;/i&gt;
        &lt;i&gt;);&lt;/i&gt;
    synchronized (postalCacheLock) {
      postalCache = newPostalCodes;
    }
  }

  // ... setter for hibernatePostalCodeDao ...
}&lt;/code&gt;&lt;/div&gt;

If you try this, you may notice that the data is retrieved the first time, but not the second time! The exception message is clear too, something like "Hibernate requires a transaction". To understand this lets follow the two execution paths.
&lt;p/&gt;
The first time &lt;tt&gt;getPostalCodes()&lt;/tt&gt; is called, it is called through Spring's transaction proxy, so the method is executed within a transaction. So when the &lt;tt&gt;PeriodicRetriever&lt;/tt&gt; is called, the runnable (which calls &lt;tt&gt;refreshPostalCache()&lt;/tt&gt;) is immediately called within the same thread and thus also within the same transaction. No problem!
&lt;p/&gt;
The second time &lt;tt&gt;getPostalCodes()&lt;/tt&gt; is called (11 minutes later), &lt;tt&gt;PeriodicRetriever&lt;/tt&gt; is called again but returns immediately. Meanwhile it starts a new thread which executes the runnable defined in &lt;tt&gt;CachingPostalCodeService&lt;/tt&gt;'s constructor. The important thing here is that the new thread does not join the transaction of its parent thread as transactions are bound to threads. The runnable then calls &lt;tt&gt;refreshPostalCache()&lt;/tt&gt;. You may think you'll get a new transaction at that moment. However, a method call within the same class will never go through the transactional proxy. This is because the instance on which we call &lt;tt&gt;refreshPostalCache()&lt;/tt&gt; is simply &lt;tt&gt;this&lt;/tt&gt;, and not the proxy that was obtained from Spring. In other words: &lt;tt&gt;refreshPostalCache()&lt;/tt&gt; is not executed within a transaction.
&lt;p/&gt;
&lt;tt&gt;CommandService&lt;/tt&gt; to the rescue (again, changes in italic):&lt;div class="codeblock"&gt;&lt;code class="java"&gt;public class CachingPostalCodeService {
  private final Object postalCacheLock = new Object();
  private List&amp;lt;PostalCode&amp;gt; postalCache;
  private PeriodicExecutor postalCacheReloader;
  private HibernatePostalCodeDao hibernatePostalCodeDao;
  &lt;i&gt;private CommandService commandService;&lt;/i&gt;
  
  public CachingPostalCodeService() {
    postalCacheReloader = new PeriodicExecutor(
          TimeUnit.MINUTES.toMillis(10), new Runnable() {
      public void run() {
        &lt;i&gt;commandService.inTransaction(new Runnable() {&lt;/i&gt;
          &lt;i&gt;public void run() {&lt;/i&gt;
            &lt;i&gt;refreshPostalCache();&lt;/i&gt;
          &lt;i&gt;}&lt;/i&gt;
        &lt;i&gt;});&lt;/i&gt;
      }

      public String toString() {
        return "Postal code cache reloader";
      }
    });
  }

  // ... same as above ...
  
  // ... setters for hibernatePostalCodeDao and commandService
}&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Download&lt;/span&gt;&lt;br/&gt;
Feel free to use the complete version of &lt;a href="http://www.grons.nl/code/CommandService.html"&gt;CommandService&lt;/a&gt; in any way you see fit.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Conclusions&lt;/span&gt;&lt;br/&gt;
Watching your transaction boundaries can be very rewarding, both in code size as in performance. A command service, such as the one presented in this article can help you do that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-3925319058596410285?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/3925319058596410285/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/08/java-transaction-boundary-tricks.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3925319058596410285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3925319058596410285'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/08/java-transaction-boundary-tricks.html' title='Java transaction boundary tricks'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-4441813458752169140</id><published>2008-08-06T10:50:00.008+02:00</published><updated>2010-08-22T20:37:28.672+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Asynchronous cache updates</title><content type='html'>This is the second article on Java concurrency. The first is &lt;a href="http://day-to-day-stuff.blogspot.com/2008/08/java-thead-control.html"&gt;Java thread control&lt;/a&gt;.
&lt;p/&gt;
Many applications use more or less static reference data such as postal codes, exchange rates and externally stored text. Often this type of data can be safely cached in memory for performance reasons. To make sure that the cached data does not become stale for too long, it can be reloaded every 10 minutes, once a day or on any other schedule you might like.
&lt;p/&gt;
Rather then creating custom code for every type of reference data, one can extract the hard concurrency code to a utility class. This article presents the &lt;tt&gt;PeriodicExecutor&lt;/tt&gt;, a utility class I wrote years ago, and proved to be so useful and reliable that is has been traveling with me from project to project.
&lt;p/&gt;
Here are the requirements I had in mind while writing &lt;tt&gt;PeriodicExecutor&lt;/tt&gt;:
&lt;ol&gt;&lt;li&gt;Support any data retrieval and any cache mechanism.&lt;/li&gt;&lt;li&gt;Refresh the cache in the background, do not slow down request handling.
&lt;/li&gt;&lt;li&gt;No idle threads, only start a thread when one is needed.&lt;/li&gt;&lt;li&gt;Be resilient against errors, retry when they occur.
&lt;/li&gt;&lt;li&gt;Support flexible reloading schedules.&lt;/li&gt;&lt;li&gt;Support initial loading of the memory cache.&lt;/li&gt;&lt;/ol&gt;Lets discuss these requirements and see how they are implemented.
&lt;p/&gt;
&lt;span style="font-style: italic;"&gt;1. Support any data retrieval and any cache mechanism.&lt;/span&gt;&lt;br/&gt;
Each type of data source needs its own retrieval code. E.g. JDBC code for database stuff and things like HttpClient for remote stuff. The cache implementation can also be different from case to case. For example in one case we just need to store a String, in another case we first transform the data to a Map. Therefore the utility class will only solve the the multi-threaded scheduling problem.
&lt;p/&gt;
&lt;tt&gt;PeriodicExecutor&lt;/tt&gt; solves this by letting the client code provide its reload task as a &lt;tt&gt;Runnable&lt;/tt&gt;. The task is also responsible to make the results available when reloading is finished.
&lt;p/&gt;
&lt;span style="font-style: italic;"&gt;2. Refresh the cache in the background, do not slow down request handling.&lt;/span&gt;&lt;br/&gt;
When its time for a data reload, it does not make sense to delay normal request processing until the reload is done. Remember, we are caching for performance reasons! As a consequence &lt;tt&gt;PeriodicExecutor&lt;/tt&gt; executes the client task in a separate thread.
&lt;p/&gt;
&lt;tt&gt;PeriodicExecutor&lt;/tt&gt; guarantees that the task is never run in parallel, but the client task must make sure that there are no concurrency problems the moment the cache is updated.
&lt;p/&gt;
&lt;span style="font-style: italic;"&gt;3. No idle threads, only start a thread when one is needed.&lt;/span&gt;&lt;br/&gt;
There are 2 approaches to thread handling. We can either start a thread upon initialization and keep that thread running all the time, or we can start a new thread each (short) time a reload is required. The first approach has the disadvantage that more resources are used then is necessary (though since Java 5 we could share an executor service with all reloaders). Inconvenient is that we need explicit code to shut down the thread (or executor service). Advantage of this approach is that a thread can schedule reloads precisely.
&lt;p/&gt;
I chose the second approach: each time the cache is accessed by some code, that code must call method &lt;tt&gt;PeriodicExecutor#requestStart()&lt;/tt&gt; to see if a reload is dictated by the schedule. If that is the case, the reload is started in a newly started background thread. A small disadvantage to this approach is that after a long period of inactivity, the first caller will trigger a reload, but until the reload is finished all cache users will see stale data.
&lt;p/&gt;
&lt;span style="font-style: italic;"&gt;4. Be resilient against errors, retry when they occur.&lt;/span&gt;&lt;br/&gt;
The reference data may be retrieved from an unreliable data source. When the reload fails, it should be tried again later. Normal processing can continue as the new background thread isolates it from exceptions. In addition, the reload task should only update the memory cache until after the reload succeeded.
&lt;p/&gt;
&lt;tt&gt;PeriodicExecutor&lt;/tt&gt; detects reload errors by catching exceptions thrown by the reload task. When an error occurred the next call to &lt;tt&gt;requestStart()&lt;/tt&gt; will trigger a second reload attempt. After 2 failed attempts the next reload task is delayed for a couple of minutes to prevent trashing.
&lt;p/&gt;
&lt;span style="font-style: italic;"&gt;5. Support flexible reloading schedules.&lt;/span&gt;&lt;br/&gt;
&lt;tt&gt;PeriodicExecutor&lt;/tt&gt; normally reloads with a fixed delay. However, it is easy to override this by providing your own next reload time. A future improvement would be to extract this code according to the strategy pattern.
&lt;p/&gt;
&lt;span style="font-style: italic;"&gt;6. Support initial loading of the memory cache.&lt;/span&gt;&lt;br/&gt;
Initially the memory cache is empty. As we are talking about reference data, it does not make sense to continue for most programs. &lt;tt&gt;PeriodicExecutor&lt;/tt&gt; therefore also support synchronous loading and exception rethrowing. During a synchronous load the caller is blocked until the reload task is done. Before one task completed, the default is to run synchronously and rethrow exceptions from the reload task. Once one task completed, the default is to run asynchronously and to swallow exceptions (they &lt;span style="font-style:italic;"&gt;are&lt;/span&gt; logged of course).
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Example&lt;/span&gt;&lt;br/&gt;
Enough theory, how do you use it? Here is a simple example:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;public class CachingPostalCodeService {
  private final Object postalCacheLock = new Object();
  private List&amp;lt;PostalCode&amp;gt; postalCache;
  private PeriodicExecutor postalCacheReloader;
  
  public CachingPostalCodeService() {
    postalCacheReloader = new PeriodicExecutor(
          TimeUnit.MINUTES.toMillis(10), new Runnable() {
      public void run() {
        refreshPostalCache();
      }
      
      public String toString() {
        return "Postal code cache reloader";
      }
    });
  }
  
  public List&amp;lt;PostalCode&amp;gt; getPostalCodes() {
    postalCacheReloader.requestStart();
    synchronized (postalCacheLock) {
      return postalCache;
    }
  }
  
  private void refreshPostalCache() {
    List&amp;lt;PostalCode&amp;gt; newPostalCodes = ....
    // Don't forget to make newPostalCodes unmodifiable.
    synchronized (postalCacheLock) {
      postalCache = newPostalCodes;
    }
  }
}&lt;/code&gt;&lt;/div&gt;
Notice that only a limited amount of code is needed to get all the discussed features. In this example a list of postal codes is cached in memory. The only user of the cache, method &lt;tt&gt;getPostalCodes&lt;/tt&gt;, calls &lt;tt&gt;requestStart&lt;/tt&gt; every time. Initially, when no data is in the cache, &lt;tt&gt;requestStart&lt;/tt&gt; will block until the data has been loaded. After 10 minutes of use, the cache will be reloaded in the background. Note how the example synchronizes on &lt;tt&gt;postalCacheLock&lt;/tt&gt; to prevent concurrency problems during the cache update.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Variations&lt;/span&gt;&lt;br/&gt;
Sometimes you only need to reload once a day, preferably early in the morning. The following example shows how one can override scheduled reload to 5 o'clock in the morning.&lt;div class="codeblock"&gt;&lt;code class="java"&gt;  public CachingPostalCodeService() {
    postalCacheReloader = new PeriodicExecutor(
          0, new Runnable() {
      // ...as above...
    }) {
      @Override
      protected long nextExecuteTime(long currentExecute) {
        return getNextTime(5);
      }
    };
  }

  /**
   * @param hour the requested hour
   * @return the next time it is the given hour
   *   in the JVM default time zone
   */
  private static long getNextTime(int hour) {
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.HOUR_OF_DAY, hour);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
    if (cal.getTimeInMillis() &amp;lt; System.currentTimeMillis()) {
      cal.add(Calendar.DAY_OF_MONTH, 1);
    }
    return cal.getTimeInMillis();
  }&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
If you need more control on when to execute synchronously or not, take a look at method &lt;tt&gt;PeriodicExecutor#requestStart(boolean)&lt;/tt&gt;.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Download&lt;/span&gt;&lt;br/&gt;
&lt;a href="http://www.grons.nl/code/PeriodicExecutor.html"&gt;Download &lt;tt&gt;PeriodicExecutor&lt;/tt&gt;&lt;/a&gt;.
&lt;tt&gt;PeriodicExecutor&lt;/tt&gt; can also be used with Java 1.4 (and perhaps 1.3 as well).
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Other options&lt;/span&gt;&lt;br/&gt;
Probably the most complete FOS library for task scheduling is &lt;a href="http://www.opensymphony.com/quartz/"&gt;Quartz&lt;/a&gt;.
&lt;p/&gt;
Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-4441813458752169140?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/4441813458752169140/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/08/asynchronous-cache-updates.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4441813458752169140'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4441813458752169140'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/08/asynchronous-cache-updates.html' title='Asynchronous cache updates'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-9186191130641062779</id><published>2008-08-02T09:50:00.008+02:00</published><updated>2010-08-22T20:39:46.579+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Java thread control</title><content type='html'>As promised in my &lt;a href="http://day-to-day-stuff.blogspot.com/2008/08/effective-java-2nd-edition-book-review.html"&gt;book review of Effective java 2nd edition&lt;/a&gt;, here is an article that shows a neat trick to control threads.
&lt;p/&gt;
The recommended way to stop a thread according to the book is to use a volatile boolean field as a flag to properly stop a running thread: &lt;div class="codeblock"&gt;&lt;code class="java"&gt;public class StopThread {
  private static &lt;b&gt;volatile&lt;/b&gt; boolean stopRequested;
  
  public StopThread() {
    Thread t = new Thread(new Runnable() {
      public void run() {
        int i = 0;
        while (!stopRequested) i++;
      }
    });
    t.start();

    TimeUnit.SECONDS.sleep(1);
    stopRequested = true;
  }
}&lt;/code&gt;&lt;/div&gt;
(See the book to understand why the &lt;tt&gt;volatile&lt;/tt&gt; keyword is essential.) This is all nice, but this thread can only be started and stopped once. Here is a better implementation: &lt;div class="codeblock"&gt;&lt;code class="java"&gt;public class StopThread {
  private static &lt;b&gt;volatile&lt;/b&gt; Thread currentThread;
  
  public StopThread() {
    Thread t = new Thread(new Runnable() {
      public void run() {
        int i = 0;
        while (currentThread == Thread.currentThread()) i++;
      }
    });
    currentThread = t;
    t.start();

    TimeUnit.SECONDS.sleep(1);
    currentThread = null;
  }
}&lt;/code&gt;&lt;/div&gt;
By storing a reference to the running thread, the running thread itself can easily see whether the rest of the system also thinks it should be running. Restarting the thread becomes easy now. Just reference a new thread instance and the old thread will die.
&lt;p/&gt;
The previous program just demonstrates the principle. A better starting point is the following program:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;public class RestartableThread {
  private AtomicInteger threadNumber = new AtomicInteger(0);
  private volatile Thread currentThread;

  public RestartableThread() {
    startThread();
  }

  private void startThread() {
    currentThread = new Thread(
      new RestartableRunnable(), "Restartable thread " + threadNumber.getAndIncrement());
    currentThread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
      public void uncaughtException(Thread t, Throwable e) {
        startThread();
        logger.error(String.format("Thread '%s' has an uncaught exception and was restarted", t.getName()), e);
      }
    });
    currentThread.start();
  }

  public void shutdown() {
    currentThread = null;
  }

  private class RestartableRunnable implements Runnable {
    public void run() {
      while (true) {
        if (currentThread != Thread.currentThread()) {
          return;
        }

        try {
          // Do a bit of work. Make sure this takes a limited time.

        } catch (Exception ex) {
          logger.error("exception", ex);
        }
      }
    }
  }
}&lt;/code&gt;&lt;/div&gt;
&lt;br/&gt;
You can stop the thread by calling the &lt;tt&gt;shutdown()&lt;/tt&gt; method. Failure to do so on program exit will prevent the JVM from stopping.&lt;br/&gt;
The runnable contains a try/catch to prevent the thread from dying. As a double fail safety mechanism, the uncaught exception handler is triggered for unhandled &lt;tt&gt;Throwable&lt;/tt&gt;s such as out of memory errors. The exception handler then simply starts a new thread so that execution continues. There are subtle advantages to this double approach. Exceptions are normally not so catastrophic, the thread could just continue. Out of memory errors however can be better dealt with by letting the thread terminate and make all its used memory available for the garbage collector.&lt;br/&gt;
If for any other reason you want to (re-)start the thread (for example because it is programmed to run only for a limited time), simply call &lt;tt&gt; startThread()&lt;/tt&gt;.
&lt;p/&gt;
Make sure that the run condition is checked regularly. Consumer threads that wait on something like a queue should wake up regularly. For example, if you are waiting on new entries in a &lt;tt&gt;BlockingQueue&lt;/tt&gt; this is better done with &lt;tt&gt;poll()&lt;/tt&gt; then with &lt;tt&gt;get()&lt;/tt&gt;.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Conclusion&lt;/span&gt;&lt;br/&gt;
Despite the presence of the new and shiny executor services, it is still sometimes necessary to write your own thread. I hope that this little article will get you started on a robust yet flexible  implementation.
&lt;p/&gt;
Next thread article: a helper for &lt;a href="http://day-to-day-stuff.blogspot.com/2008/08/asynchronous-cache-updates.html"&gt;asynchronous cache updates&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-9186191130641062779?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/9186191130641062779/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/08/java-thead-control.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/9186191130641062779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/9186191130641062779'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/08/java-thead-control.html' title='Java thread control'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-8665015302393442087</id><published>2008-08-01T20:34:00.006+02:00</published><updated>2010-08-22T20:41:10.244+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='book review'/><title type='text'>Effective Java 2nd edition - Book review</title><content type='html'>If you are even a bit serious about your java programming, this book is a must read. There is no other book that improved the quality of my java programming in such a dramatic way.
&lt;p/&gt;
Now this was my opinion on the first edition. After having read the second edition, I can tell you that the same top quality is there, with lots and lots of big and tiny updates on top.
&lt;p/&gt;
Ok, with that out of way, here are some comments:
&lt;ul&gt;&lt;li&gt;In the introduction it says 'This book is not intended to be read from cover to cover'. Ignore that, read it from cover to cover anyway!
&lt;/li&gt;&lt;li&gt;Initially I missed an overview of what changed since the 1st edition. But when I started reading I understood that such a list would be ridiculous. Every chapter has been improved in tiny, and sometimes big ways.&lt;/li&gt;&lt;/ul&gt;And now for some tiny counter noises:
&lt;ul&gt;&lt;li&gt;Item 4: This item is about enforcing noninstantiability of classes that contain static methods only. However, except for saying that is nonsensical, it fails to provide a good reason for preventing this. I would say: don't bother. Let silly programmers instantiate the class. Firstly it can not do much wrong, secondly, any good IDE warns you about it.&lt;/li&gt;&lt;li&gt;Item 8: When overriding &lt;tt&gt;equals&lt;/tt&gt;, one should use &lt;tt&gt;==&lt;/tt&gt; instead of &lt;tt&gt;equals&lt;/tt&gt; to compare fields of enum type. The item mentions that this should only be done for certain primitive fields.
&lt;/li&gt;&lt;li&gt;Item 60: I disagree with the advice to throw &lt;tt&gt;NullPointerException&lt;/tt&gt; in case an argument was null while the contract of the method specifies that this is not allowed. NPE is also thrown by the JVM and is therefore more suitable to indicate a programming error. &lt;tt&gt;IllegalArgumentException&lt;/tt&gt; is in my opinion more suited to express that a method is used wrongly. In other words: a NPE indicates an error inside the method, an IAE indicates an error outside of the method.&lt;/li&gt;&lt;li&gt;Item 66: I recently saw a neater trick to control threads. More on this in the &lt;a href="http://day-to-day-stuff.blogspot.com/2008/08/java-thead-control.html"&gt;next post&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Item 68: One thing that still puzzles me is how one can write a consumer task with the new executor services. This item does not make this clear. Perhaps this use case still warrants the creation of your own thread.&lt;/li&gt;&lt;/ul&gt;Happy reading!
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2008-08-03:&lt;/span&gt; refined my critics on throwing a NPE vs IAE.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-8665015302393442087?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/8665015302393442087/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/08/effective-java-2nd-edition-book-review.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8665015302393442087'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8665015302393442087'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/08/effective-java-2nd-edition-book-review.html' title='Effective Java 2nd edition - Book review'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-2647934605667564610</id><published>2008-07-24T20:38:00.005+02:00</published><updated>2010-08-22T20:42:23.612+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>Spring message resource weirdness</title><content type='html'>Today I got a bug report that e-mail subject appeared in Dutch while they should have been send in English. The first thing I check of course was whether the correct locale was used. It was.
&lt;p/&gt;
Here is the situation:
&lt;ul&gt;&lt;li&gt;Spring message source
&lt;/li&gt;&lt;li&gt;2 files: &lt;tt&gt;email-subjects.properties&lt;/tt&gt; and &lt;tt&gt;email-subjects_nl.properties&lt;/tt&gt;&lt;/li&gt;&lt;li&gt;requested locale: en_US&lt;/li&gt;&lt;/ul&gt;After a bit of debugging I noticed that Spring looks for the following files (in order):
&lt;ol&gt;&lt;li&gt;&lt;tt&gt;email-subjects_en_us.properties&lt;/tt&gt;&lt;/li&gt;&lt;li&gt;&lt;tt&gt;email-subjects_en.properties&lt;/tt&gt;&lt;/li&gt;&lt;li&gt;&lt;tt&gt;email-subjects_nl_NL.properties&lt;/tt&gt;&lt;/li&gt;&lt;li&gt;&lt;tt&gt;email-subjects_nl.properties&lt;/tt&gt;&lt;/li&gt;&lt;li&gt;&lt;tt&gt;email-subjects.properties&lt;/tt&gt;&lt;/li&gt;&lt;/ol&gt;WTF?! It also looks for the default locale! Thank you Spring! This is really the first time you disappoint me (in 4 years, not bad). But of course I should have looked further, when will Spring ever let you down?
&lt;p/&gt;
The solution? Well luckily the authors of the code realized this is not always what you want. An easy &lt;tt&gt;setFallbackToSystemLocale(false)&lt;/tt&gt; overrides this funny behavior. I added &lt;tt&gt;&amp;lt;property name="fallbackToSystemLocale" value="false" /&amp;gt;&lt;/tt&gt; to my config file and it all worked. Hurray for Spring!
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2008-08-04:&lt;/span&gt; adapted the article to the real solution.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-2647934605667564610?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/2647934605667564610/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/07/spring-message-resource-weirdness.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2647934605667564610'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2647934605667564610'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/07/spring-message-resource-weirdness.html' title='Spring message resource weirdness'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5183558070737637600</id><published>2008-06-28T14:22:00.005+02:00</published><updated>2010-08-22T20:46:01.278+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>JPA and bit operations (Hibernate/Postgres)</title><content type='html'>In my current data model one of the entities has a column of integer type that contains a large set of boolean flags. Of course this is well hidden in the domain model objects, so you can just call things like &lt;tt&gt;event.isCanceled()&lt;/tt&gt;.
&lt;p/&gt;
However, in the administration interface we need to search for this entity based on separate flags. The JPA query I found in our code base is &lt;tt&gt;select ... where flagBits = 1&lt;/tt&gt; (canceled is expressed in bit 0). Oops. What if the entity is not only canceled, but also sold out (bit 1)? Quite wrong, lets fix it.
&lt;p/&gt;
So, I rewrote the where clause to &lt;tt&gt;flagBits &amp;amp; 1 = 1&lt;/tt&gt; (&lt;tt&gt;&amp;amp;&lt;/tt&gt; is the &lt;a href="http://www.postgresql.org/docs/8.3/interactive/functions-math.html"&gt;binary and operation&lt;/a&gt; in Postgres). Hibernate, our JPA implementation parses the query to do its ORM magic. Unfortunately, Hibernate &lt;a href="http://www.hibernate.org/hib_docs/v3/reference/en/html/queryhql.html#queryhql-expressions"&gt;does not understand&lt;/a&gt; the &lt;tt&gt;&amp;amp;&lt;/tt&gt; operation (though I heard it did in Hibernate 2) so it will throw an exception.
&lt;p/&gt;
Looking through the Hibernate and Postgres documentations I found the &lt;tt&gt;get_bit&lt;/tt&gt; operation. It does not except integer types as their value, so a cast was needed: &lt;tt&gt;get_bit(''||flagBits, 1) == 1&lt;/tt&gt;. Another exception. And hopelessly wrong as well, &lt;tt&gt;''||flagBits&lt;/tt&gt; creates a string with the decimal representation of the number, the function &lt;tt&gt;get_bit&lt;/tt&gt; expects a byte array. Maybe: &lt;tt&gt;get_bit((''||flagBits)::bytea, 1) == 1&lt;/tt&gt;. Not really, the problem is still there. And furthermore, Hibernate also does not support the &lt;tt&gt;||&lt;/tt&gt; and &lt;tt&gt;::&lt;/tt&gt; operations, more exceptions from Hibernate. (The &lt;tt&gt;::&lt;/tt&gt; could be replaced by a supported &lt;tt&gt;cast&lt;/tt&gt; operation.)
&lt;p/&gt;
Back to basics. How can you access a given bit with more basic, supported operations?
Fact: division by 2&lt;span style="font-style:italic;vertical-align:super;"&gt;x&lt;/span&gt; will shift the bits right by &lt;span style="font-style:italic;"&gt;x&lt;/span&gt;.
Fact: a number modulo 2 gives 1 if bit 0 is set.
&lt;p/&gt;
What follows is the where clause &lt;tt&gt;(flagBits / &lt;span style="font-style:italic;"&gt;bitmask&lt;/span&gt;) % 2 = 1&lt;/tt&gt;. Where &lt;tt&gt;&lt;span style="font-style:italic;"&gt;bitmask&lt;/span&gt;&lt;/tt&gt; is 1 for bit 0, and 2 for bit 1, etc. Oops, &lt;tt&gt;%&lt;/tt&gt; is not supported either! Last attempt: &lt;tt&gt;mod(flagBits / &lt;span style="font-style:italic;"&gt;bitmask&lt;/span&gt;, 2) = 1&lt;/tt&gt;. Success at last.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Conclusion&lt;/span&gt;&lt;ul&gt;&lt;li&gt;JPA/Hibernate give an extensive but still limited subset of SQL expressiveness.&lt;/li&gt;&lt;li&gt;It pays off to pay attention during math classes.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5183558070737637600?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5183558070737637600/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/06/jpa-and-bit-operations.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5183558070737637600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5183558070737637600'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/06/jpa-and-bit-operations.html' title='JPA and bit operations (Hibernate/Postgres)'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-6814697345291826412</id><published>2008-06-13T20:32:00.010+02:00</published><updated>2010-08-22T20:47:17.833+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><category scheme='http://www.blogger.com/atom/ns#' term='networking'/><title type='text'>Ubuntu server 7.10 to 8.04 upgrade experiences</title><content type='html'>Yesterday I upgraded my server at home (running 3 &lt;a href="http://no-commons-logging.zapto.org/"&gt;we&lt;/a&gt;b&lt;a href="http://www.theatergroepzout.nl/"&gt;si&lt;/a&gt;t&lt;a href="http://milo.grons.nl/"&gt;es&lt;/a&gt; and a small e-mail server) from Ubuntu 7.10 to 8.04. My main system is a nice looking iMac, completed with an eeepc for traveling and kitchen table work. But I use Ubuntu in the closet and at work.&lt;br/&gt;
The upgrade went rather well. Of course, I had installed as little packages from outside the repositories as possible. I even did the upgrade over ssh while that is not recommended.
&lt;p/&gt;
Unfortunately, I only just noticed that no e-mail was being received anymore. After some head scratching (I had also changed the firewall settings), I decided to compare my backup of &lt;tt&gt;/etc&lt;/tt&gt; (yes, I had one!) to the current copy.
&lt;p/&gt;
So how do you compare such a complex directory as &lt;tt&gt;/etc&lt;/tt&gt; on a Mac? I tried Eclipse and IntelliJ. Despite their excellent compare tools, they have no such thing for complete directories. Searching the web for open source solutions did not give any conclusive evidence as well. &lt;a href="http://kdiff3.sourceforge.net/"&gt;Kdiff3&lt;/a&gt; was mentioned most frequently, so I decided to try that one. Kdiff3 can be installed with &lt;a href="http://www.macports.org/"&gt;MacPorts&lt;/a&gt;, but make sure you have plenty of time. It took more then more then 2 hours on this screaming iron!&lt;br/&gt;
Kdiff3 is not so intuitive as the commercial products Araxis and Beyond Compare (which I have used professionally), but once you get used to it, it is a nice and powerful merging tool.
&lt;p/&gt;
And there it was: a small copy/paste error in my dovecot configuration. Postfix uses dovecot for authentication so every TLS connection failed. Copy, paste, restart, done!
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Conclusions&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;Make sufficient backups of all your configuration files.&lt;/li&gt;
&lt;li&gt;Use the packages from your distribution as much as possible. Do not install later versions of a package unless you are prepared to install it again and again and again.&lt;/li&gt;
&lt;li&gt;Kdiff3 is an acceptable diff tool on the Mac.&lt;/li&gt;
&lt;li&gt;Do not be afraid for an Ubuntu server upgrade!&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-6814697345291826412?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/6814697345291826412/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/06/ubuntu-server-710-to-804-upgrade.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6814697345291826412'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6814697345291826412'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/06/ubuntu-server-710-to-804-upgrade.html' title='Ubuntu server 7.10 to 8.04 upgrade experiences'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-8390148603910572913</id><published>2008-06-01T21:09:00.006+02:00</published><updated>2010-08-22T20:48:28.299+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='opinion'/><title type='text'>Do not offer money!</title><content type='html'>Money and open source, always an interesting combination. But how do we, simple developers (with no ambition but to make beautiful stuff and live from it), deal with this? Let me offer you my opinion.
&lt;p/&gt;
Lets start with a real example on what you should not do. Here is a quote from an e-mail that was sent on the user list of well respected project xyz (which it is, is not important):
&lt;blockquote&gt;Hi!
&lt;p/&gt;
I was wondering how I could make a contribution for xyz. I'm not talking about a code contribution but rather a small money contribution. I have got a lot of help here on this forum and in fact I don't think I have ever experienced this kind of help elsewhere!
&lt;p/&gt;
Thanks to all that have helped and especially core xyz coders.&lt;/blockquote&gt;
The author obviously has good intentions, so how can this be so disastrously wrong? Suppose you are celebrating Christmas (or any other traditional family event) at your mother-in-law´s house. She cooked really well and everything tasted just wonderfully. You stand up and say, "Mother, this was excellent. This must have been worth at least 400 euros. Here, this is for you." and you hand here 4 fresh 100 euros bills. You will probably understand that your mother-in-law will not be happy, and in fact she will likely not forget this for a long time. Will she have the same pleasure in cooking for you next year? I think not.
&lt;p/&gt;
So what is the problem? As Dan Ariely, a professor of behavioral economics wrote in his excellent book &lt;a href="http://www.predictablyirrational.com/"&gt;Predictably Irrational&lt;/a&gt; (I read the Dutch translation &lt;span xml:lang="nl" lang="nl"&gt;"Waarom we altijd tijd te kort komen"&lt;/span&gt;) there are 2 sets of norms, market norms and social norms. Market norms regulate how we interact with each other when money is involved. Social norms regulate all other interactions. So eating at a restaurant will fall under the market norms, eating at your mother-in-low falls under the social norms. It is both about eating, but if you think about it, there are vast differences in what you expect, how you deal with bad food, etc.
&lt;p/&gt;
The coders of xyz do all of their hard work in their free time. They are not getting paid, they do this purely out of love for what they are creating, and perhaps because of the idea that it is useful to others, and perhaps because of the respect they will receive. Any social interaction therefore falls under the social norms. Now if you start to offer money, you will move the social interactions to market norms. Luckily the core coders of project xyz are wise and responded with a request to donate to a charity project. But suppose they accepted the money. Now why would they do their best next time when no money is involved? They might as well go drinking beer with friends. Or worse, why would non core contributors do their best if they do not get paid? If you know somebody else is getting paid for the same task, even if it only takes 1 minute, why would you spend any brain power on it?
&lt;p/&gt;
What is even worse, after switching to market norms, it takes a lot of time before the social norms are reinstated. Here is &lt;a href="http://www.ericdaugherty.com/blog/2008/01/java-email-server-20-branch.html"&gt;quote&lt;/a&gt; from Eric Daugherty, author of the &lt;a href="http://www.ericdaugherty.com/java/mailserver/"&gt;Java Email Server&lt;/a&gt;, on JES version 2.0:
&lt;blockquote&gt;I started down this path with passion and drive. After I'd completed the SMTP (I think) portion of the code base, I was contacted by Andrew Oliver about my interest in working on a JBoss mail server. He was working on a new project to build an enterprise class mail (and calender, etc) server build on JBoss (where he was employed at the time). This project, JBossMailServer at the time, seemed to eclipse what I was attempting, and given my already slowing momentum on the 2.0 branch, pretty much brought it to a screeching halt. I had a bit of interest in working on the JBoss version, but in truth some of the motivation for the projects that I work on is ego. Working on a project that wasn't 'mine' didn't quite have the same appeal, even if it was JBoss (remember, this was 4 years ago).
&lt;p/&gt;
Apparently JBoss had found someone interested in the server, as they approached me with an offer to pay me for the task of completing the SMTP (or POP, I don't recall) functionality. Since I was a pretty easy task for me (I'd done it once already), and hey, they were paying me, I jumped on board. I worked through the deliverable they wanted and earned a little spending money.
&lt;p/&gt;
However, a funny thing happened. Once I'd worked for pay on the project, it was really hard to get excited about working for free. Combine this with the lack of real ego payoff, and I drifted away from the project.&lt;/blockquote&gt;
So when the previous state is restored (the money is gone), the market norms stay for quite a while. This is proven by Dan and colleagues and this is also Eric´s own observation. A troubling conclusion: &lt;span style="font-weight:bold;"&gt;by offering money for free services the result is less services.&lt;/span&gt;
&lt;p/&gt;
Suppose you care for the success of an open source project, what can you do? My answer is simple: contribute by writing documentation, by answering questions, by writing patches, provide hosting and otherwise interact according to the social norms. &lt;span style="font-weight:bold;"&gt;If you are not able to do any of these things, it is better to stay out!&lt;/span&gt;
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Acknowledgments&lt;/span&gt;&lt;br/&gt;
Thanks to Dan Ariely for his research. The Christmas dining example is from his book.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-8390148603910572913?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/8390148603910572913/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/06/do-not-offer-money.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8390148603910572913'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8390148603910572913'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/06/do-not-offer-money.html' title='Do not offer money!'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-7886684619915297981</id><published>2008-05-04T14:55:00.040+02:00</published><updated>2010-08-22T20:56:07.782+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Everything about Wicket internationalization</title><content type='html'>I have been using Wicket for about one and a half year (and followed it about a year longer), and only recently I realized the tremendous flexibility of Wicket. The core developers keep introducing major features by replacing large and small parts of the core, without changing much of the existing programming interface. It doesn't stop there though. Any good developer can do the same with relatively little effort. On occasion I replaced important parts of the core myself, just because I wanted it a little bit different.
&lt;p/&gt;
Upcoming release 1.4 is all about introducing the long awaited generic models. But the &lt;a href="http://wicket.apache.org/news.html"&gt;release notes of Wicket 1.4-m1&lt;/a&gt; contain another little gem. Created by non-core developer John Ray: child components within a wicket:message element. But before I explain what that means, lets first look at your internationalization options in Wicket.
&lt;p/&gt;
This article requires you to have a very basic grasp of Wicket (what the application class is, how wicket:id is used, and simple components like Panel and Label). A bit of knowledge on Java properties helps as well.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Simple texts: wicket:message element, and wicket:message attribute&lt;/span&gt;&lt;br/&gt;
A simple web page easily contains dozens of texts that need i18n. It would be cumbersome to have to put a wicket:id on all of these, and then add a Label to each. Instead you can use the &lt;tt&gt;wicket:message&lt;/tt&gt; element in the HTML file directly. For example in MyPanel.html:
&lt;div class="codeblock"&gt;&lt;code&gt;&amp;lt;wicket:message key="helloworld"&gt;Hello&amp;lt;/wicket:message&gt;&lt;/code&gt;&lt;/div&gt;
The key (&lt;tt&gt;helloworld&lt;/tt&gt;) is used to lookup the message in the property files. The default text (&lt;tt&gt;Hello&lt;/tt&gt;) is used when the message could not be found.&lt;br/&gt;
The property files are (MyPanel.properties): &lt;div class="codeblock"&gt;&lt;code&gt;helloworld: Hello world&lt;/code&gt;&lt;/div&gt;&lt;br/&gt;
And for Dutch(MyPanel_nl.properties):&lt;div class="codeblock"&gt;&lt;code&gt;helloworld: Hallo wereld&lt;/code&gt;&lt;/div&gt;&lt;br/&gt;
The ouput with an English locale:&lt;div class="codeblock"&gt;&lt;code&gt;Hello world&lt;/code&gt;&lt;/div&gt;
You can do the same for HTML attributes. For example:&lt;div class="codeblock"&gt;&lt;code&gt;&amp;lt;input type="submit" value="Default text"
    wicket:message="value:helloworld"/&gt;&lt;/code&gt;&lt;/div&gt;
Would result in: &lt;div class="codeblock"&gt;&lt;code&gt;&amp;lt;input type="submit" value="Hello world"/&gt;&lt;/code&gt;&lt;/div&gt;
If there are multiple attributes to translate, add them prepended with a comma:&lt;div class="codeblock"&gt;&lt;code&gt;&amp;lt;input type="submit" value="Default text"
    wicket:message="value:helloworld,title:hellotitle"/&gt;&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Wicket's locale selection&lt;/span&gt;&lt;br/&gt;
As usual in Java i18n systems, messages are looked up by locale. The locale is automatically extracted from the HTTP request, but can also be explicitly set with &lt;tt&gt;getSession().setLocale(Locale.US)&lt;/tt&gt;.&lt;br/&gt;
If you need to override the locale for a specific component, override &lt;tt&gt;getLocale()&lt;/tt&gt; on that component and it will be used by that component and its children.
&lt;p/&gt;
If a client does not provide a preferred Locale, Java's default Locale is used. See &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Locale.html#getDefault()"&gt;Locale.getDefault()&lt;/a&gt; and &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Locale.html#setDefault(java.util.Locale)"&gt;Locale.setDefault(Locale))&lt;/a&gt; for more information.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Finding the message&lt;/span&gt;&lt;br/&gt;
Another extremely powerful concept is how the message is looked up in the property files. Normally a properties file with the same name as the current component is used. E.g. if you are working within the component SummaryPanel, the message is looked up in the file &lt;tt&gt;SummaryPanel_nl_NL.properties&lt;/tt&gt; (for Dutch, Netherlands locale). If that fails (either because the file does not exist, or because it does not contain the message), file &lt;tt&gt;SummaryPanel_nl.properties&lt;/tt&gt;, and then &lt;tt&gt;SummaryPanel.properties&lt;/tt&gt; are investigated. This is all just like Java's resource bundles work. But of course Wicket goes further. If the message is still not found, the property files of the &lt;span style="font-style:italic;"&gt;parent class&lt;/span&gt; are investigated, and again, and again, all the way up to &lt;tt&gt;java.lang.Object&lt;/tt&gt;. For each class all locale variants are searched for.
&lt;p/&gt;
Components are reusable, but in order to make it really reusable you should be able to override the messages depending on where the component is used. This is facilitated by first looking up the message (following the algorithm above) for every parent in the &lt;span style="font-style:italic;"&gt;component hierarchy&lt;/span&gt; (aka page hierarchy). Every component can override the messages of its child components, so the search starts at the page's properties and then trickles down to the component that uses it (yes, its top-down). In order to make overrides specific to a certain child component, you can prefix the message key with the component id of the child. See &lt;a href="http://wicket.apache.org/docs/1.4/org/apache/wicket/resource/loader/ComponentStringResourceLoader.html"&gt;ComponentStringResourceLoader&lt;/a&gt; for more details.
&lt;p/&gt;
If no message was found in the page hierarchy, another search starts which will look at your application class and its super classes. So Wicket first looks at &lt;tt&gt;MyApplication.properties&lt;/tt&gt; (provided &lt;tt&gt;MyApplication&lt;/tt&gt; is the name of your application) and then up the class hierarchy, passing &lt;tt&gt;org.apache.wicket.Application&lt;/tt&gt;, up to &lt;tt&gt;java.lang.Object&lt;/tt&gt;. This is how Wicket provides its many default i18n texts.
&lt;p/&gt;
This might sound complicated, but in practice you simply have one properties file per page and some more for components that are reused over multiple pages. For smaller applications you can even put everything in one properties file. These rules work so well that you just do what you think is correct and it almost always just is.
&lt;p/&gt;
One note on the location of the properties files: like HTML files, they must be in the same package (and same classloader) as the component they are associated with. In practice they live next to each other in the same directory.&lt;br/&gt;
If you want Wicket to get its resources from somewhere else (e.g. from a database), you can implement the interface org.apache.wicket.resource.loader.IStringResourceLoader and configure this in the &lt;tt&gt;init()&lt;/tt&gt; method of your application class.
&lt;p/&gt;
References: &lt;a href="http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/util/resource/locator/ExtensionResourceNameIterator.html"&gt;ExtensionResourceNameIterator&lt;/a&gt;, &lt;a href="http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/resource/loader/ComponentStringResourceLoader.html"&gt;ComponentStringResourceLoader&lt;/a&gt;, and for real control freaks the new &lt;a href="http://www.ddpoker.com/javadoc/wicket/1.4-m1/org/apache/wicket/resource/loader/PackageStringResourceLoader.html"&gt;PackageStringResourceLoader&lt;/a&gt;.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Reloading and caching&lt;/span&gt;&lt;br/&gt;
When Wicket is started in development mode, changed properties files are detected and reloaded. To properly make use of this feature from an IDE, you should run Wicket directly from the compiled sources, for example with the &lt;tt&gt;Start&lt;/tt&gt; file, included in every &lt;a href="http://wicket.apache.org/quickstart.html"&gt;QuickStart&lt;/a&gt;. In Eclipse you just save the properties file, in IntelliJ you must do a make (Ctrl-F9) before changes are picked up.
In production mode, resolved properties are heavily cached for performance. (Same applies to html files.)
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Putting dynamic values in the messages&lt;/span&gt;&lt;br/&gt;
As soon as you need to add values to the messages, you also need to add some Java code. The java code provides the vales, but the rest of the text still comes from the properties file. One of the nice things here is how Wicket leverages java bean properties.
&lt;p/&gt;
Here is a complete example. Not many frameworks make this so easy!&lt;div class="codeblock"&gt;&lt;code&gt;&lt;i&gt;MyPanel.properties:&lt;/i&gt;
summ: You, and ${otherCount} others, reviewed '${title}' \
 and rated it ${rate}.

&lt;i&gt;MyPanel.html:&lt;/i&gt;
&amp;lt;span wicket:id="summary"&gt;Text that will be replaced.&amp;lt;/span&gt;

&lt;i&gt;MyPanel.java:&lt;/i&gt;
// Summary has getters for otherCount, title, etc.
Summary summary = ...;
add(new Label("summary", new StringResourceModel(
    "summ", this, new Model(summary))));&lt;/code&gt;&lt;/div&gt;Resulting in something like:&lt;div class="codeblock"&gt;&lt;code&gt;&amp;lt;span&gt;You, and 5 others, reviewed 'Wicket in Action'
and rated it excellent.&amp;lt;/span&gt;&lt;/code&gt;&lt;/div&gt;
&lt;span style="font-weight:bold;"&gt;Property based message key&lt;/span&gt;
It goes further: do you know a framework that can do this?
&lt;div class="codeblock"&gt;&lt;code&gt;&lt;i&gt;MyPanel.properties:&lt;/i&gt;
summ.short: Thanks!
summ.long: You, and ${otherCount} others, reviewed '${title}' \
 and rated it ${rate}. Thanks!

&lt;i&gt;MyPanel.html:&lt;/i&gt;
&amp;lt;span wicket:id="summary"&gt;Text that will be replaced.&amp;lt;/span&gt;

&lt;i&gt;MyPanel.java:&lt;/i&gt;
// summary.getMsgPrefs().getStyle() returns "short" or "long"
add(new Label("summary", new StringResourceModel(
    "summ.${msgPrefs.style}", this, new Model(summary))));&lt;/code&gt;&lt;/div&gt;
&lt;span style="font-weight:bold;"&gt;And more!&lt;/span&gt;
The Java property syntax is also still available (e.g. like &lt;tt&gt;{0,Date}&lt;/tt&gt;, &lt;tt&gt;{2,number,###.##}&lt;/tt&gt; etc.)? You can find all forms in the &lt;a href="http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/model/StringResourceModel.html"&gt;javadocs of StringResourceModel&lt;/a&gt;.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;The trouble with property files&lt;/span&gt;&lt;br/&gt;
Now this is all nice, but there is one problem you will frequently encountered while using string resources. In this problem you have a sentence that contains one or more dynamically constructed parts (for example some links). The order of these dynamic parts is potentially different for each locale. In the previous example, we may want to link to an information page on the title, and on the word 'others' open a modal window with a list of people.
&lt;p/&gt;
If you try this with Wicket 1.3 (or any other Java framework I know of that uses resource bundles) you'll find that there is actually no pure way to do this. I know of 3 workarounds, but neither is very attractive. Lets review them. The first way is to split the texts before, after and between the dynamic components. This works good enough, but varying the order of the components is not possible unless you so something clever on the Java side, or have multiple HTML files. The latter is also workaround number 2: for each locale use a separate HTML file with the translation directly included (note: Wicket uses the same rules to lookup HTML files as it does for property files, e.g. MyPanel_nl.html just works like you expect it to). However, having more then one HTML file per component often leads to maintenance horror as changes must be synchronized over many files. The third workaround is to add placeholders to the message text that later are replaced by some HTML. That HTML is either hand written (in which case you may wonder why you started with  Wicket at all), or you need to do some serious Wicket hacking to get components to render to a StringBuffer.
&lt;p/&gt;
Now lets do this with Wicket 1.4-m1:&lt;div class="codeblock"&gt;&lt;code&gt;&lt;i&gt;MyPanel.properties:&lt;/i&gt;
summ: You, and ${othersLink}, reviewed ${titleLink} \
 and rated it ${rate}.
others: ${otherCount} others

&lt;i&gt;MyPanel.html:&lt;/i&gt;
&amp;lt;wicket:message key="summ"&gt;Text that will be replaced.
  &amp;lt;span wicket:id="rate"&gt;rate&amp;lt;/span&gt;
  &amp;lt;a href="#" wicket:id="titleLink"&gt;
    &amp;lt;span wicket:id="titleLabel"&gt;label&amp;lt;/span&gt;&amp;lt;/a&gt;
  &amp;lt;a href="#" wicket:id="othersLink"&gt;
    &amp;lt;span wicket:id="othersLabel"&gt;label&lt;&amp;lt;/span&gt;&amp;lt;/a&gt;
&amp;lt;/wicket:message&gt;

&lt;i&gt;MyPanel.java:&lt;/i&gt;
// Note, we directly add the embedded components
// rate, othersLink and titleLink

add(new Label("rate", new PropertyModel(summary, "rate")));

Link othLink = new Link("othersLink") { .... }
add(othLink);
othLink.add(new Label("othersLabel", new StringResourceModel(
    "others", this, new Model(summary))));

ExternalLink titleLink = new ExternalLink(
    "titleLink", summary.getTitleUrl());
add(titleLink);
titleLink.add(new Label("titleLabel",
    new PropertyModel(summary, "title")));&lt;/code&gt;&lt;/div&gt;
The html file contains a wicket:message element with some embedded components. All text within the element is removed and replaced by the text from the properties file. Labels like &lt;tt&gt;${othersLink}&lt;/tt&gt; are replaced by the rendered component of the same wicket:id. That component must be embedded in the wicket:message element. Note that the order of the components  is irrellevant. Also note that you will not see any reference to the wicket:message element in the java code.
&lt;p/&gt;
If you want runnable code you can download the &lt;a href="http://www.grons.nl/i18ntest.zip"&gt;complete example code&lt;/a&gt; (a Maven 2 project based on &lt;a href="http://wicket.apache.org/quickstart.html"&gt;Wicket's QuickStart&lt;/a&gt;). See below for instructions.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Getting string resources from code&lt;/span&gt;&lt;br/&gt;
Despite all of the above, there always remain some cases in which you need direct access to the messages. Luckily Wicket provides access through the &lt;a href="http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/Localizer.html"&gt;Localizer&lt;/a&gt;. You can get the localizer from any component with &lt;tt&gt;getLocalizer()&lt;/tt&gt;.
&lt;p/&gt;
Again, see the &lt;a href="http://www.grons.nl/i18ntest.zip"&gt;complete example code&lt;/a&gt; for an example.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Encoding troubles&lt;/span&gt;&lt;br/&gt;
Fairly unknown to beginning programmers is that you are only allowed to use ISO-8859-1 encoding in java properties files. If you live in Europe this is a fairly annoying as many languages have characters that are not known to ISO-8859-1 (for example the euro symbol &amp;euro;). The simple workaround is escaping: &lt;tt&gt;cree\u00EBr&lt;/tt&gt; instead of &lt;tt&gt;cree&amp;euml;r&lt;/tt&gt;. (I always use &lt;a href="http://unicode.coeurlumiere.com/"&gt;this site&lt;/a&gt; to look up the ISO codepoint.)
&lt;p/&gt;
But imagine you are making a site in Thai! Luckily Wicket can also read XML property files. Here is a fragment of the Thai properties that comes with Wicket:&lt;div class="codeblock"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&gt;
&amp;lt;!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"&gt;
&amp;lt;properties&gt;
  &amp;lt;entry key="Required"&gt;ข้อมูลใน ${label} เป็นที่ต้องการ.&amp;lt;/entry&gt;
&amp;lt;/properties&gt;
&lt;/code&gt;&lt;/div&gt;
Nice!
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Sample code&lt;/span&gt;&lt;br/&gt;
To test the code in this article I created a &lt;a href="http://www.grons.nl/i18ntest.zip"&gt;small test application&lt;/a&gt;. Unzip it, and run &lt;tt&gt;mvn jetty:run&lt;/tt&gt; to start it. When it is started access it on &lt;a href="http://localhost:8080/i18ntest"&gt;http://localhost:8080/i18ntest&lt;/a&gt;.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Changing resource settings&lt;/span&gt;&lt;br/&gt;
Finally, if you need more power, you can change all of Wicket's settings in the &lt;tt&gt;init()&lt;/tt&gt; method of your application. Call &lt;a href="http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/Application.html#getResourceSettings()"&gt;getResourceSettings()&lt;/a&gt; to get a &lt;a href="http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/settings/IResourceSettings.html"&gt;IResourceSettings&lt;/a&gt; instance. Let look at some of the options.
&lt;p/&gt;
&lt;a href="http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/settings/IResourceSettings.html#setThrowExceptionOnMissingResource(boolean)"&gt;ThrowExceptionOnMissingResource&lt;/a&gt;: this will make Wicket throw an exception when a resource is missing. At first this may seem a convenient way to test that you listed all messages in a properties file. However, many standard Wicket components use defaults as fall back, so this option is mostly useless. Alternatively, watch for warnings in the log.
&lt;p/&gt;
&lt;a href="http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/settings/IResourceSettings.html#setUseDefaultOnMissingResource(boolean)"&gt;UseDefaultOnMissingResource&lt;/a&gt;: this will make Wicket use the default value (e.g. the text within the wicket:message element) when the resource is not found in a properties file. Setting this to true (the default) may hide errors for a long time, but setting this false will make your site not work if you made an error. Choose carefully.
&lt;p/&gt;
There are many more options that allow you to change what, when and how properties are loaded. I have never found a use for these, but they are there if you need them.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Conclusion&lt;/span&gt;&lt;br/&gt;
As you have seen in this article, Wicket provides simple ways to do everything around internationalization, and some more less simple ways to completely customize this for the rare case you need it. Furthermore, the new Wicket 1.4 release will make it a lot better with support for componenta embedded in a wicket:message element. You can download the &lt;a href="http://www.grons.nl/i18ntest.zip"&gt;example&lt;/a&gt; to see everything in action.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 20080526:&lt;/span&gt; Incorperated the comment from Stefan Fußenegger on what happens if the user gives no preferred locale.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 20080527:&lt;/span&gt; Scott Swank put this article on the &lt;a href="http://cwiki.apache.org/confluence/display/WICKET/Everything+about+Wicket+internationalization"&gt;Wicket Wiki&lt;/a&gt;. Feel free to edit it there!
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 20091203:&lt;/span&gt; Corrected text after Satish_j pointed out that I made a glaring mistake in the order messages are looked up in the page hierarchy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-7886684619915297981?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/7886684619915297981/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/05/wicket-internationalization.html#comment-form' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7886684619915297981'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7886684619915297981'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/05/wicket-internationalization.html' title='Everything about Wicket internationalization'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5216676552050397252</id><published>2008-04-15T22:10:00.008+02:00</published><updated>2010-08-22T20:58:12.436+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='radiant'/><category scheme='http://www.blogger.com/atom/ns#' term='radiant-postfix-extension'/><title type='text'>Announcement: radiantpostfixextension release 1.0</title><content type='html'>I have just released a new extension for Radiant. From the description:
&lt;blockquote&gt;With this Radiant extension you can manage a small Postfix virtual domain. A Postfix virtual domain allows you to forward e-mail to another e-mail address. The extensions adds a tab to the admin screens of Radiant in which you to enter the redirects. It also supports crude but effective aliases and mail-lists. In addition the Postfix configuration is verified and instructions will be displayed if something is not right.&lt;/blockquote&gt;
Here is an &lt;a href="http://www.grons.nl/radiantpostfixextension.png"&gt;screendump&lt;/a&gt; to see the effect.
&lt;p/&gt;
The first release, boldly named 1.0, should work under Radiant 0.6.3 and up.
&lt;p/&gt;
Please visit the &lt;a href="http://code.google.com/p/radiantpostfixextension/"&gt;Radiant Postfix Extension project home&lt;/a&gt;.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 2008-04-21:&lt;/span&gt; just released version 1.1 as a file had the wrong name.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5216676552050397252?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5216676552050397252/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/04/announcement-radiantpostfixextension.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5216676552050397252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5216676552050397252'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/04/announcement-radiantpostfixextension.html' title='Announcement: radiantpostfixextension release 1.0'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-1548748212194385096</id><published>2008-03-19T13:11:00.010+01:00</published><updated>2011-09-12T10:31:08.616+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><title type='text'>Installing Maven under Mac OSX</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Update 2011-09-12&lt;/span&gt;: Forget everything that's below here. Just install &lt;a href="http://mxcl.github.com/homebrew/"&gt;HomeBrew&lt;/a&gt; and type &lt;tt&gt;brew install maven&lt;/tt&gt;.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 2008-03-28&lt;/span&gt;: Instead of following this article, you are probably better of with &lt;a href="http://www.macports.org"&gt;MacPorts&lt;/a&gt;. Thanks for the suggestion &lt;a href="http://www.blogger.com/profile/00327754125903096137"&gt;Nino&lt;/a&gt;!
&lt;p/&gt;
I did not know it could be this hard to install Maven. I've installed Maven a bunch of times already but never before on a Mac.  It took me about 1 hour!
&lt;p/&gt;
Here is what you need to do:
&lt;ul&gt;&lt;li&gt;Download the apache-maven.tar.gz.
If you follow the link on the &lt;a href="http://maven.apache.org/download.html"&gt;maven download page&lt;/a&gt; both Firefox and Safari will get an html page but save it as a tar file. Look in the downloaded file (search for the string "apache-maven") and extract a real download link. When in doubt run &lt;tt&gt;md5 apache-maven-version.tar.gz&lt;/tt&gt; on the result and compare it to the MD5 that is posted on the download page.&lt;/li&gt;&lt;li&gt;Extract the tar somewhere. For example with &lt;tt&gt;tar xzf apache-maven-version.tar.gz&lt;/tt&gt;. Here it gives an error (&lt;tt&gt;tar: A lone zero block at 3083&lt;/tt&gt;) but so far it does not seem to affect the result.
&lt;/li&gt;&lt;li&gt;Ignore all the &lt;a href="http://developer.apple.com/qa/qa2001/qa1067.html"&gt;babbling&lt;/a&gt; about environment.plist files, and just create a &lt;tt&gt;~/.bash_profile&lt;/tt&gt; and put something like the following in it:&lt;div class="codeblock"&gt;&lt;code&gt;export M2_HOME=/Users/erik/apps/apache-maven-2.0.8
export M2=$M2_HOME/bin
export JAVA_HOME=/Library/Java/Home/
export PATH=$PATH:$M2&lt;/code&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;That's it. Good luck!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-1548748212194385096?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/1548748212194385096/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/03/installing-maven-under-mac-osx.html#comment-form' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1548748212194385096'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1548748212194385096'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/03/installing-maven-under-mac-osx.html' title='Installing Maven under Mac OSX'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-2762449462505819351</id><published>2008-02-20T04:06:00.008+01:00</published><updated>2010-08-22T21:01:49.957+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Repeated lesson: always distrust 3th party code generators</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Warning&lt;/span&gt;: rant follows!
&lt;p/&gt;
I am usually try to avoid generation tools. But I fell for it again. I thought that using Wicket Web Beans would speed up development of the administration screens in my current project. Indeed, the first screens were shown very quickly. But a few weeks later, it did not turn out to be so smooth as expected.
&lt;p/&gt;
Here are some of my pain points:
&lt;ul&gt;&lt;li&gt;You must explicitly exclude properties you do not want to see. So when you add the method &lt;code&gt;boolean isPersisted()&lt;/code&gt; in the base class of all your entities, you must visit the meta data for each entity and add &lt;code&gt;"-persisted"&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Writing new types of input fields for specific properties works, but is a little clumsy.&lt;/li&gt;&lt;li&gt;I have no clue how to add validation. Especially when you consider the excellent vlidation support in Wicket has for this, it is kind weird that it is not clear how to do this with Wicket Web Beans.&lt;/li&gt;&lt;li&gt;When you have a recursive data structure, Wicket Web Beans  crashes during the render phase with a StackOverflowException!&lt;/li&gt;&lt;li&gt;Somehow I failed to get custom input fields for collection properties.
&lt;/li&gt;&lt;/ul&gt;Some positive points:
&lt;ul&gt;&lt;li&gt;Support is good though the wwb user e-mail list. Unfortunately, I failed to provide the correct input (which is source code) as we changed the code constantly in an attempt to work around  to shortcomings mentioned above. Secondly I did not want to sent our domain objects to a public list.&lt;/li&gt;&lt;li&gt;For simple domain models you are probably quickly set up and done.
&lt;/li&gt;&lt;/ul&gt;
&lt;p/&gt;
Okay, we could have worked on Wicket Web Beans itself. After all it is open source. The code quality seems quite alright though I expect you need some time to get into it. Due to project dynamics and  considering the excellent Wicket form support, I decided to pull out.
&lt;p/&gt;
Unfortunately its now 2 days before the end of the sprint. So in 2 days we will shame ourselves in the demo before the customer with almost nothing working. Hopefully we can do better next week with regular Wicket forms.
&lt;p/&gt;
&lt;span style="font-size:85%;"&gt;Note: 'me too' comments are likely to be removed. I am also very interested in success stories!&lt;/span&gt;
&lt;p/&gt;
&lt;span style="font-weight: bold;"&gt;Update, a couple of hours later&lt;/span&gt;: I reworded the article a bit as I found the first version too harsh.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-2762449462505819351?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/2762449462505819351/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/02/repeated-lessons-always-distrust-3th.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2762449462505819351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2762449462505819351'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/02/repeated-lessons-always-distrust-3th.html' title='Repeated lesson: always distrust 3th party code generators'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-7980666133955144578</id><published>2008-01-21T19:25:00.000+01:00</published><updated>2008-01-23T09:58:31.921+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='radiant'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='camping'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>I finally made up my mind</title><content type='html'>Those who read this blog more often may have noticed the minor change in the sub-title. It now reads:&lt;blockquote&gt;Experiences from a hard core Java programmer that finally decided Ruby is a very very nice language, but rather stays on the JVM.&lt;/blockquote&gt;Compare it to the previous text:&lt;blockquote&gt;Experiences from a hard core Java programmer that struggles with the question of switching to the world of Ruby and Ruby on Rails.&lt;/blockquote&gt;So how did I come to this conclusion? Here are some random considerations:
&lt;ul&gt;&lt;li&gt;I have only &lt;span style="font-style: italic;"&gt;once&lt;/span&gt; in the last 2 years used Ruby professionally (to do some counting in a site dump).&lt;/li&gt;&lt;li&gt;I do have 3 Ruby sites running (&lt;a href="http://day-to-day-stuff.blogspot.com/2007/10/announcement-version-99-does-not-exist.html"&gt;one&lt;/a&gt; &lt;a href="http://code.whytheluckystiff.net/camping/"&gt;Camping&lt;/a&gt; and &lt;a href="http://milo.grons.nl/"&gt;two&lt;/a&gt; &lt;a href="http://www.theatergroepzout.nl/"&gt;using&lt;/a&gt; &lt;a href="http://radiantcms.org/"&gt;Radiant&lt;/a&gt;). So I am not going to stop using Ruby anytime soon.
&lt;/li&gt;&lt;li&gt;ActiveRecord totally kicks ass and is hugely impressive. Rails for the rest not so much (totally old school request response based, with like JSP code in the templates). It may be just my lack of experience, so don't base your opinion on this.
&lt;/li&gt;&lt;li&gt;I am very addicted to correct &lt;span style="font-style: italic;"&gt;refactoring&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;code completion&lt;/span&gt;.
&lt;/li&gt;&lt;li&gt;As I wrote &lt;a href="http://day-to-day-stuff.blogspot.com/2007/12/demise-of-java-long-live-jvm.html"&gt;before&lt;/a&gt;, we now have &lt;a href="http://www.scala-lang.org/"&gt;Scala&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;Update 2008-01-23:&lt;/span&gt; This one needs to be added. Since I recently became a &lt;a href="http://day-to-day-stuff.blogspot.com/2007/07/little-pause.html"&gt;father&lt;/a&gt;, it is simply too much to keep track of 2 worlds at the same time.
&lt;/li&gt;&lt;/ul&gt;So if you expected me to be on &lt;a href="http://groups.google.com/group/amsterdam-rb"&gt;amsterdam.rb&lt;/a&gt; right now, here is why.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-7980666133955144578?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/7980666133955144578/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/01/i-finally-made-up-my-mind.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7980666133955144578'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7980666133955144578'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/01/i-finally-made-up-my-mind.html' title='I finally made up my mind'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-6927756068051416119</id><published>2008-01-21T19:10:00.001+01:00</published><updated>2010-08-22T21:03:05.644+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='book review'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><title type='text'>GWT in action - Book review</title><content type='html'>I recently joined &lt;a href="http://www.jteam.nl/"&gt;JTeam&lt;/a&gt;, a self declared GWT specialist. Of course I do not want to stay behind so I started reading &lt;a href="http://www.manning.com/hanson/"&gt;GWT in Action&lt;/a&gt;.
&lt;p/&gt;
That was a mistake. There is nothing wrong with GWT, I think it is a very nice component oriented technology in the spirit of Swing and Wicket, which makes it a lot easier to build very complex applications.
&lt;p/&gt;
But back to the book. Reading the book is a lot like looking at an extremely knowledgeable professor. The kind of professors that know so much that they don't know where to begin. And like with the professor you will eventually get it if you hang on and are able to sort it all out in your head.&lt;br/&gt;
Well, I was fed up with it in chapter 5, half way through the 17 chapter book. Here are my mischiefs: 1) the text was constantly referring to chapters further away in the book, 2) it was continuously repeating itself (I had to read at least 4 times that it is okay to use something else then Eclipse), but worse, 3) the text was almost continuously on a different conceptual level then the reader could be. For example implementation details are mentioned (for example the event_bits attribute in the generated HTML) while they have nothing to do with making you understand the API (writing an event callback class). Or the other way around, at places where a simple examples would do wonders there is only some broad goal description.
&lt;p/&gt;
Since there are many other books on GWT, you are probably better of with another one.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-6927756068051416119?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/6927756068051416119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/01/gwt-in-action-book-review.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6927756068051416119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6927756068051416119'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2008/01/gwt-in-action-book-review.html' title='GWT in action - Book review'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-6248705002466056887</id><published>2007-12-27T19:42:00.001+01:00</published><updated>2010-08-22T21:04:03.607+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jruby'/><category scheme='http://www.blogger.com/atom/ns#' term='book review'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>JRuby on Rails - Book review</title><content type='html'>So what do you put in a book on JRuby? After all, for the programmer JRuby is 'just' another Ruby implementation. The question must have come up when Ola Bini decided to write this book. The result is materialized in &lt;span style="font-style:italic;"&gt;&lt;a href="http://www.apress.com/book/view/1590598814"&gt;Practical JRuby on Rails Web 2.0 Projects - Bringing Ruby on Rails to the Java Platform&lt;/a&gt;&lt;/span&gt;. The title is well chosen and also answers the question: JRuby on Rails is a practical book; it guides you through the implementation of several Rails sites and along the way it shows you neat tricks that are only possible in JRuby.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Contents&lt;/span&gt;&lt;br/&gt;
The first half of the book is all about JRuby on Rails. If you already know Rails it is funny to consistently see &lt;tt&gt;jruby&lt;/tt&gt; instead of &lt;tt&gt;ruby&lt;/tt&gt;, but otherwise this is part is boring for the initiated. If you do not know Rails and like to learn from examples, this chapter is well worth your read.
&lt;p/&gt;
Right after that is an interesting part for all us that want to mix Java and Ruby.
&lt;p/&gt;
Later in the book, Ola goes through the trouble of explaining how to use JRuby with all the non interesting things in Java: session beans, message beans, JMX, XML processing and SOAP. I have been in Java business for 7 years and have been luckily able to avoid these for most of the time. The integration provided by Spring could have been a very nice replacement for these subjects.
&lt;p/&gt;
A bit hidden between this stuff, the book again shows it practicality and goes through all the options of deploying JRuby on Rails applications.
&lt;p/&gt;
The book ends with some convenient JRuby specific references.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Conclusion&lt;/span&gt;&lt;br/&gt;
I strongly recommend this book when you are a Java programmer (of any skill) that knows some Ruby and want to start working with (J)Ruby on Rails. If you know Rails well, but little Java and you want to start with JRuby on Rails, the book is probably too heavy and will teach you only a few useful things. Do not start on this book with zero Ruby knowledge. Though appendix A helps, for seriously learning Ruby, the old &lt;a href="http://whytheluckystiff.net/ruby/pickaxe/"&gt;Pickaxe&lt;/a&gt; (&lt;a href="http://www.pragprog.com/titles/ruby"&gt;dead tree version&lt;/a&gt;) is an excellent first read.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-6248705002466056887?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/6248705002466056887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/11/jruby-on-rails-book-review.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6248705002466056887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6248705002466056887'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/11/jruby-on-rails-book-review.html' title='JRuby on Rails - Book review'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5268941565552302202</id><published>2007-12-16T18:00:00.002+01:00</published><updated>2010-08-22T21:04:23.374+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='conference'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='programming philosophy'/><title type='text'>WebBeans, the JSF cover up</title><content type='html'>At &lt;a href="http://www.javapolis.com/confluence/display/JP07/Home"&gt;JavaPolis&lt;/a&gt; I saw very good &lt;a href="http://www.javapolis.com/confluence/display/JP07/JSR+299+-+Web+Beans"&gt;presentation&lt;/a&gt; on &lt;a href="http://jcp.org/en/jsr/detail?id=299"&gt;WebBeans&lt;/a&gt; by &lt;a href="http://www.javapolis.com/confluence/display/JP07/Bob+Lee"&gt;Bob Lee&lt;/a&gt;. Bob did a very good job in explaining the concepts while thankfully still giving lots of code examples. WebBeans provides a way to use ordinary Java beans in the JSF environment. You do this with annotations.
&lt;p/&gt;
Praise for WebBeans, and its main predecessor Seam; they really make developing JSF applications simpler. In particular, the conversation scope is a brilliant invention. But how simple does it get? Well lets see. For every aspect that can be managed there is an annotation. In one of the presentation examples I counted about 5 lines of ordinary Java code plus at least 12 annotations! Now there are ways to group annotations, but you do this by introducing more annotations! So instead of having to deal with JSF stuff, you now how to cook annotation soup.
&lt;p/&gt;
I have always hated JSP programming, and now that I see that even talented people like Bob Lee and Gavin King are wrestling to fix JSF, I finally know why. The problem is that &lt;span style="font-weight:bold;"&gt;the whole idea of having &lt;span style="font-style:italic;"&gt;contexts&lt;/span&gt; to store page data is flawed.&lt;/span&gt; It breaks all kinds of encapsulation rules and breaks the beloved type safety.
&lt;p/&gt;
So what are the alternatives? Actually there are a few very nice web frameworks that do not use contexts. The one I am most familiar with is &lt;a href="http://wicket.apache.org/"&gt;Wicket&lt;/a&gt;. Another often named contender is &lt;a href="http://tapestry.apache.org/"&gt;Tapestry&lt;/a&gt;, but of course there are many more. Wicket, like Swing, uses a component tree to build pages. Each component is completely responsible for its own markup (html) and data (the model). No encapsulation rules are broken. Another addition is GWT, not exactly a web framework, but nevertheless useful for making componentized web applications.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5268941565552302202?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5268941565552302202/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/12/webbeans-jsf-cover-up.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5268941565552302202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5268941565552302202'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/12/webbeans-jsf-cover-up.html' title='WebBeans, the JSF cover up'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-6909937529918086566</id><published>2007-12-16T15:37:00.001+01:00</published><updated>2010-08-22T21:05:28.941+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='programming philosophy'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>The demise of Java, long live the JVM!</title><content type='html'>The Java language is dying. Most people do not yet realize this yet, but it is inevitable. What do I mean by dying? Why do I think it is dying, and why is there lots of hope?
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Dead languages&lt;/span&gt;&lt;br/&gt;
So what do I mean by dead? First of all, languages do not die just like that. There are still people programming in Cobol, and likewise, there are still people that can read old Greek fluently. But just like Cobol and every other programming language, Java will at some time be left to the dinosaur programmers. I think that Java is currently at its peak (or will soon be), and will go downwards from here. It will take some time, but the signs are there.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;The signs&lt;/span&gt;&lt;br/&gt;
First of all, there is the rise of dynamic languages. They have been around for quite some time, but all of sudden there was Ruby on Rails. Many Java programmers bit the bullet and switched to be rewarded with a up to 10 times productivity boost. There are those that say this can be attributed to dynamic typing. I believe this is not the case. I think the boost is possible because the Java language is quite weak compared to Ruby; the same problem can be expressed in a lot less lines of Ruby then lines of Java. This has nothing to do with typing, but everything with how the language is structured.
&lt;p/&gt;
Secondly, there is the way Java is extended over time. The extensions have been done with great care. This has paid off: Java is still mostly a clean and simple language. Even with the tiny changes, each new version brought its problems. The largest change so far has been the introduction of generics. I see generics as an improvement, but &lt;a href="http://day-to-day-stuff.blogspot.com/2006/07/java-7-improvements.html"&gt;as I wrote earlier&lt;/a&gt;, they can be a pain to use.&lt;br/&gt;
And now there is the &lt;a href="http://www.ibm.com/developerworks/java/library/j-jtp04247.html"&gt;closures&lt;/a&gt; &lt;a href="http://www.juixe.com/techknow/index.php/2007/01/29/java-7-the-closure-debate/"&gt;debate&lt;/a&gt;. Some people (like James Gosling) want to have them at every price. Others (like Joshua Bloch) say that the complexity budget has been used and that Java is not ready for big changes like closures. I agree with the latter.
&lt;p/&gt;
Despite the care with which Java was and is being extended, the possibilities for doing so are reaching zero rapidly.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;There is hope!&lt;/span&gt;&lt;br/&gt;
If there is one gem coming out of the Java world, it is the Java Virtual Machine. With each release the JVM has brought performance improvements. And this is likely to continue for the next versions. The JVM is still climbing and is nowhere near its peak. This can be proved by looking at the many languages that now run on the JVM. Everything from PHP, Lisp, Cobol to Ruby and Python. Some of these (in particular Ruby) are very well supported by several IDEs.
&lt;p/&gt;
The Java language does not need closures or other more advanced stuff, there are many languages on the JVM that already provide those things: Ruby, Python, Lisp and Scala.
&lt;p/&gt;
Yes, now there is &lt;a href="http://www.scala-lang.org/"&gt;Scala&lt;/a&gt;! Scala is a language for both the JVM and the CLR. Scala's syntax is as concise as Ruby's. It provides a very smooth transition from the Java language, but nevertheless is a complete functional language while at the same time staying purely object oriented and statically typed. And, as I learned at &lt;a href="http://www.javapolis.com"&gt;Javapolis&lt;/a&gt;, its library is already excellent, and even better: without any particular optimizations Scala programs outperform Java programs on the same JVM! What is not to like about Scala?
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Disclaimer&lt;/span&gt;&lt;br/&gt;
Many statements in this article are based on personal observations and anecdotal evidence.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 2007-12-24&lt;/span&gt;: Despite the tone of this article, I am still writing Java and foresee to do so for quite a bit longer. And note: having fun doing it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-6909937529918086566?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/6909937529918086566/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/12/demise-of-java-long-live-jvm.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6909937529918086566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6909937529918086566'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/12/demise-of-java-long-live-jvm.html' title='The demise of Java, long live the JVM!'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-2626902281090881480</id><published>2007-11-22T10:34:00.002+01:00</published><updated>2010-08-22T21:06:11.312+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Double-Checked Locking found in JVM</title><content type='html'>I just found an implementation of the &lt;a href="http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html"&gt;broken double checked locking&lt;/a&gt; in the java 6 runtime library!
&lt;p/&gt;
Here is a snippet of the offending code:&lt;div class="codeblock"&gt;&lt;code class="java"&gt;package java.lang;
import java.util.Random;
public final class Math {
  private static Random randomNumberGenerator;
  private static synchronized void initRNG() {
    if (randomNumberGenerator == null) 
      randomNumberGenerator = new Random();
  }
  public static double random() {
    if (randomNumberGenerator == null) initRNG();
    return randomNumberGenerator.nextDouble();
  }
}&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
Amazingly, it used to work correctly, but the extra synchronization &lt;a href="http://bugs.sun.com/view_bug.do?bug_id=4228660"&gt;was removed in java 1.3&lt;/a&gt;. You can track the progress of this bug in &lt;a href="http://bugs.sun.com/view_bug.do?bug_id=6470700"&gt;report 6470700&lt;/a&gt; and &lt;a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6633229"&gt;report 6633229&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-2626902281090881480?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/2626902281090881480/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/11/double-checked-locking-found-in-jvm.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2626902281090881480'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2626902281090881480'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/11/double-checked-locking-found-in-jvm.html' title='Double-Checked Locking found in JVM'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-2292956233135881568</id><published>2007-11-16T08:51:00.002+01:00</published><updated>2010-08-22T21:14:43.343+02:00</updated><title type='text'>Howto extend LDAP in java with JLDAP</title><content type='html'>&lt;a href="http://en.wikipedia.org/wiki/LDAP"&gt;LDAP&lt;/a&gt; is a protocol that is wonderfully extensible. You can augment existing messages by adding 'controls', and you can define complete new messages. Extensions are identified by a universal &lt;a href="http://en.wikipedia.org/wiki/Object_identifier"&gt;OID&lt;/a&gt;, so that even code that does not know about an extensions can still work properly. For this each extension has a criticality flag to indicate whether the receiver may ignore unknown extensions. As a bonus, the content of controls and messages are all defined by a common syntax (&lt;a href="http://en.wikipedia.org/wiki/ASN.1"&gt;ASN.1&lt;/a&gt;) and common encoding (which is &lt;a href="http://en.wikipedia.org/wiki/Basic_Encoding_Rules"&gt;BER&lt;/a&gt; with restrictions).
&lt;p/&gt;
Writing you own controls and messages is a kind of under documented thing. In addition, not all LDAP libraries support all kinds of messages. In this small howto I show how to implement custom controls and messages based on my experiences with implementing a &lt;a href="http://www.rfc-editor.org/rfc/rfc4533.txt"&gt;RFC4533&lt;/a&gt; (synchronization) client. I used &lt;a href="http://www.openldap.org/jldap/"&gt;JLDAP&lt;/a&gt; as it is the only java library I could find that supports IntermediateMessages, a requirement for RFC4533. And before you ask: no sorry, I can not open source the results.
&lt;p/&gt;
In case you actually want to start with JLDAP, you get a lot of knowledge from at the examples that are provided by Novel. You can find them through the JLDAP site. The javadoc is also useful at times.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Decoding a control&lt;/span&gt;
&lt;p/&gt;
Lets take a look at the SyncDoneControl from RFC4533. The control's OID is 1.3.6.1.4.1.4203.1.9.1.3 and its content value is defined with ASN.1 as:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code&gt;syncDoneValue ::= SEQUENCE {
  cookie          OCTET STRING OPTIONAL,
  refreshDeletes  BOOLEAN DEFAULT FALSE
}&lt;/code&gt;&lt;/div&gt;&lt;br/&gt;
Read this as: the value is a sequence that contains 2 other values. The first, named &lt;code&gt;cookie&lt;/code&gt; is optional and has binary content. The second is named &lt;code&gt;refreshDeletes&lt;/code&gt; and has boolean content. The default of refreshDeletes is false. See RFC4533 for the semantics.
&lt;p/&gt;
Lets map this to java. All controls must have the same constructor signature so that JLDAP can instantiate it. We'll start with:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;public class SyncDoneControl extends LDAPControl {
  public static final String OID = "1.3.6.1.4.1.4203.1.9.1.3";
   
  private byte[] cookie;
  private boolean refreshDeletes;
  // add getters for cookie and refresDeletes
   
  public SyncDoneControl(String oid, boolean critical, byte[] value) {
    super(oid, critical, value);
    ...see below
  }
}
&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
The byte array &lt;code&gt;value&lt;/code&gt; contains the BER encoded value of the control. The LDAP restriction put on the BER encoding mean that optional values and values that are equal to the default value must be omitted. With other words: when there is no cookie (allowed because it is declared OPTIONAL), and refreshDeletes is FALSE (which is the default), constructor argument &lt;code&gt;value&lt;/code&gt; is null! Just to be robust we'll check for the empty array as well:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;  if (value == null || value.lenght == 0) {
    cookie = null;
    refreshDeletes = false;
  } else {
    ...see below
  }&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
If it is not null/empty, we'll use the decoder as provided by JLDAP to decode the bytes. As the ASN.1 value is defined to start with a SEQUENCE (one of the native ASN.1 types), the LBERDecoder will instantiate an object of type &lt;code&gt;ASN1Sequence&lt;/code&gt;:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;ASN1Sequence asn1 = (ASN1Sequence) new LBERDecoder().decode(value);&lt;/code&gt;&lt;/div&gt;

The decoder can decode all native ASN.1 types. These native types are called "universal". Other important universal types are BOOLEAN, OCTET STRING, CHOICE and SET. Type information is available on every ASN1Object through the &lt;code&gt;ASN1Object#getIdentifier()&lt;/code&gt; method.
&lt;p/&gt;
We can examine the sequence further by calling the &lt;code&gt;ASN1Sequence#size()&lt;/code&gt; and &lt;code&gt;ASN1Sequence#get(int)&lt;/code&gt; methods. Again, we must take into account that each element may be omitted. You can do this by examining the type of ASN.1 value you get out of the sequence. First, extract a value from the sequence:
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;ASN1Object asn1Obj = asn1.get(0);&lt;/code&gt;&lt;/div&gt;
When this is the cookie, the value must be from the type-class UNIVERSAL, with as type OCTET STRING:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;boolean isCookie =
   asn1Obj.getIdentifier().getASN1Class() == ASN1Identifier.UNIVERSAL &amp;&amp;
   asn1Obj.getIdentifier().getTag() == ASN1OctetString.TAG;&lt;/code&gt;&lt;/div&gt;
If it is, we can safely cast the object to an ASN1OctetString and extract the cookie:
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;cookie = ((ASN1OctetString) asn1Obj).byteValue()&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
We can do the same for the value refreshDeletes and JLPAP class ASN1Boolean. After we have moved this very verbose code to the utility class Asn1Util (exercise for the reader) we'll get the following code:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;    ASN1Sequence asn1 = (ASN1Sequence) new LBERDecoder().decode(value);
    for (int i = 0; i &lt; asn1.size(); i++) {
        ASN1Object asnSeqObj = asn1.get(i);
        if (i == 0 &amp;&amp; Asn1Util.isOctetString(asnSeqObj)) {
            cookie = Asn1Util.getByteValue(asnSeqObj);
        } else if (i == (cookie == null ? 0 : 1) &amp;&amp; Asn1Util.isBoolean(asnSeqObj)) {
            refreshDeletes = Asn1Util.getBooleanValue(asnSeqObj);
        } else {
            throw new IllegalArgumentException("Parse error at index " + i + ", parsing: " + asnSeqObj);
        }
    }&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
Tada! Your first JLDAP extension. All we have to do is make JLDAP aware of the extension and it will be parsed automatically when the control is present in a received LDAP message.&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;LDAPControl.register(SyncDoneControl.OID, SyncDoneControl.class);&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
One small warning: when there is an exception in the control's constructor, JLDAP will silently ignore your class and do its default thing.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Encoding a control&lt;/span&gt;
&lt;p/&gt;
To start a sync operation, one must add a SyncRequestControl to the search constraints. Here is the ASN.1 definition of the control value:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code&gt;syncRequestValue ::= SEQUENCE {
  mode ENUMERATED {
      refreshOnly       (1),
      refreshAndPersist (3)
  },
  cookie     OCTET STRING OPTIONAL,
  reloadHint BOOLEAN DEFAULT FALSE
}&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
First the ASN.1 enumeration is translated into a Java enumeration:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;public enum SyncRequestMode { REFRESH_ONLY, REFRESH_AND_PERSIST }&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
The we'll start the control with&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;public class SyncRequestControl extends LDAPControl {
  public static final String OID = "1.3.6.1.4.1.4203.1.9.1.1";
  private SyncRequestMode mode;
  private byte cookie[];
  boolean reloadHint = false;&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
As we will construct this control ourself, and not JLDAP, we can give it any constructor we like. For example:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;public SyncRequestControl(SyncRequestMode mode, byte cookie[], boolean reloadHint) {
  super(OID, true, null);
  this.mode = mode;
  this.cookie = cookie;
  this.reloadHint = reloadHint;
  setValue(encodedValue());
}&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
In the last line we set the BER encoded value. Here is a complete implementation of the encode method. Note how we follow the ASN.1 definition, but skip optional values and values that have the default value.&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;private byte[] encodedValue() throws IOException {
  ASN1Sequence asn1 = new ASN1Sequence();
  asn1.add(new ASN1Enumerated(mode == REFRESH_ONLY ? 1 : 3));
  if (cookie != null) {
    asn1.add(new ASN1OctetString(cookie));
  }
  if (reloadHint) {
    asn1.add(new ASN1Boolean(reloadHint));
  }
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  new LBEREncoder().encode(asn1, baos);
  return baos.toByteArray();
}&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;More complex example, decoding a message&lt;/span&gt;
&lt;p/&gt;
The JLDAP BER decoder can only decode ASN.1 universal types. As soon as you define your own types, you must help the decoder. Lets look at the decoding of the SyncInfoMessage to see how this works. The value of SyncInfoMessage is defined with the following ASN.1:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code&gt;syncInfoValue ::= CHOICE {
   newcookie      [0] OCTET STRING,
   refreshDelete  [1] SEQUENCE {
        cookie         OCTET STRING OPTIONAL,
        refreshDone    BOOLEAN DEFAULT TRUE
    },
    refreshPresent [2] SEQUENCE {
        cookie         OCTET STRING OPTIONAL,
        refreshDone    BOOLEAN DEFAULT TRUE
    },
    syncIdSet      [3] SEQUENCE {
        cookie         OCTET STRING OPTIONAL,
        refreshDeletes BOOLEAN DEFAULT FALSE,
        syncUUIDs      SET OF OCTET STRING (SIZE)16))
    }
}&lt;/code&gt;&lt;/div&gt;&lt;br/&gt;
The ASN.1 defines that the value can have one or four values. We'll represent the chosen value with a Java enumeration. By defining the enum values in order, we can abuse that the ordinal value of the enum values corresponds to the tag (defined between brackets []) value.&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;public static enum SyncInfoMessageChoiceType {
  // Note: order is important
  NEW_COOKIE, REFRESH_DELETE, REFRESH_PRESENT, SYNC_ID_SET
}&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
As SyncInfoMessage is an intermediate response, we'll start the message implementation as:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;public class SyncInfoMessage extends LDAPIntermediateResponse {
  public static final String OID = "1.3.6.1.4.1.4203.1.9.1.4";
  private SyncInfoMessageChoiceType syncInfoMessageChoiceType;
  private byte[] cookie;
  private Boolean refreshDone;
  private Boolean refreshDeletes;
  private List&lt;byte[]&gt; syncUuids;
  // add getters ...&lt;/code&gt;&lt;/div&gt;&lt;br/&gt;
Not all fields will always get a value. For example field syncUuids will only be set when &lt;code&gt;syncInfoMessageChoiceType == SYNC_ID_SET&lt;/code&gt;. This is the most simple implementation, and the user of this class must know about the CHOICE type anyway.
&lt;p/&gt;
Intermediate messages must always have the same constructor, so that JLDAP can construct it for us:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;public SyncInfoMessage(RfcLDAPMessage message) {
  super(message);
  ...&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
The choice is represented by an instance of type ASN1Tagged. The identifier of the tag indicates the choice. Instantiate field syncInfoMessageChoiceType so:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;ASN1Tagged asn1Choice = (ASN1Tagged) new LBERDecoder().decode(getValue());
int tag = asn1Choice.getIdentifier().getTag();
syncInfoMessageChoiceType = SyncInfoMessageChoiceType.values()[tag];&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
Now comes the tricky part. As JLDAP has no clue about the ASN.1 definition, it does not know about the choice, and it can not decode any further. What we &lt;i&gt;can&lt;/i&gt; do is get the contents of &lt;code&gt;asn1Choice&lt;/code&gt; as an OCTET STRING, get its byte array, and decode that again with the JLDAP decoder.
&lt;p/&gt;
So for most choice types we need to decode the tag's contents to a SEQUENCE. Here is a utility method we can add to the AsnUtil class:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;public static ASN1Sequence parseContentAsSequence(ASN1Tagged asn1Choice) throws IOException {
  ASN1OctetString taggedValue = (ASN1OctetString) asn1Choice.taggedValue();
  byte[] taggedContent = taggedValue.byteValue();
  return new ASN1Sequence(new LBERDecoder(), new ByteArrayInputStream(taggedContent), taggedContent.length);
}&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
With this tool we'll decode the choice refreshPresent. Notice how we decode the contents of the tag, and how refreshDone is set to its default value when we did not see it in the sequence.&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;if (syncInfoMessageChoiceType == SyncInfoMessageChoiceType.REFRESH_PRESENT) {
  ASN1Sequence asn1Seq = Asn1Util.parseContentAsSequence(asn1Choice);
  for (int i = 0; i &lt; asn1Seq.size(); i++) {
    ASN1Object asnSeqObj = asn1Seq.get(i);
    if (i == 0 &amp;&amp; Asn1Util.isOctetString(asnSeqObj)) {
      cookie = Asn1Util.getByteValue(asnSeqObj);
    } else if ((i == (cookie == null ? 0 : 1)) &amp;&amp; Asn1Util.isBoolean(asnSeqObj)) {
      refreshDone = Asn1Util.getBooleanValue(asnSeqObj);
    } else {
      throw new RuntimeException("Parse error");
    }
  }
  if (refreshDone == null) {
      refreshDone = Boolean.TRUE;
  }
}&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
When the choice is newCookie things are a bit simpler. The content of &lt;code&gt;asnChoice&lt;/code&gt; is already an OCTET_STRING, so we can use that directly:&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;if (syncInfoMessageChoiceType == SyncInfoMessageChoiceType.NEW_COOKIE) {
  ASN1OctetString taggedValue = (ASN1OctetString) asn1Choice.taggedValue();
  cookie = taggedValue.byteValue();
}&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
Again, we have to make JLDAP aware of the new message. it will be parsed automatically when the control is present in a received LDAP message.&lt;br/&gt;
&lt;div class="codeblock"&gt;&lt;code class="java"&gt;LDAPIntermediateResponse.register(SyncInfoMessage.OID, SyncInfoMessage.class);&lt;/code&gt;&lt;/div&gt;
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Conclusion&lt;/span&gt;
&lt;p/&gt;
In this article I showed how to get started with extending JLDAP. The example are functional but not always complete. Nor are they always according to best practices (for example, I would normally not declare so many exceptions). I shared some of the pitfalls you will encounter when encoding and decoding messages and controls.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-2292956233135881568?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/2292956233135881568/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/11/howto-extend-ldap-in-java-with-jldap.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2292956233135881568'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2292956233135881568'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/11/howto-extend-ldap-in-java-with-jldap.html' title='Howto extend LDAP in java with JLDAP'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5845945829356994604</id><published>2007-10-29T20:57:00.010+01:00</published><updated>2010-09-22T14:46:06.284+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='radiant'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><category scheme='http://www.blogger.com/atom/ns#' term='camping'/><title type='text'>Getting started with Ruby on Ubuntu 7.10</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style="font-size: larger;"&gt;&lt;span style="font-weight:bold;"&gt;Update 20100922&lt;/span&gt;: Apparently this article is so out of date that I even get e-mail to get this article down (or replace it).&lt;/p&gt;
&lt;p style="font-size: larger;"&gt;Therefore: &lt;span style="font-style:italic;"&gt;please do not use this article&lt;/span&gt;, in fact don't even rely on the Ubuntu packages as they are not all maintained properly.&lt;/p&gt;
&lt;p style="font-size: larger;"&gt;One approach that may be fine (I did not try, don't blame me), is on &lt;a href="http://krainboltgreene.github.com/l/3/"&gt;http://krainboltgreene.github.com/l/3/&lt;/a&gt;. Good luck!&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
So now I have a new server, and a new 10Mbit/s internet connection. Its time for some applications! Here are my experiences with installing Rails, Radiant and Camping on a fresh Ubuntu 7.10 server installation.
&lt;p/&gt;
First of all, if your server is running in a closet somewhere (like mine) or worse: in a remote data center, you need to disable apt-get asking for the installation CD. Edit &lt;code&gt;/etc/apt/sources.list&lt;/code&gt; and comment the line that refers to the installation cdrom.
&lt;p/&gt;
The next step of course it to install &lt;span style="font-weight:bold;"&gt;Ruby&lt;/span&gt; and &lt;span style="font-weight:bold;"&gt;Ruby Gems&lt;/span&gt;:
&lt;code&gt;sudo apt-get install ruby rubygems ruby1.8-dev&lt;/code&gt;
Without the &lt;code&gt;ruby1.8-dev&lt;/code&gt; you can not do much, don't forget it!
&lt;p/&gt;
Where would you be without irb? The default install however does not link it!
&lt;code&gt;sudo ln -s /usr/bin/irb1.8 /usr/bin/irb&lt;/code&gt;
&lt;p/&gt;
Any serious Ruby web application uses &lt;span style="font-weight:bold;"&gt;Mongrel&lt;/span&gt;. However, Mongrel compiles some of its stuff during the install. So you first need to get the compilation tools:&lt;br/&gt;
&lt;code&gt;sudo apt-get install make gcc libc6&lt;/code&gt;
&lt;p/&gt;
Mongrel is a trusted enterprise application nowadays, so you can install it with a certificate:&lt;div style="overflow: auto;"&gt;&lt;pre&gt;wget http://rubyforge.org/frs/download.php/25325/mongrel-public_cert.pem
gem cert --add mongrel-public_cert.pem
rm mongrel-public_cert.pem
gem install mongrel --include-dependencies -P HighSecurity&lt;/pre&gt;&lt;/div&gt;
Select the highest version followed by &lt;code&gt;(ruby)&lt;/code&gt;.
&lt;p/&gt;
Luckily, installing &lt;span style="font-weight:bold;"&gt;Rails&lt;/span&gt; is still a simple:
&lt;code&gt;sudo gem install rails --include-dependencies&lt;/code&gt;
&lt;p/&gt;
If you want &lt;span style="font-weight:bold;"&gt;Camping&lt;/span&gt;, you'll first need to install sqlite:
&lt;code&gt;sudo apt-get install libsqlite3 libsqlite3-dev&lt;/code&gt;
&lt;p/&gt;
Now you can:&lt;div style="overflow: auto;"&gt;&lt;pre&gt;sudo gem install camping --source http://code.whytheluckystiff.net
sudo gem install camping-omnibus --source http://code.whytheluckystiff.net&lt;/pre&gt;&lt;/div&gt;
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Radiant&lt;/span&gt; is a simple:
&lt;code&gt;sudo gem install radiant&lt;/code&gt;
&lt;p/&gt;
For some reason the gem executables do not get added to the path. Make sure they do by adding &lt;code&gt;/var/lib/gems/1.8/bin&lt;/code&gt; to your path. For example by adding the following line at the end of &lt;code&gt;/etc/bash.bashrc&lt;/code&gt;:
&lt;code&gt;export PATH="$PATH:/var/lib/gems/1.8/bin"&lt;/code&gt;.
&lt;p/&gt;
The favorite database amongst Railers is of course &lt;span style="font-weight:bold;"&gt;MySQL&lt;/span&gt;. I assume you selected LAMP during the Ubuntu server installation, that means you already have MySQL installed. To use MySQL with ActiveRecord, its best to install the MySQL native adapter. Before you can install that, you first need to install all the compile stuff from above and some more:
&lt;code&gt;sudo apt-get install libmysqlclient15-dev
sudo gem install mysql&lt;/code&gt;
Again, for the gem select highest version number followed by &lt;code&gt;(ruby)&lt;/code&gt;.
&lt;p/&gt;
Have fun!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5845945829356994604?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5845945829356994604/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/10/getting-started-with-ruby-on-ubuntu-710.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5845945829356994604'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5845945829356994604'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/10/getting-started-with-ruby-on-ubuntu-710.html' title='Getting started with Ruby on Ubuntu 7.10'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-78052726467467783</id><published>2007-10-24T14:34:00.001+02:00</published><updated>2010-08-22T21:17:55.891+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>How to really test RAM, or My search for system stability</title><content type='html'>I recently bought a Mac because my previous system kept crashing. It would just beep, and reboot without a visible cause. Since I still wanted to use the old system for my Linux firewall, I needed to find out what the culprit was.
&lt;p/&gt;
Over time I found out that:
&lt;br/&gt;- Reboots started intermittently after placing new memory. However, &lt;a href="http://www.memtest86.com/"&gt;Memtest86&lt;/a&gt; reported no problems whatsoever.
&lt;br/&gt;- Over time the reboots occurred more and more often.
&lt;br/&gt;- The problem occurred more often during heavy disk activity, sometimes after only 2 minutes. I could not even finish a long Cygwin install session.
&lt;br/&gt;- According to &lt;a href="http://www.almico.com/speedfan.php"&gt;SpeedFan&lt;/a&gt; my processor heated up quite a bit (up to 65&amp;#186;C), after putting a bit of heat sink paste between the CPU and the heat sink, that problem  was solved.
&lt;br/&gt;- The same SpeedFan reported that my harddisk went to high temperatures as well, reaching 50&amp;#186;C but still rising when the system went down. Some searches taught me that this is high but acceptable. A full copy of the harddisk (as a USB drive) did not give any problems.
&lt;br/&gt;- Replacing the power unit did not help.
&lt;p/&gt;
When I had moved everything to another motherboard an interesting thing happened: once, just once out of many reboots I got a memory failure. Got you!
&lt;p/&gt;
I finally was able to pin the wrong RAM module using an old &lt;a href="http://people.redhat.com/dledford/memtest.html"&gt;memory test&lt;/a&gt; from &lt;a href="http://people.redhat.com/dledford/"&gt;Doug Ledford&lt;/a&gt;.
&lt;p/&gt;
Since the shown script can not be used as is, here is what I did to make it work on Ubuntu 7.10:
&lt;br/&gt;- Download a Linux kernel from &lt;a href="http://kernel.org/"&gt;kernel.org&lt;/a&gt; (we are not going to compile a kernel, we just need a large zip file):
  &lt;code&gt;wget http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.23.1.tar.bz2&lt;/code&gt;
&lt;br/&gt;- Transform it to a gzipped tar:
&lt;br/&gt;  &lt;code&gt;bunzip2 linux-2.6.23.1.tar.bz2
&lt;br/&gt;  gzip linux-2.6.23.1.tar
&lt;br/&gt;  cp linux-2.6.23.1.tar.gz /tmp&lt;/code&gt;
&lt;br/&gt;- Download the adapted &lt;a href="http://www.grons.nl/memtest.sh"&gt;memtest.sh&lt;/a&gt;. My changes auto-detects files named linux-*.tar.gz, and uses the file name to predict the name of the root folder in the tar.
&lt;br/&gt;- One by one place a memory module in your computer and run memtest.sh for each configuration.
&lt;p/&gt;
The &lt;a href="http://people.redhat.com/dledford/memtest.html"&gt;original memtest.sh site&lt;/a&gt; has more information on how the script works and why Memtest86 is actually useless. The point is that a modern CPU can not put enough load on your memory. With concurrent DMA transfers more errors are detected.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-78052726467467783?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/78052726467467783/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/10/how-to-really-test-ram-or-my-search-for.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/78052726467467783'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/78052726467467783'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/10/how-to-really-test-ram-or-my-search-for.html' title='How to really test RAM, or My search for system stability'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-1114637357548389081</id><published>2007-10-23T10:28:00.001+02:00</published><updated>2010-08-22T21:21:16.831+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Help! Looking for a Java LDAP client library!</title><content type='html'>I often read that Java is very mature and that you can find Java libraries for everything. Wrong!
&lt;p/&gt;
At least 5 complete working days I have spend investigating how to implement the LDAP synchronization protocol as supported by the OpenLdap server (RFC4533) in our Java product. Here are the libraries I investigated. None of them support RFC455 out of the box.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;&lt;a href="http://java.sun.com/products/jndi/"&gt;JNDI&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
Sun's implementation is alright for most things, and now that the JVM is being open sourced, you can actually see the com.sun classes you need to program with (BerEncoder and BerDecoder) for new LDAP controls. Unfortunately, JNDI is not actively developed anymore. As far as I can see the required LDAP Intermediate Response Messages (RFC4511, the most recent definition of LDAP) is not supported. No idea on how to add this to JNDI either.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;&lt;a href="https://opends.dev.java.net/"&gt;OpenDS&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
Another Sun initiative, the open source directory server. The code shows support for the LDAP Intermediate Response Message however, all code is written from a server perspective. I did not see how this code could be used in a client.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;&lt;a href="http://directory.apache.org/"&gt;ApacheDS&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
The code of this directory server has clearly separated code that could be used by both client and server. However, again, no support for the Intermediate Response Message.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;&lt;a href="http://www.openldap.org/jldap/"&gt;JLDAP&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
I could not download the sources as our firewall does not allow CVS to go through. I'll investigate later. I doubt that Intermediate Response Messages are supported as there is not much development going on here. I think Novel's resources are all tied to their newer products.
&lt;p/&gt;
So, still no go. What should I do? Any synchronization supported by OpenLdap will do. Help!
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2007-10-23&lt;/span&gt;: It seems that JLDAP has support for Intermediate Response Messages after all! Meanwhile a friendly colleague at another location has downloaded the code for me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-1114637357548389081?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/1114637357548389081/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/10/help-looking-for-java-ldap-client.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1114637357548389081'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1114637357548389081'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/10/help-looking-for-java-ldap-client.html' title='Help! Looking for a Java LDAP client library!'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5084627816932029603</id><published>2007-10-10T13:09:00.012+02:00</published><updated>2011-12-19T21:11:35.557+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='camping'/><category scheme='http://www.blogger.com/atom/ns#' term='version99'/><title type='text'>Announcement: Version 99 Does Not Exist</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Last update: 2011-12-19&lt;/span&gt;&lt;p/&gt;

&lt;span style="font-size:larger;"&gt;&lt;span style="font-weight:bold;"&gt;Update 2011-08-15&lt;/span&gt;: &lt;a href="http://day-to-day-stuff.blogspot.com/2011/08/version-99-dns-off-line.html"&gt;DNS for Version 99 is offline.&lt;/a&gt;&lt;/span&gt;&lt;p/&gt;

In a previous post I announced &lt;a href="http://day-to-day-stuff.blogspot.com/2007/07/no-more-commons-logging.html"&gt;no-commons-logging&lt;/a&gt; (a.k.a. commons-logging version 99.0-does-not-exist). After a request to add no-commons-logging-api I immediately realized that this can be generalized. So here it is: Version 99 Does Not Exist.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Features&lt;/span&gt;&lt;br/&gt;
Version 99 Does Not Exist emulates a Maven 2 repository and serves empty jars for any valid package that has version number &lt;span style="font-style:italic;"&gt;99.0-does-not-exist&lt;/span&gt;. It also generates poms, metadata files and of course the appropriate hashes.
&lt;p/&gt;
For example the following links will give an &lt;a href="http://no-commons-logging.zapto.org/mvn2/commons-logging/commons-logging/99.0-does-not-exist/commons-logging-99.0-does-not-exist.jar"&gt;empty jar&lt;/a&gt;, its &lt;a href="http://no-commons-logging.zapto.org/mvn2/commons-logging/commons-logging/99.0-does-not-exist/commons-logging-99.0-does-not-exist.pom"&gt;pom&lt;/a&gt; and the &lt;a href="http://no-commons-logging.zapto.org/mvn2/commons-logging/commons-logging/maven-metadata.xml"&gt;maven metadata&lt;/a&gt; for commons-logging.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Why?&lt;/span&gt;&lt;br/&gt;
To get rid of dependencies that maven 2 insists on including on your classpath (like commons-logging while you want to use jcl-over-slf4j).
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;How do you use it?&lt;/span&gt;&lt;br/&gt;
First of all: if you were using no-commons-logging before, you do not need to change anything! Version 99 Does Not Exist is fully backward compatible with no-commons-logging. Otherwise, read on.
&lt;p/&gt;
In your pom.xml declare the following 2 things: 1) the Version 99 Does Not Exist repository, and 2) for each jar that you get but do not want, declare a dependency with version 99.0-does-not-exist.
&lt;p/&gt;
So, for example, if you do not want to be bothered with commons-logging, include the following in your pom.xml:&lt;br/&gt;
&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;&lt;div class="code"&gt;&lt;pre class="xml"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;repositories&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;repository&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;id&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;Version99&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/id&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;name&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;Version 99 Does Not Exist Maven repository&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/name&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;layout&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;default&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/layout&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;url&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;http://no-commons-logging.zapto.org/mvn2&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/url&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/repository&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/repositories&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;dependencies&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="color: rgb(128, 128, 128); font-style: italic;"&gt;&amp;lt;!-- get empty jar instead of commons-logging --&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;commons-logging&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;commons-logging&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;99.0-does-not-exist&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/dependencies&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;span style="font-weight:bold;"&gt;When?&lt;/span&gt;
Right now. As I received questions on how stable this service will be, I hereby promise to keep this maven repository on-line for at least 5 years (or until no-ip stops offering their free dns service). Read further if you want to run Version 99 Does Not Exist yourself.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;How?&lt;/span&gt;&lt;br/&gt;
Version 99 Does Not Exist is implemented in a single file as a &lt;a href="http://code.whytheluckystiff.net/camping/"&gt;Camping&lt;/a&gt; application.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Where is the code?&lt;/span&gt;&lt;br/&gt;
If you want to run it yourself you'll need the following: &lt;a href="http://no-commons-logging.zapto.org/version99.rb"&gt;Version 99 Does Not Exist download&lt;/a&gt; (rb file, 4Kb, MIT license), Ruby, Ruby Gems and &lt;a href="http://code.whytheluckystiff.net/camping/"&gt;Camping&lt;/a&gt;. Version 99 Does Not Exist is started with a simple &lt;tt&gt;camping version99.rb&lt;/tt&gt;. Good camping!
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 2007-11-06&lt;/span&gt;: The empty jar is no longer completely empty as 'mvn site' fails on it. Thanks to Stefan Fußenegger for the report.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 2007-12-14&lt;/span&gt;: The repository was off line for a day or so because I goofed while switching internet provider. Now, it is not only working again, but reachability is better then ever: 10 Mbit/s up and down!
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 2008-02-09&lt;/span&gt;: Version 1.2: another update to the empty jar. Sasha Ovsankin reported that the compiler could not open it. The jar now contains a valid manifest. Thanks Sacha!
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 2009-05-01&lt;/span&gt;: During my move from Amsterdam to Haarlem the server has been off line for a day or so. I am now on ADSL so my internet connection is a lot slower. If anyone want to run a mirror, I am happy to set up a rotating DNS.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 2009-07-24&lt;/span&gt;: Version 1.3: artifacts with a groupid that contain a dot are now supported. Éric Vigeant, thanks for the bug report!
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 2009-10-17&lt;/span&gt;: Version 2.0: Éric Vigeant's problems were still not over. Now removed metadata support, this will hopefully make some proxies behave better.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 2011-08-15&lt;/span&gt;: &lt;a href="http://day-to-day-stuff.blogspot.com/2011/08/version-99-dns-off-line.html"&gt;DNS for Version 99 is offline.&lt;/a&gt;
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Update 2011-12-19&lt;/span&gt;: I just found an alternative with almost the same name: &lt;a href="http://version99.qos.ch/"&gt;version99.qos.ch&lt;/a&gt;. It is a static maven repository with a limited number of version 99 jars.
&lt;p/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5084627816932029603?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5084627816932029603/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/10/announcement-version-99-does-not-exist.html#comment-form' title='32 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5084627816932029603'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5084627816932029603'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/10/announcement-version-99-does-not-exist.html' title='Announcement: Version 99 Does Not Exist'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>32</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-4693678256009202834</id><published>2007-10-08T15:35:00.001+02:00</published><updated>2010-08-22T21:24:37.576+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='camping'/><title type='text'>Watch where you place those Camping constants!</title><content type='html'>Finally got that bastard &lt;a href="http://code.whytheluckystiff.net/camping/"&gt;Camping&lt;/a&gt; application to work properly. I kept getting a &lt;tt&gt;ERROR: wrong number of arguments (0 for 3)&lt;/tt&gt; on the first request. Subsequent requests would sometimes succeed, sometimes not. The funny thing was, the get method was not even called yet!
&lt;p/&gt;
With deep gratitude for &lt;a href="http://blog.remvee.net/"&gt;ruby guru Remco van 't Veer&lt;/a&gt; who found out that you can not use constants in a Camping Controller (actually, I already knew it from a previous camping app, but I had stupidly forgotten it). Move it up to the main module and you're good to go.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Wrong&lt;/span&gt;&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;&lt;div class="code"&gt;Camping.goes :Nowhere
module Nowhere
  module Controllers
    MY_CONSTANT = 'wrong'   # Camping going nowhere
  end
end&lt;/div&gt;&lt;/div&gt;&lt;br/&gt;
&lt;span style="font-weight:bold;"&gt;Correct&lt;/span&gt;&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;&lt;div class="code"&gt;Camping.goes :ForPresident
module ForPresident
  MY_CONSTANT = 'fine'
  module Controllers
  end
end&lt;/div&gt;&lt;/div&gt;&lt;br/&gt;
See &lt;a href="http://day-to-day-stuff.blogspot.com/2007/10/announcement-version-99-does-not-exist.html"&gt;my next post&lt;/a&gt; to see what this is all about.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-4693678256009202834?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/4693678256009202834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/10/watch-where-you-place-those-camping.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4693678256009202834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4693678256009202834'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/10/watch-where-you-place-those-camping.html' title='Watch where you place those Camping constants!'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5194948204160617320</id><published>2007-10-01T20:48:00.000+02:00</published><updated>2007-10-10T13:31:28.681+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='camping'/><title type='text'>Camping: optional group in regular expression</title><content type='html'>Recently I finished a &lt;a href="http://code.whytheluckystiff.net/camping/"&gt;Camping&lt;/a&gt; application where I wanted to handle URIs with an optional part. Like so:
&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;&lt;div class="code"&gt;class Jar &lt; R '/(.*)\.jar(\.sha1|\.md5)?'
  def get(jarname, hash)  # does not work
    ...
  end
end&lt;/div&gt;&lt;/div&gt;
Unfortunately this does not work. (Does this look familiar? See my &lt;a href="http://day-to-day-stuff.blogspot.com/2007/10/announcement-version-99-does-not-exist.html"&gt;next post&lt;/a&gt;!) In the end it turned out to be as simple as:
&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;&lt;div class="code"&gt;class Jar &lt; R '/(.*)\.jar(\.sha1|\.md5)?'
  def get(jarname, hash = nil)
    ...
  end
end&lt;/div&gt;&lt;/div&gt;
At RailsconfEurope &lt;a href="http://www.fngtps.com/"&gt;Manfred&lt;/a&gt; taught me another trick:
&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;&lt;div class="code"&gt;n = 4
class DigitParty &lt; R "/" + ("(\\d)?" * n)
  def get(*args)
    args.length   # -&gt; 4
    args[0]       # -&gt; first digit or nil
    args[n - 1]   # -&gt; last digit or nil
  end
end&lt;/div&gt;&lt;/div&gt;
This matches anything from 0 to n digits where a group is created for each digit. Using * in the parameter list will make method &lt;tt&gt;get&lt;/tt&gt; work for any value of n. For URI's with less then n digits, the corresponding array elements are nil.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5194948204160617320?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5194948204160617320/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/10/camping-optional-group-in-regular.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5194948204160617320'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5194948204160617320'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/10/camping-optional-group-in-regular.html' title='Camping: optional group in regular expression'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-7718224740070049874</id><published>2007-09-05T14:30:00.000+02:00</published><updated>2007-09-05T14:49:11.592+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><title type='text'>Multi user mac?</title><content type='html'>Well who would have thought? I have become a Mac user.

After seeing Windows Vista at my moms computer, I decided I had enough of MS. And I love it! No noise, very useful software included, very quick to start and stop, and my girl-friend and I are simply always logged in. &lt;a href="http://www.apple.com/macosx/features/fastuserswitching/"&gt;Switching&lt;/a&gt; takes 2 clicks and 2 seconds.

Then an idea struck me. I still have a spare monitor and attaching a second keyboard and mouse is not so hard either: why can we not work simultaneously? Unfortunately this is not supported. But why not?

My question of the month: Why is the Mac still a single user system?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-7718224740070049874?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/7718224740070049874/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/09/multi-user-mac.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7718224740070049874'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7718224740070049874'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/09/multi-user-mac.html' title='Multi user mac?'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-2427445680831768854</id><published>2007-08-01T11:13:00.001+02:00</published><updated>2010-08-22T21:25:59.241+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='mule'/><title type='text'>Having fun with Mule</title><content type='html'>&lt;a href="http://mule.mulesource.org/display/MULE/Home"&gt;Mule&lt;/a&gt;, a very nice open source ESB implementation kept me busy in a not very nice way for 2 days.
&lt;p/&gt;
In the Mule configuration you can inject properties that are created from another containers, in my case: Spring. You do so with the &lt;tt&gt;&amp;lt;container-property&gt;&lt;/tt&gt;. However, it seemed that these properties were not set on the services that need them. A full day of debugging Mule configuration parsing code gave no results. The properties were correctly loaded from the Spring container but somehow they were not used.
&lt;p/&gt;
When we found out that another deployment unit of our project did work correctly we started comparing the configurations. It turned out that the configuration was not at all the problem. The problem was a rouge spring bean.
&lt;p/&gt;
In my setup &lt;a href="http://mule.mulesource.org/jira/browse/MULE-659"&gt;Mule is started by Spring&lt;/a&gt;. From the Spring configuration you simply instantiate Mule with some bootstrap code as follows:
&lt;p/&gt;
&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;&lt;div class="code"&gt;&lt;pre class="xml"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;bean&lt;/span&gt; &lt;span style="color: rgb(0, 0, 102);"&gt;id&lt;/span&gt;=&lt;span style="color: rgb(255, 0, 0);"&gt;"muleManager"&lt;/span&gt; &lt;span style="color: rgb(0, 0, 102);"&gt;class&lt;/span&gt;=&lt;span style="color: rgb(255, 0, 0);"&gt;"org.mule.extras.spring.config.SpringMuleBootstrap"&lt;/span&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;property&lt;/span&gt; &lt;span style="color: rgb(0, 0, 102);"&gt;name&lt;/span&gt;=&lt;span style="color: rgb(255, 0, 0);"&gt;"configResources"&lt;/span&gt; &lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;list&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;value&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;classpath:mule-config.xml&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/value&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/list&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/property&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/bean&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p/&gt;
Unfortunately, one of the other beans that are created by Spring instantiates a MuleClient in its constructor. The new MuleClient starts a Mule core (the MuleManager) if it can not find a running one. I guess you see the problem, a bit later the bootstrap code creates another MuleManager, but by now it is too late and somehow all the container properties get ignored.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2007-08-01&lt;/span&gt;: More container property problems: the first transformer gets injected nicely. Somehow another instance is used later on. Not injected with container properties of course.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-2427445680831768854?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/2427445680831768854/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/08/having-fun-with-mule.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2427445680831768854'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2427445680831768854'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/08/having-fun-with-mule.html' title='Having fun with Mule'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-174159103920643351</id><published>2007-07-19T10:32:00.003+02:00</published><updated>2011-08-15T20:49:13.292+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='version99'/><title type='text'>No more commons-logging</title><content type='html'>&lt;span style="font-style:italic;"&gt;Update 2007-10-10&lt;/span&gt;: No-commons-logging has been superseded by (backward compatible) &lt;a href="http://day-to-day-stuff.blogspot.com/2007/10/announcement-version-99-does-not-exist.html"&gt;Version 99 Does Not Exist&lt;/a&gt;.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Disclaimer&lt;/span&gt;&lt;br/&gt;
COMMONS-LOGGING VERSION 99.0-does-not-exist IS NOT IN ANY WAY AFFILIATED WITH THE ORIGINAL DEVELOPERS OF COMMONS-LOGGING NOR WITH APACHE.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Why no-commons-logging?&lt;/span&gt;&lt;br/&gt;
If you are using &lt;a href="http://maven.apache.org/"&gt;Maven&lt;/a&gt; you'll know it is practically impossible to move away from &lt;a href="http://jakarta.apache.org/commons/logging/"&gt;commons-logging&lt;/a&gt; (with its &lt;a href="http://www.qos.ch/logging/classloader.jsp"&gt;class-loading problems&lt;/a&gt;) and migrate to &lt;a href="http://www.slf4j.org/"&gt;SLF4J&lt;/a&gt;. About every second pom declares it is dependent on commons-logging. Unfortunately Maven does not provide an easy way to exclude a certain package throughout your project. You will have to exclude commons-logging on each and every dependency you need (including transitive dependencies).
&lt;p/&gt;
No-commons-logging is a Maven hack that allows you to exclude commons-logging from your application with a single piece of configuration. 
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;How does no-commons-logging work?&lt;/span&gt;&lt;br/&gt;
No-commons-logging is a Maven2 package that mimics a commons-logging package with a high version number, but without any actual java code in the jar. This trick works because Maven allows you to specify a specific version for a dependency, and that version will then be used regardless of other dependency specifications.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2007-07-22:&lt;/span&gt; Added package to mimic commons-logging-api as requested by &lt;a href="http://olamy.blogspot.com/2007/07/bye-bye-commons-logging.html"&gt;Olivier Lamy&lt;/a&gt;.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;How do you use no-commons-logging?&lt;/span&gt;&lt;br/&gt;
In your pom.xml include the following piece of xml:
&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;&lt;div class="code"&gt;&lt;pre class="xml"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;repositories&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;repository&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;id&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;no-commons-logging&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/id&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;name&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;No-commons-logging Maven Repository&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/name&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;layout&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;default&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/layout&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;url&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;http://no-commons-logging.zapto.org/mvn2&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/url&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/repository&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/repositories&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;dependencies&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="color: rgb(128, 128, 128); font-style: italic;"&gt;&amp;lt;!-- use no-commons-logging --&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;commons-logging&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;commons-logging&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;99.0-does-not-exist&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="color: rgb(128, 128, 128); font-style: italic;"&gt;&amp;lt;!-- no-commons-logging-api, if you need it --&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;commons-logging&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;commons-logging-api&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;99.0-does-not-exist&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="color: rgb(128, 128, 128); font-style: italic;"&gt;&amp;lt;!-- the slf4j commons-logging replacement --&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;org.slf4j&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;jcl104-over-slf4j&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;1.4.2&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="color: rgb(128, 128, 128); font-style: italic;"&gt;&amp;lt;!-- the other slf4j jars --&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;org.slf4j&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;slf4j-api&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;1.4.2&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="color: rgb(128, 128, 128); font-style: italic;"&gt;&amp;lt;!-- using log4j as backend --&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;org.slf4j&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;slf4j-log4j12&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;1.4.2&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;log4j&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/groupId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;log4j&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/artifactId&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;1.2.14&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/version&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/dependency&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/dependencies&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Disclaimer&lt;/span&gt;&lt;br/&gt;
COMMONS-LOGGING VERSION 99.0-does-not-exist IS NOT IN ANY WAY AFFILIATED WITH THE ORIGINAL DEVELOPERS OF COMMONS-LOGGING NOR WITH APACHE.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-174159103920643351?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/174159103920643351/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/07/no-more-commons-logging.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/174159103920643351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/174159103920643351'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/07/no-more-commons-logging.html' title='No more commons-logging'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5597859541931684850</id><published>2007-07-13T16:24:00.001+02:00</published><updated>2010-08-22T21:32:21.498+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='mule'/><title type='text'>(Pre-)announcement: transport-smtpin, an embedded e-mail server for Mule</title><content type='html'>I am proud to (pre-)announce the open source project: transport-smtpin. Transport-smtpin acts as an embedded e-mail SMTP server for the &lt;a href="http://mule.codehaus.org/"&gt;Mule&lt;/a&gt; ESB implementation. Under the cover transport-smtpin uses &lt;a href="http://subethasmtp.tigris.org/"&gt;SubEthaSMTP Mail Server&lt;/a&gt; (the last freaking java SMTP implementation).
&lt;p/&gt;
I'll post a download location as soon as I am convinced the implementation is stable (should not take long). Please let me know if you are interested. It will be released under something like the Mozilla Public License Version 1.1.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2007-07-16&lt;/span&gt;: This code was sponsored by &lt;a href="http://www.uzorg.nl"&gt;Uzorg BV&lt;/a&gt;.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2007-07-17&lt;/span&gt;: A project proposal was made on &lt;a href="http://www.muleforge.org/"&gt;MuleForge&lt;/a&gt;.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2007-8-12&lt;/span&gt;: The project has been accepted by MuleForge: &lt;a href="http://xircles.muleforge.org/projects/mule-transport-smtpin"&gt;mule-transport-smtpin project page&lt;/a&gt;. There is no code there yet. It seems to work for me, but not all features are thoroughly tested, nevertheless I'll try to upload the code this week so that more people can play with it.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;A bit of history&lt;/span&gt;&lt;br/&gt;
Transport-smtpin came about when I was searching for a way to directly receive e-mails. In the Dutch health-care a much used standard (mis-)uses e-mail as a request-response protocol: the &lt;a href="http://www.ozis.nl/"&gt;OZIS&lt;/a&gt; standard. The actual messages are in an attachment and are defined by one of many Edifact variants.
&lt;p/&gt;
In the system I am currently working on, there is actually a person waiting for the answer in such a OZIS request-response cycle. So receiving the responses by polling a POP server would incur an unacceptable overhead.
&lt;p/&gt;
In an earlier attempt to get e-mail directly we used &lt;a href="http://james.apache.org/"&gt;Apache James&lt;/a&gt;. Our mailet installed in James passes received e-mails immediately to our ESB with a &lt;a href="http://www.caucho.com/resin-3.0/protocols/hessian.xtp"&gt;Hessian&lt;/a&gt; web service. Though writing a mailet is quite easy, this set-up has some serious disadvantages:&lt;ul&gt;&lt;li style="margin-top: 0px;"&gt;Installing and configuring James for this task is not easy nor straightforward.&lt;/li&gt;&lt;li&gt;In a cluster, we need to either install one James per cluster node, or devise some way to redirect e-mails to the right cluster node.&lt;/li&gt;&lt;/ul&gt;
In an attempt to get rid of James I stumbled upon &lt;a href="http://subethasmtp.tigris.org/"&gt;SubEthaSMTP Mail Server&lt;/a&gt;. SubEthaSMTP is a quite easy to use SMTP server implementation.
&lt;p/&gt;
As we are using &lt;a href="http://mule.codehaus.org/"&gt;Mule&lt;/a&gt;, it would be neat if SubEthaSMTP could be configured as a transport. It took me 3 days (mostly to read Mule documentation), but the result is there: my very first open source project.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5597859541931684850?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5597859541931684850/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/07/announcement-embedded-e-mail-server-for.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5597859541931684850'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5597859541931684850'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/07/announcement-embedded-e-mail-server-for.html' title='(Pre-)announcement: transport-smtpin, an embedded e-mail server for Mule'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-1965132710920717036</id><published>2007-07-06T21:20:00.000+02:00</published><updated>2008-01-23T09:58:58.661+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='radiant'/><title type='text'>Making a site within 1 hour</title><content type='html'>Pardon my little pause on this blog. I just got a son: Milo. Feel free to look at &lt;a href="http://milo.grons.nl/"&gt;Milo's website&lt;/a&gt;. I build it with &lt;a href="http://radiantcms.org/"&gt;Radiant&lt;/a&gt;. It took me, including the first content, less then 1 hour!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-1965132710920717036?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/1965132710920717036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/07/little-pause.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1965132710920717036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1965132710920717036'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/07/little-pause.html' title='Making a site within 1 hour'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-8551441247590970258</id><published>2007-05-24T22:13:00.000+02:00</published><updated>2007-05-24T22:31:10.754+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Wicket for BSCs, part II</title><content type='html'>So you are in a &lt;a href="http://day-to-day-stuff.blogspot.com/2006/09/wicket-for-bscs.html"&gt;Big Slow Company (BSC)&lt;/a&gt; and want to switch to &lt;a href="http://incubator.apache.org/wicket/"&gt;Wicket&lt;/a&gt;. I wrote about this &lt;a href="http://day-to-day-stuff.blogspot.com/2006/09/wicket-for-bscs.html"&gt;before&lt;/a&gt;, but today's &lt;a href="http://www.nabble.com/wicket-vs.-struts-presentation-tf3795289.html#a10790427"&gt;e-mail from Mark Stock&lt;/a&gt; on the Wicket user list backs up the arguments with some hard figures.

Here is an abstract with some key phrases:
&lt;blockquote&gt;In my experience, it's taken me about two weeks to get up to speed on Wicket. ..... Now, the prototype that I have so far would have probably taken me at least two weeks in Struts 1 and I already know Struts 1 very well. The difference in productivity between the two frameworks is pretty dramatic.&lt;/blockquote&gt;
So? What are you waiting for!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-8551441247590970258?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/8551441247590970258/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/wicket-for-bscs-part-ii.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8551441247590970258'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8551441247590970258'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/wicket-for-bscs-part-ii.html' title='Wicket for BSCs, part II'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-1940244491411746484</id><published>2007-05-11T11:33:00.001+02:00</published><updated>2010-08-22T21:33:10.181+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Comparing XML in a JUnit test</title><content type='html'>Today I tried to compare 2 XML documents in a JUnit test. One was created with Altova's MapForce, the other was the result of a new XmlBeans document (BTW, both are nice products). Notice that these XML documents use a slightly different notation for the main namespace:
&lt;p/&gt;
Document one:
&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&gt;
&amp;lt;Message xmlns="http://www.a.nl/a10.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="C:/longpath/a10.xsd"&gt;
&amp;lt;MessageHeader&gt;....&lt;/div&gt;
&lt;p/&gt;
Document two:
&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&gt;
&amp;lt;a:Message xmlns:a="http://www.a.nl/a10.xsd"&gt;
&amp;lt;a:MessageHeader&gt;....&lt;/div&gt;
&lt;p/&gt;
Here is what I tried:
&lt;p/&gt;
1. org.w3c.dom.Document.equals. Well, that goes nowhere.
&lt;p/&gt;
2. org.dom4j.Document.equals. Same.
&lt;p/&gt;
3. &lt;a href="http://xmlunit.sourceforge.net/"&gt;XMLUtil&lt;/a&gt;'s XMLAssert.assertXMLEqual. Bummer, works alright, but it says that &lt;tt&gt;Message&lt;/tt&gt; and &lt;tt&gt;a:Message&lt;/tt&gt; are different and they are not (they're in the same namespace!).
&lt;p/&gt;
4. &lt;a href="http://juxy.tigris.org/"&gt;Juxy&lt;/a&gt;'s XMLComparator.assertXMLEquals. No go, same result.
&lt;p/&gt;
5. I took a short look at the site of &lt;a href="http://xsltunit.org/"&gt;XSLTunit&lt;/a&gt;. It says that XSLTunit is a proof of concept. Furthermore, this one is also targetted at XSLT testing. So I decided to skip it.
&lt;p/&gt;
6. Reading a bit closer I noticed that XMLUtil 1.0 released in April 2003 (wow, that's old), has a followup: XMLUtil 1.1beta1 released in April 2007 (wow, that's new). The website says they fixed the namespace thing! Unfortunately they didn't (yet, I hope).
&lt;p/&gt;
7. The final solution: with some String.replaces, I just removed the namespace stuff and the schema location from the documents. XMLUtil 1.0 now works nicely with very good diff messages.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2007-05-24&lt;/span&gt; I was quite wrong. XmlUnit does notice the differences in namespace usage (and puts a message in the exception), but it does not fail until it sees a real difference. The real difference turned out to be whitespace. By adding the code added below, the differences disappear.
&lt;p/&gt;
&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;XMLUnit.setControlParser("org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
XMLUnit.setTestParser("org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
XMLUnit.setSAXParserFactory("org.apache.xerces.jaxp.SAXParserFactoryImpl");
XMLUnit.setTransformerFactory("org.apache.xalan.processor.TransformerFactoryImpl");
&lt;span style="font-weight:bold;"&gt;XMLUnit.setIgnoreWhitespace(true);&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-1940244491411746484?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/1940244491411746484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/comparing-xml-in-junit-test.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1940244491411746484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1940244491411746484'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/comparing-xml-in-junit-test.html' title='Comparing XML in a JUnit test'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-219466166484698666</id><published>2007-05-08T17:30:00.001+02:00</published><updated>2010-08-22T21:33:40.106+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='networking'/><title type='text'>Firewall gone crazy!</title><content type='html'>Today I saw the solution to one of my weirdest problems ever. We have been searching for about a whole week why our JEE application would suddenly stop. The problem was that it took at least an hour to reproduce it (if at all), and even then we could find next to nothing in the logs. We only started to see the light when we noticed the difference between environments where the error was reproducable, and where is was not reproducable: the firewall.
&lt;p/&gt;
Turns out the firewall between the application server and the database server would stop all trafic on the JDBC connection after an hour if idling, &lt;span style="font-style:italic;"&gt;without actually killing the connection&lt;/span&gt;. Weblogic, and all other applications we tried, totally freak out when this happens. What on earth did the creators of that firewall think? When you kill a connection, then kill it! Do not just stop the data flow!
&lt;p/&gt;
Well anyway, the solution? We now let Weblogic 'test' the connection every 10 minutes. Apparently, the firewall is happier when there is some traffic now and then.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-219466166484698666?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/219466166484698666/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/firewall-gone-crazy.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/219466166484698666'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/219466166484698666'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/firewall-gone-crazy.html' title='Firewall gone crazy!'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-1841817232021176406</id><published>2007-05-05T17:34:00.001+02:00</published><updated>2010-08-22T21:34:04.158+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='book review'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Agile Web Development with Rails, 2nd edition - Book review</title><content type='html'>About time for another book review: &lt;a href="http://www.pragmaticprogrammer.com/titles/rails/"&gt;Agile Web Development with Rails, 2nd edition&lt;/a&gt;, written (mostly) by &lt;a href="http://pragdave.pragprog.com/pragdave/"&gt;Dave Thomas&lt;/a&gt;.
&lt;p/&gt;
The book shows you all facets of developing a Rails application. This is actually a big improvement over the first edition. That one did not even talk about migrations. There have been a lot of changes in Rails, and these are reflected in the second edition.
&lt;p/&gt;
Just like the first edition, the second edition is well written and has the same structure: the first chapters walk you through creating a new application, the latter chapters provide more insight in the separate building blocks of Rails. Unfortunately the book never really goes to the bottom of things (though its pretty good in the Active Record area); there is simply too much to cover. So this book is probably not a good buy if you are already an experienced Rails developer and are prepared to find information about new Rails features on the internet.
&lt;p/&gt;
So although it took me a couple of months to read this book from cover to cover, my conclusion on this book is short: &lt;span style="font-style:italic;"&gt;this is a must-have for any new Rails developer&lt;/span&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-1841817232021176406?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/1841817232021176406/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/agile-web-development-with-rails-2nd.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1841817232021176406'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1841817232021176406'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/agile-web-development-with-rails-2nd.html' title='Agile Web Development with Rails, 2nd edition - Book review'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5198418759581067810</id><published>2007-05-04T16:12:00.000+02:00</published><updated>2007-07-16T14:50:48.935+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='conference'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Aslak Hellesøy to speak on RubyEnRails 2007!</title><content type='html'>I just heard that &lt;a href="http://blog.aslakhellesoy.com/"&gt;Aslak Hellesøy&lt;/a&gt; will speak at the &lt;a href="http://2007.rubyenrails.nl/"&gt;RubyEnRails 2007&lt;/a&gt; conference! If want to know more about Behavior driven development (BDD), or love to hear more about RSpec, come to the conference!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5198418759581067810?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5198418759581067810/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/aslak-hellesoy-to-speak-on-rubyenrails.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5198418759581067810'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5198418759581067810'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/aslak-hellesoy-to-speak-on-rubyenrails.html' title='Aslak Hellesøy to speak on RubyEnRails 2007!'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-6578913645720185149</id><published>2007-05-02T21:39:00.001+02:00</published><updated>2010-08-22T21:34:34.914+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='radiant'/><category scheme='http://www.blogger.com/atom/ns#' term='conference'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='camping'/><title type='text'>RubyEnRails 2007 site is live! (Dutch)</title><content type='html'>Vanaf vandaag kunnen mensen zich aanmelden voor RubyEnRails dag 2007! Op de website http://2007.rubyenrails.nl kan je je aanmelden met je OpenID.
&lt;p/&gt;
Het sprekers gedeelte op de pagina is nog niet volledig. De lijst met sprekers zal zo spoedig mogelijk worden aangevuld. Er zullen diverse presentaties plaatsvinden over o.a. RESTful ontwikkeling, Behaviour Driven Development, Radiant en het Camping Framework.
&lt;p/&gt;
Dus ben jij een Ruby On Rails specialist of ben je gewoon geïnteresseerd in Ruby of Ruby on Rails? Meldt je dan nu aan! Deelname is overigens geheel gratis!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-6578913645720185149?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/6578913645720185149/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/rubyenrails-2007-site-is-live-dutch.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6578913645720185149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6578913645720185149'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/rubyenrails-2007-site-is-live-dutch.html' title='RubyEnRails 2007 site is live! (Dutch)'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-4918205267818717826</id><published>2007-05-01T18:48:00.000+02:00</published><updated>2007-05-01T18:48:55.510+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Wicket article translated to French</title><content type='html'>My article &lt;a href="http://day-to-day-stuff.blogspot.com/2007/01/backward-compatible-ajax-development.html"&gt;Backward compatible AJAX development with Wicket&lt;/a&gt; has just been tranlated to &lt;a href="http://zedros.developpez.com/articles/java/wicket/ajax/"&gt;French&lt;/a&gt; by &lt;a href="http://www.developpez.net/forums/member.php?u=51129"&gt;ZedroS&lt;/a&gt;!
Nice work Joseph!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-4918205267818717826?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/4918205267818717826/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/04/wicket-article-translated-to-french.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4918205267818717826'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4918205267818717826'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/04/wicket-article-translated-to-french.html' title='Wicket article translated to French'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-2511048605284329601</id><published>2007-05-01T14:15:00.000+02:00</published><updated>2007-05-01T14:20:10.097+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>New layout</title><content type='html'>I liked the little dots, but they were also a little too psychedelic. With 9 days to go for my blog's first anniversary, I decided it was time for a new layout: Tictac blue.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-2511048605284329601?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/2511048605284329601/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/new-layout.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2511048605284329601'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2511048605284329601'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/05/new-layout.html' title='New layout'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-6667007750947711531</id><published>2007-04-26T09:01:00.001+02:00</published><updated>2010-08-22T21:36:05.055+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Spring integration test hacking</title><content type='html'>&lt;blockquote&gt;&lt;span style="font-style:italic;"&gt;Update 2007-05-01&lt;/span&gt;: You probably can not follow this article if you did not work with Spring tests yet. The key to this article is at the end, where I show how to call an injected object while it has been proxied by Spring.&lt;/blockquote&gt;
A colleague (Levi Hoogenberg) showed me a working example of integration tests with Spring. The key is to make a &lt;a href="http://www.junit.org/"&gt;JUnit&lt;/a&gt; test that inherits from &lt;tt&gt;&lt;a href="http://www.springframework.org/docs/api/org/springframework/test/annotation/AbstractAnnotationAwareTransactionalTests.html"&gt;AbstractAnnotationAwareTransactionalTests&lt;/a&gt;&lt;/tt&gt; (they like long names at Spring). Simply override &lt;tt&gt;getConfigLocations()&lt;/tt&gt; and a complete spring context will be loaded. Any setters in your test class will automatically be called with beans from the context (called &lt;a href="http://www.springframework.org/docs/api/org/springframework/test/AbstractDependencyInjectionSpringContextTests.html#setAutowireMode(int)"&gt;auto-wiring&lt;/a&gt;). In addition, you can execute some SQL to initialize a database (for example by calling &lt;tt&gt;&lt;a href="http://www.springframework.org/docs/api/org/springframework/test/AbstractTransactionalDataSourceSpringContextTests.html#executeSqlScript(java.lang.String,%20boolean)"&gt;executeSqlScript()&lt;/a&gt;&lt;/tt&gt; in &lt;tt&gt;&lt;a href="http://www.springframework.org/docs/api/org/springframework/test/AbstractTransactionalSpringContextTests.html#onSetUpBeforeTransaction()"&gt;onSetUpBeforeTransaction&lt;/a&gt;()&lt;/tt&gt;). Each test gets a fresh view at the filled database and runs in a fresh new transaction.
&lt;p/&gt;
My main spring configuration (in spring.xml) sets up a context with all the services and its &lt;a href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt; backend. The &lt;a href="http://www.mysql.com"&gt;MySQL&lt;/a&gt; database connection is set up in a separate file (spring-db.xml). The test however, is not using MySQL but an in-memory &lt;a href="http://www.h2database.com/"&gt;H2&lt;/a&gt; database so that it can easily be run from &lt;a href="http://maven.apache.org/continuum/"&gt;Continuum&lt;/a&gt;. This is configured in another file (spring-test.xml).
&lt;p/&gt;
A big advantage of this setup is that it allows you to test most of the real application wiring. Disadvantage is that it uses Hibernate against H2 and not the MySQL target database. I am not too worried about this, I am not using advanced Hibernate features and I do not have to test Hibernate!
&lt;p/&gt;
So I was happily writing tests until I found out that one integration test went much too far. The service I was trying to test (lets call it AbcService) did more then just save something in the database; it also called another service to schedule a task. In a real integration test I would need to assert that the task would have been scheduled. I realized that I was actually misusing the integration test to also do a unit test on AbcService. Instead of writing a proper unit test, I decided to leave it at that. 
&lt;p/&gt;
So how was I going to assert that the scheduler service was called? Here are my attempts:
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Solution 1: Test specific config files&lt;/span&gt;&lt;br/&gt;
Since you can not override a small part of the configuration, this solution requires you to duplicate a lot of configuration files. Furthermore, you loose the ability to test the actual application configuration files. As soon as I realized this I gave up on the idea.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Solution 2: Override the configured service by changing the setter in the test class&lt;/span&gt;&lt;br/&gt;
The test class (&lt;tt&gt;AbcServiceImplTest&lt;/tt&gt;) has a setter to inject the service under test like so:
&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;public void setAbcService(AbcService abcService) {
  this.abcService = abcService;
}&lt;/div&gt;
Pretty standard. So my idea was to override the used scheduler service like this:
&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;public void setAbcService(AbcService abcService) {
  this.abcService = abcService;

  // Override the scheduler service with a mock
  SchedulerService mockSchedulerService = ...
  ((AbcServiceImpl) abcSerive).setSchedulerService(
      mockSchedulerService);  // Cast fails!
}&lt;/div&gt;
Unfortunately, the passed in &lt;tt&gt;abcService&lt;/tt&gt; is not the real thing. It has been proxied by Spring to add transaction support. The proxy that Spring uses (the standard JDK proxy) can only be casted to the implemented interfaces, and not to an actual class.
&lt;p/&gt;
After a lot of searching and looking with the debugger, I finally found a solution. Be warned: this is a big hack. Do try this at home, but don't complain when it suddenly fails.
&lt;p/&gt;
&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto;" &gt;public void setAbcService(AbcService abcService) {
  this.abcService = abcService;

  // Override the scheduler service with a mock
  SchedulerService mockSchedulerService = ...
  InvocationHandler invocationHandler =
      Proxy.getInvocationHandler(abcService);
  try {
    invocationHandler.invoke(
      abcService,
      AbcServiceImpl.class.getMethod(
        "setSchedulerService",
        new Class[] {SchedulerService.class}),
      new Object[] {mockSchedulerService});
  } catch (Throwable e) {
    fail("setSchedulerService failed");
  }
}&lt;/div&gt;
Incredible, isn't it?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-6667007750947711531?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/6667007750947711531/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/04/spring-integration-test-hacking.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6667007750947711531'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6667007750947711531'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/04/spring-integration-test-hacking.html' title='Spring integration test hacking'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-492211710542329797</id><published>2007-03-23T09:16:00.001+01:00</published><updated>2010-08-23T08:12:17.892+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='conference'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Ruby and Rails conference update</title><content type='html'>The preparations for Ruby and Rails 2007 in Amsterdam are progressing quite well. We are proud to have &lt;a href="http://drnicwilliams.com/"&gt;Dr. Nic&lt;/a&gt; fly in to give a presentation. Also the rest of the conference is already well filled with many speakers. Mind you, the official announcement has not been done yet!
&lt;p/&gt;
We are only searching for more people that want to present a quickie. A quickie is a presentation of 5 minutes sharp. You could use it for example to show some cool stuff. So if you are around in June, and you made something new, or saw a nice new feature somewhere, feel free to e-mail us at danny at rubyenrails dot nl.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2007-04-23&lt;/span&gt;: Unfortunately &lt;a href="http://simonwillison.net/"&gt;Simon Willison&lt;/a&gt; canceled. He felt too much out of place on a Ruby and Rails conference.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-492211710542329797?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/492211710542329797/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/03/ruby-en-rails-conference-update.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/492211710542329797'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/492211710542329797'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/03/ruby-en-rails-conference-update.html' title='Ruby and Rails conference update'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-6375867999527462796</id><published>2007-03-08T19:35:00.001+01:00</published><updated>2010-08-23T08:12:40.827+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Turmoil in Wicket land</title><content type='html'>The impossible task faced by the Wicket team is finally coming to an end. For months and months work was done on 2 branches (1.3 and 2.0), with a third in maintenance mode (1.2). Three branches is realy too much for a group of volunteers. For the uninitiated: the 1.3 and 2.0 branches differ in one major big way: 2.0 has one extra argument to each and every component. Another large difference is that 2.0 supports generic models. For the rest, most new features and fixes were added to both 1.3 and 2.0.
&lt;p/&gt;
So what are the options? It seems that the 'constructor change' will be dropped. This means that the 2.0 branch will be abandoned. All other 2.0 only features will be ported to 1.3. Users that use the 2.0 beta code from svn will have to migrate to 1.3.
&lt;p/&gt;
Is this good news? I think so! If this turns out to be true, the excellent work of the Wicketeers will be much more focused again. Furthermore, all the good new features will be available for Wicket users of all branches!
&lt;p/&gt;
&lt;span style="font-style: italic;"&gt;Update 2007-03-19&lt;/span&gt;
And yes, it did turn out to be true:
&lt;blockquote&gt;&lt;/blockquote&gt;&lt;blockquote&gt;I think it is time to close the vote and count our blessings. [...]
&lt;p/&gt;
7 +1 binding votes, [...] 4 abstainees, and no non-binding votes.
&lt;p/&gt;
This wraps it: we will drop the constructor change and migrate all features of trunk to branch 1.x in 2 releases: everything non-Java 5 goes into 1.3 and java 5 specifics into 1.4
&lt;p/&gt;
Martijn&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-6375867999527462796?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/6375867999527462796/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/03/turmoil-in-wicket-land.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6375867999527462796'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6375867999527462796'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/03/turmoil-in-wicket-land.html' title='Turmoil in Wicket land'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-2553596967382259172</id><published>2007-03-07T21:56:00.001+01:00</published><updated>2010-08-23T08:33:08.783+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='radiant'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='camping'/><title type='text'>Vooraankondiging Ruby En Rails 2007 (Dutch)</title><content type='html'>Binnenkort is het weer zover. Een nieuwe Ruby En Rails dag. Vorig jaar was het een zeer geslaagde, zonnige en gezellige dag waar veel Rails en Ruby enthousiastelingen aanwezig waren.
&lt;p/&gt;
Ook voor dit jaar zijn we bezig om weer een dag te organiseren. Er wordt druk gezocht naar een lokatie (waarschijnlijk Amsterdam) en we zijn aan het inventariseren welke sprekers uitgenodigd worden. Waarschijnlijk zal het allemaal plaatsvinden op &lt;span style="text-decoration:line-through;"&gt;31 mei&lt;/span&gt;, maar het is 7 juni geworden.
&lt;p/&gt;
Heb je zelf nog goede ideeën of verzoeken m.b.t. sprekers op deze dag, surf dan naar &lt;a href="http://blog.rubyenrails.nl/articles/2007/03/07/rubyenrails-2007"&gt;Ruby en Rails&lt;/a&gt; en laat iets achter in de comments. Je kunt ook een e-mail sturen naar danny at rubyenrails.nl.
&lt;p/&gt;
We zijn op zoek naar sprekers voor:
&lt;ul&gt;&lt;li&gt; Presentaties van 45-50 minuten;&lt;/li&gt;&lt;li&gt; Lightning Quickies™ van 5-15 minuten.&lt;/li&gt;&lt;/ul&gt;
Mogelijke onderwerpen:
&lt;ul&gt;&lt;li&gt; Handige Ruby libraries (gems)&lt;/li&gt;&lt;li&gt; Rails plugins&lt;/li&gt;&lt;li&gt; Interessante real-world toepassingen van Rails&lt;/li&gt;&lt;li&gt; Tips &amp; truuks, leuke vondsten, enzovoorts&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-style: italic;"&gt;Update 2007-03-20&lt;/span&gt;
De datum is intussen vast gezet op 7 juni. Ruby en Rails 2007 wordt gehouden in het gebouw van de Hogeschool van Amsterdam nabij het Amstel station.
&lt;span style="font-style: italic;"&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-2553596967382259172?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/2553596967382259172/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/03/vooraankondiging-ruby-en-rails-2007.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2553596967382259172'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2553596967382259172'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/03/vooraankondiging-ruby-en-rails-2007.html' title='Vooraankondiging Ruby En Rails 2007 (Dutch)'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-564492227848490076</id><published>2007-03-06T22:38:00.001+01:00</published><updated>2010-08-23T08:33:23.656+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='radiant'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='programming philosophy'/><category scheme='http://www.blogger.com/atom/ns#' term='camping'/><title type='text'>Mule patch accepted</title><content type='html'>Another product that accepted my patch. Till today I was proud to have patches in the following products:
&lt;br/&gt;- Xml2Db (specification for an aspect of the API, no code, long time ago)
&lt;br/&gt;- Struts (something with generating session ids, not long ago enough)
&lt;br/&gt;- Wicket (MixedUrlEncoding, other patches pending)
&lt;br/&gt;- Spring (a small documentation error)
&lt;p/&gt;
Today this is extended with:
- Mule (MailMessageAdapter potentially adds "null" string)
&lt;p/&gt;
These were all small things, but I am still proud I was able to move open source further :)
&lt;p/&gt;
For the future: I wrote a Camping application to manage a virtual e-mail domain for Postfix. My plan is to convert this to a Radiant extension and make it available under the MIT license.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-564492227848490076?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/564492227848490076/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/03/mule-patch-accepted.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/564492227848490076'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/564492227848490076'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/03/mule-patch-accepted.html' title='Mule patch accepted'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-6482337038457735706</id><published>2007-03-06T22:15:00.000+01:00</published><updated>2007-04-23T19:53:27.111+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Wicket to the recue!</title><content type='html'>My previous site (for the municipality of Amsterdam) is barely finished, and I have already convinced my next client (where I work as a consultant) that Wicket is more suited for them then Spring MVC. Life is sweet!

Spring MVC has a lot of Spring quality, but the programming model is just not what I want to work with. Wicket to the rescue!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-6482337038457735706?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/6482337038457735706/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/03/wicket-to-recue.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6482337038457735706'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6482337038457735706'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/03/wicket-to-recue.html' title='Wicket to the recue!'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5139594209930505272</id><published>2007-01-30T22:24:00.001+01:00</published><updated>2010-08-29T20:44:55.437+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>More Ubuntu experiences - continued and discontinued</title><content type='html'>Che promised and delivered. Almost.
&lt;p/&gt;
The boot time was indeed fast. Che had compiled the wacom-linux kernel module so the Wacom tablet worked fine. Even dual head worked, well almost...
&lt;p/&gt;
Problem 1: The screens were in the wrong order. The second screen is standing to the left of the main screen. No problem, just one one word changed in xorg.conf, reset and done.
&lt;p/&gt;
Problem 2: The Wacom tablet driver still thinks that the second screen is to the right of the main screen. No idea how to solve this. Che thought this would require a code change and recompilation of the wacom-linux kernel module.
&lt;p/&gt;
Problem 3: The external screen gets the wrong resolution. Whatever we tried, we could not get it to run on 1850x1050, the optimal Dell 2005fpw resolution. Bummer.
&lt;p/&gt;
So again, after 2 additional hours of hacking, I am back with MS Windows. Perhaps, if time permits, I'll erase the Ubuntu partition and try familiar SuSE this week.
&lt;p/&gt;
Note: fear not if you have one of the newer Dell Latitude D620s with nVidea graphics. Dual head works a lot better on these then on my older D620 with Intel graphics.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5139594209930505272?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5139594209930505272/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/01/more-ubuntu-experiences-continued-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5139594209930505272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5139594209930505272'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/01/more-ubuntu-experiences-continued-and.html' title='More Ubuntu experiences - continued and discontinued'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5449039990150180990</id><published>2007-01-26T15:09:00.001+01:00</published><updated>2010-08-29T20:45:12.974+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>More Ubuntu experiences :(</title><content type='html'>Having little to do for a couple of days, I decided it was time to move to a real OS on my work laptop, a Dell Latitude D620. One of my colleagues very enthusiastically recommended Ubuntu. Having read a whole lot of other enthusiastic stories about Ubuntu, it was time to give it another try. This time it was to be Ubuntu 6.10.
&lt;p/&gt;
Stupid me.
&lt;p/&gt;
First of all GParted just gave me an "Error" while resizing my Window's NTFS partition. That was it, just "Error", no details whatsoever. About an hour later I had tried to downloading the latest GParted iso. Luckily this did work.
&lt;p/&gt;
Second problem was the line "BUG: soft lockup detected on CPU#0!" during startup of Ubuntu. After this the system froze up. No where to go for, my enthusiastic colleague did a google and find out that the wireless driver crashed when the hardware kill switch for the radio was on. Bummer. I flipped the switch, rebooted and Ubuntu now started. It did take a good 2 minutes longer, just to find out there is no access to a wireless network. (Why do you think the switch was off?)
&lt;p/&gt;
Third problem was the mouse. Well, actually I do not have a mouse but a very nice Wacom Graphire3 tablet. The tablet kind of works (I could move the mouse pointer) but you can not always reach the edges of the screen. You know, like the place where the menu is located. This really annoyed the hell out of me. Luckily Firefox was already installed so I searched the internet for solutions. I found several and none of them worked. There was one guy with the same setup as me who had worked on it for 3 days! After 4 hours of messing around I found a workaround by setting the mouse acceleration a lot higher.
&lt;p/&gt;
Today I gave it another try. The older Latitude D620s have a really bad completely sucking low contrast LCD screen that is totally unfit for anything serious. So I decided to configure my kick-ass 20" wide-screen Dell 2005FPW. I found a post on how to configure dual head but to no avail. Boy what a bummer.
&lt;p/&gt;
Now, I am back on Windows XP and Cygwin. Don't get me wrong, I am a big Linux fan, running it for ages already, and usually avoid MS programs (even on Windows). I liked the looks of Ubuntu and I still do. But as long as it does not run with my hardware, I will not use it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5449039990150180990?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5449039990150180990/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/01/more-ubuntu-experiences.html#comment-form' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5449039990150180990'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5449039990150180990'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/01/more-ubuntu-experiences.html' title='More Ubuntu experiences :('/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-4201268585881230541</id><published>2007-01-07T12:35:00.003+01:00</published><updated>2010-08-29T20:53:28.613+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><title type='text'>Backward compatible AJAX development with Wicket</title><content type='html'>(Also published in &lt;a href="http://blog.finalist.com/2007/01/08/ajax_en_wicket"&gt;Dutch&lt;/a&gt; and &lt;a href="http://zedros.developpez.com/articles/java/wicket/ajax/"&gt;French&lt;/a&gt;, French translation by &lt;a href="http://www.developpez.net/forums/member.php?u=51129"&gt;ZedroS&lt;/a&gt;).
&lt;p/&gt;
Despite the general acceptance of AJAX, there is still some reticence in some sectors. The rich interaction provided by AJAX can not be used by people that, because of their handicap have to use a browser that does not support Javascript or CSS. For a sector like the government it is not acceptable to exclude these people and I believe this is an attitude that should be practiced more often.
&lt;p/&gt;
Many developers today can or will only create web applications that work solely in Internet Explorer. Building an AJAX application that also works without Javascript is simply too much for many companies. To do this anyway, a number of techniques are known. A more general approach is given by &lt;a href="http://onlinetools.org/articles/unobtrusivejavascript/"&gt;Unobtrusive Javascript&lt;/a&gt;, but this article presents a different, more flexible approach that uses &lt;a href="http://wicketframework.org/"&gt;Wicket&lt;/a&gt;.
&lt;p/&gt;
Wicket is one of the few fully component based web frameworks. In such a web framework one combines components to larger components until you have a web page. The advantage of components is that it is a lot more comfortable to develop and modify components separately. With a page oriented web framework one must usually develop the whole page simultaneously. Struts for example prescribes that you first collect all information for the whole page before a JSP page will render the (whole) page.
&lt;p/&gt;
&lt;strong&gt;Composing Wicket components (a Wicket introduction)&lt;/strong&gt;
&lt;p/&gt;
Once creates a component in Wicket by writing an HTML fragment (the template) and by writing Java code that couples more components to the template. Creation and coupling  of components happens during the construction phase. During the render phase the components can add or change the fragment or even completely replace it.
&lt;p/&gt;
Lets look at an example. Here is an HTML template and the associated Java code:
&lt;div style="overflow: auto; font-family: courier new; border: 1px solid #9B9; padding: 3px;" class="xml"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;h1&lt;/span&gt; wicket:&lt;span style="color: rgb(0, 0, 102);"&gt;id&lt;/span&gt;=&lt;span style="color: rgb(255, 0, 0);"&gt;"title"&lt;/span&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;_Template title&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/h1&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style="overflow: auto; font-family: courier new; border: 1px solid #9B9; padding: 3px;" class="java"&gt;add&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;new&lt;/span&gt; Label&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(42, 0, 255);"&gt;"title"&lt;/span&gt;, &lt;span style="color: rgb(42, 0, 255);"&gt;"The real title"&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;/div&gt;
The Label component is coupled to an &lt;code&gt;h1&lt;/code&gt; element. Label will put the real title in the HTML template during the render phase. The result is:
&lt;div style="overflow: auto; font-family: courier new; border: 1px solid #9B9; padding: 3px;" class="xml"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;h1&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;The real title&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/h1&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p/&gt;
Composing components is just as simple. Suppose we want to use a title with subtitle on many places. We will create a component for that, a Panel to be precise:
&lt;div style="overflow: auto; font-family: courier new; border: 1px solid #9B9; padding: 3px;" class="xml"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;wicket:panel&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;h1&lt;/span&gt; wicket:&lt;span style="color: rgb(0, 0, 102);"&gt;id&lt;/span&gt;=&lt;span style="color: rgb(255, 0, 0);"&gt;"title"&lt;/span&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;_Template title&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/h1&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;h2&lt;/span&gt; wicket:&lt;span style="color: rgb(0, 0, 102);"&gt;id&lt;/span&gt;=&lt;span style="color: rgb(255, 0, 0);"&gt;"subtitle"&lt;/span&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;_Template subtitle&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/h2&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/wicket:panel&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style="overflow: auto; font-family: courier new; border: 1px solid #9B9; padding: 3px;" class="java"&gt;&lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;class&lt;/span&gt; TitlePanel &lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;extends&lt;/span&gt; Panel &lt;span style="color: rgb(51, 51, 51);"&gt;{&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;public&lt;/span&gt; TitlePanel&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;String id, String title, String subtitle&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(51, 51, 51);"&gt;{&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &amp;nbsp; &lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;super&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;id&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;br/&gt;
&amp;nbsp; &amp;nbsp; add&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;new&lt;/span&gt; Label&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(42, 0, 255);"&gt;"title"&lt;/span&gt;, title&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;br/&gt;
&amp;nbsp; &amp;nbsp; add&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;new&lt;/span&gt; Label&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(42, 0, 255);"&gt;"subtitle"&lt;/span&gt;, subtitle&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;br/&gt;
&amp;nbsp; &lt;span style="color: rgb(51, 51, 51);"&gt;}&lt;/span&gt;
&lt;span style="color: rgb(51, 51, 51);"&gt;}&lt;/span&gt;&lt;/div&gt;
The panel can now be used (for example in the template of another panel) with:
&lt;div style="overflow: auto; font-family: courier new; border: 1px solid #9B9; padding: 3px;" class="xml"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;span&lt;/span&gt; wicket:&lt;span style="color: rgb(0, 0, 102);"&gt;id&lt;/span&gt;=&lt;span style="color: rgb(255, 0, 0);"&gt;"titlepanel"&lt;/span&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/span&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style="overflow: auto; font-family: courier new; border: 1px solid #9B9; padding: 3px;" class="java"&gt;add&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;new&lt;/span&gt; TitlePanel&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &lt;span style="color: rgb(42, 0, 255);"&gt;"titlepanel"&lt;/span&gt;, &lt;span style="color: rgb(42, 0, 255);"&gt;"The real title"&lt;/span&gt;, &lt;span style="color: rgb(42, 0, 255);"&gt;"with a subtitle"&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;/div&gt;
&lt;p/&gt;
Linking between pages is done with the Link component:
&lt;div style="overflow: auto; font-family: courier new; border: 1px solid #9B9; padding: 3px;" class="xml"&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;a&lt;/span&gt; &lt;span style="color: rgb(0, 0, 102);"&gt;href&lt;/span&gt;=&lt;span style="color: rgb(255, 0, 0);"&gt;"#"&lt;/span&gt; wicket:&lt;span style="color: rgb(0, 0, 102);"&gt;id&lt;/span&gt;=&lt;span style="color: rgb(255, 0, 0);"&gt;"detaillink"&lt;/span&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Book details&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;span style="font-weight: bold; color: black;"&gt;&amp;lt;/a&lt;span style="font-weight: bold; color: black;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style="overflow: auto; font-family: courier new; border: 1px solid #9B9; padding: 3px;" class="java"&gt;add&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;new&lt;/span&gt; Link&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(42, 0, 255);"&gt;"detaillink"&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(51, 51, 51);"&gt;{&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;void&lt;/span&gt; onClick&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(51, 51, 51);"&gt;{&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &amp;nbsp; setResponsePage&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;new&lt;/span&gt; DetailPage&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;bookId&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;br/&gt;
&amp;nbsp; &lt;span style="color: rgb(51, 51, 51);"&gt;}&lt;/span&gt;
&lt;span style="color: rgb(51, 51, 51);"&gt;}&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;/div&gt;
The Link component will put a Wicket generated &lt;code&gt;href&lt;/code&gt; attribute on the &lt;code&gt;a&lt;/code&gt; element during the render phase. When the link is clicked the Wicket servlet will call the &lt;code&gt;onClick&lt;/code&gt; method. In this example the response page is changed to a page that is constructed on the spot (pages are of course also components). After this the response page is rendered and sent to the browser. If the &lt;code&gt;onClick&lt;/code&gt; method was left empty, the response page would not have changed and the current page is rendered again.
&lt;p/&gt;
&lt;strong&gt;Dynamic pages in Wicket&lt;/strong&gt;
&lt;p/&gt;
Links are not only for jumping to other pages. In Wicket it is just as easy to change a part of the page by replacing a component by another one. Lets extend the example a bit:
&lt;div style="overflow: auto; font-family: courier new; border: 1px solid #9B9; padding: 3px;" class="java"&gt;&lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;final&lt;/span&gt; BookDetailPanel bookDetailPanel = ...;&lt;br/&gt;
add&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;bookDetailPanel&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;br/&gt;
add&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;new&lt;/span&gt; Link&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(42, 0, 255);"&gt;"detaillink"&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(51, 51, 51);"&gt;{&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;void&lt;/span&gt; onClick&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(51, 51, 51);"&gt;{&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &amp;nbsp; bookDetailPanel.&lt;span style="color: rgb(17, 17, 17);"&gt;replaceWith&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;new&lt;/span&gt; BookDetailPanel&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;bookId&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;br/&gt;
&amp;nbsp; &lt;span style="color: rgb(51, 51, 51);"&gt;}&lt;/span&gt;
&lt;span style="color: rgb(51, 51, 51);"&gt;}&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;/div&gt;
Clicking the link leads to a change in the current page. After this the current page is rendered again, and another book is displayed. Note that exceptionally little code is needed. In many other web frameworks all information of the complete page must be collected again.
&lt;p/&gt;
The observant reader will have noticed that replacing a piece of a page is a trick that is nowadays mostly done with AJAX. In the example however, we have not used a single line of Javascript. Since the whole page is send to the browser again and again, lets change the example a bit more:
&lt;div style="overflow: auto; font-family: courier new; border: 1px solid #9B9; padding: 3px;" class="java"&gt;&lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;final&lt;/span&gt; Component bookDetailPanel = ...;&lt;br/&gt;
bookDetailPanel.&lt;span style="color: rgb(17, 17, 17);"&gt;setOutputMarkupId&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;true&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;br/&gt;
add&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;bookDetailPanel&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;br/&gt;
add&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;new&lt;/span&gt; AjaxFallbackLink&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(42, 0, 255);"&gt;"detaillink"&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(51, 51, 51);"&gt;{&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;void&lt;/span&gt; onClick&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;AjaxRequestTarget target&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(51, 51, 51);"&gt;{&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &amp;nbsp; Component newBookDetailPanel =&lt;br/&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;new&lt;/span&gt; BookDetailPanel&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;bookId&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;br/&gt;
&amp;nbsp; &amp;nbsp; newBookDetailPanel.&lt;span style="color: rgb(17, 17, 17);"&gt;setOutputMarkupId&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;true&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;br/&gt;
&amp;nbsp; &amp;nbsp; bookDetailPanel.&lt;span style="color: rgb(17, 17, 17);"&gt;replaceWith&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;newBookDetailPanel&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;br/&gt;
&amp;nbsp; &amp;nbsp; &lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;target != &lt;span style="color: rgb(127, 0, 85); font-weight: bold;"&gt;null&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(51, 51, 51);"&gt;{&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; target.&lt;span style="color: rgb(17, 17, 17);"&gt;addComponent&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;(&lt;/span&gt;newBookDetailPanel&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;br/&gt;
&amp;nbsp; &amp;nbsp; &lt;span style="color: rgb(51, 51, 51);"&gt;}&lt;/span&gt;&lt;br/&gt;
&amp;nbsp; &lt;span style="color: rgb(51, 51, 51);"&gt;}&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: rgb(51, 51, 51);"&gt;}&lt;/span&gt;&lt;span style="color: rgb(51, 51, 51);"&gt;)&lt;/span&gt;;&lt;/div&gt;
During the render phase the component AjaxFallbackLink generates both a &lt;code&gt;href&lt;/code&gt; and an &lt;code&gt;onclick&lt;/code&gt; attribute on the coupled &lt;code&gt;a&lt;/code&gt; element. Furthermore it makes sure that a number of Wicket Javascript files are added to the HTML header.
Depending on whether Javascript is supported, the browser will request either a normal URL, or it will do an AJAX call. In both cases the Wicket servlet will call the &lt;code&gt;onClick&lt;/code&gt; method. In the first case the argument of &lt;code&gt;onClick&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; and everything will work exactly as in the previous example. When an AJAX call is done, Wicket will only render the components that were added to the AjaxRequestTarget and the result of that is sent to the browser. On the browser side, the Wicket Javascript searches for the element that needs to be replaced by using the &lt;code&gt;id&lt;/code&gt; attribute. To make sure it is set, method &lt;code&gt;setOutputMarkupId(true)&lt;/code&gt; is called.
&lt;p/&gt;
With just a few lines of code we have created an AJAX application that even works in browsers without Javascript.
&lt;p/&gt;
&lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;p/&gt;
This article shows only a small piece of Wicket's AJAX capabilities. For example it is fairly easy to let the user know an AJAX call is in progress, to quickly execute some Javascript before an AJAX call (for example to disable a submit button), or to validate a form input when it changed.
&lt;p/&gt;
Wicket is not only a fantastic framework to easily build modern and maintainable web applications, it is even possible to do it in such a way that older and special browsers can deal with them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-4201268585881230541?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/4201268585881230541/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/01/backward-compatible-ajax-development.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4201268585881230541'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4201268585881230541'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2007/01/backward-compatible-ajax-development.html' title='Backward compatible AJAX development with Wicket'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-7478489594380242497</id><published>2006-12-15T08:30:00.000+01:00</published><updated>2006-12-15T08:40:19.479+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Why would you go for Wicket too?</title><content type='html'>To answer that question Ryan Crumley investigated a couple of web frameworks for long term web development. He choose Wicket for a number of teams, even though there was lots of knowledge for older things like JSP. You can read his report to the wicket user list on &lt;a href="http://www.nabble.com/Why-I-recommended-Wicket...-tf2822928.html"&gt;nabble&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-7478489594380242497?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/7478489594380242497/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/12/why-would-you-go-for-wicket-too.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7478489594380242497'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7478489594380242497'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/12/why-would-you-go-for-wicket-too.html' title='Why would you go for Wicket too?'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-8656058484075176302</id><published>2006-11-30T20:01:00.001+01:00</published><updated>2010-08-29T20:54:14.944+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Wicket most active Java Web framework forum</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/x/blogger2/7581/3402/1600/178103/nabble.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://photos1.blogger.com/x/blogger2/7581/3402/400/78707/nabble.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br/&gt;
As you can see on this &lt;a href="http://www.nabble.com/Web-Development-Framework-f16257.html"&gt;Nabble page&lt;/a&gt;, Wicket has the most active forum of all registered Java Web Frameworks.
&lt;p/&gt;
Ruby on Rails and Django are the hot things in Ruby and Phyton land. So may we conclude Wicket is &lt;span style="font-style: italic;"&gt;the&lt;/span&gt; hot thing in Java land?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-8656058484075176302?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/8656058484075176302/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/wicket-most-active-java-web-framework.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8656058484075176302'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8656058484075176302'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/wicket-most-active-java-web-framework.html' title='Wicket most active Java Web framework forum'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-7045588629615506233</id><published>2006-11-30T19:24:00.000+01:00</published><updated>2006-11-30T19:27:35.322+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jruby'/><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>Typo of the week</title><content type='html'>Found on &lt;a href="http://blogs.sun.com/tor/entry/netbeans_and_ruby_is_true"&gt;Tor Nobye's&lt;/a&gt; blog:
&lt;blockquote&gt;Hi Matt, I asked Roman Strobl the "Go to File" or "Open File" dialogs (equivalent to Idea's Ctrl+Shit+N). He assured me that this is planned to Netbeans 6.0.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-7045588629615506233?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://blogs.sun.com/tor/entry/netbeans_and_ruby_is_true' title='Typo of the week'/><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/7045588629615506233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/typo-of-week.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7045588629615506233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7045588629615506233'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/typo-of-week.html' title='Typo of the week'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-6849585769204175904</id><published>2006-11-22T20:24:00.002+01:00</published><updated>2010-08-29T20:54:31.026+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming philosophy'/><title type='text'>SOA and Application/Integration databases</title><content type='html'>Hmm, I was going to write about a new insight. &lt;a href="http://en.wikipedia.org/wiki/Service-oriented_architecture"&gt;SOA&lt;/a&gt; has been reported to give huge cost savings. I figured that must have been because they used an &lt;a href="http://martinfowler.com/bliki/ApplicationDatabase.html"&gt;application database&lt;/a&gt; and prevented the complexity of an &lt;a href="http://martinfowler.com/bliki/IntegrationDatabase.html"&gt;integration database&lt;/a&gt;.
&lt;p/&gt;
But of course, our hero &lt;a href="http://martinfowler.com/"&gt;Martin Fowler&lt;/a&gt; has already thought of that. Its right there in the second link.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-6849585769204175904?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/6849585769204175904/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/soa-and-applicationintegration.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6849585769204175904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/6849585769204175904'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/soa-and-applicationintegration.html' title='SOA and Application/Integration databases'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-9132539324085695312</id><published>2006-11-22T08:59:00.001+01:00</published><updated>2010-08-29T20:56:05.552+02:00</updated><title type='text'>Lessons from giving a workshop Introduction Spring</title><content type='html'>Yesterday I lectured a small group of students at the Hogeschool of Amsterdam to introduce them into the wonders of the Spring framework. To my surprise it was quite hard to convert the abstract ideas from Spring in to something lively and concrete.
&lt;p/&gt;
Luckily one of the students suggested to explain the workings of dependency injection by working through the 'hello world' application from the book 'Spring in Action'. This I did using Eclipse with all keyboard shortcuts I known.&lt;br/&gt;
But even then, I am not sure I could really explain why Spring is useful. Simple examples never do just to Spring's power as they are more easily written directly in Java. 
&lt;p/&gt;
I am afraid that seeing why you need Spring only comes when you are building a real application.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-9132539324085695312?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/9132539324085695312/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/lessons-from-giving-workshop.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/9132539324085695312'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/9132539324085695312'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/lessons-from-giving-workshop.html' title='Lessons from giving a workshop Introduction Spring'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5891417606641879157</id><published>2006-11-19T21:07:00.001+01:00</published><updated>2010-08-29T20:56:18.371+02:00</updated><title type='text'>Charles -JRuby- Nutter visits The Netherlands</title><content type='html'>December 19, JRuby author Charles Nutter will talk about his work on JRuby in Rotterdam, The Netherlands. The meeting is organized by my colleagues at &lt;a href="http://www.finalist.com/en"&gt;Finalist&lt;/a&gt;. The presentation is free and open for everybody. If you're interested (and of course you are), send an e-mail to &lt;a href="mailto:seminar@finalist.com"&gt;seminar@finalist.com&lt;/a&gt;.
&lt;p/&gt;
&lt;a href="http://blog.finalist.com/2006/10/27/unieke-jruby-lezing-in-nederland-door-amerikaanse-jruby-guru-charles-nutter/"&gt;More information (Dutch)&lt;/a&gt;
&lt;p/&gt;
Note: There is a rumor that Thomas Enebo, another JRuby author will also be there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5891417606641879157?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5891417606641879157/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/charles-jruby-nutter-visits-netherlands.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5891417606641879157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5891417606641879157'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/charles-jruby-nutter-visits-netherlands.html' title='Charles -JRuby- Nutter visits The Netherlands'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-4741883013701063968</id><published>2006-11-19T15:39:00.001+01:00</published><updated>2010-08-29T20:56:47.541+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>My first (bad) Ubuntu experience, long live SuSE</title><content type='html'>SuSE 9.1 is no longer supported so it was time to update my home firewall. SuSE has been my choice since its 6.0 version many year ago. But now that there are so many hallelujah stories on Ubuntu, I thought lets give it a try. The installation went well but as soon as it was prime time the computer simply rebooted right after grub had loaded the kernel and the initial ramdisk. No error message or anything else to shed some light on the matter, just a friendly beep and the process started all over. I tried the 6.06 and the 6.10 versions, with the same results. The weird thing is, the hardware is not even that old (its an AMD-K6 at 400MHz) and has more than enough memory (almost 400Mb) and harddisk space (6 and 30 Gb).
&lt;p/&gt;
After that I went through hell to get the firewall running again. My steps included:
&lt;br/&gt;- installing an ancient SuSE 7.0 from CD
&lt;br/&gt;- figuring out how to get that SuSE 10.0 installation DVD copy I accidentally still had on my MP3 player to the firewall's second harddisk
&lt;br/&gt;- trying to update SuSE 7.0 to 10.0 (it refused)
&lt;br/&gt;- figuring out how to make a SuSE 10 installation boot CD (see previous post)
&lt;br/&gt;- installing SuSE 10.0 from harddisk
&lt;p/&gt;
Weirdly enough the hardest step was to copy the SuSE 10.0 installation files to the firewall's harddisk. First I tried WarFTP to serve it from my Windows machine. However, I kept getting access denied errors (configuring WarFTP is a science), and the ftp program is not really suitable for recursive retrieval. I finally succeeded by using the smbclient program.
&lt;p/&gt;
Perhaps I'll try Ubuntu again when SuSE 10.0 is no longer supported. Unfortunately this is already next summer, sigh.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2006-11-22&lt;/span&gt;: In my hurry to write this article I made a small mistake: my previous SuSE version was 9.2 and its support has been discontinued as of today. SuSE 10.0 is good to go until October 2007. See also &lt;a href="http://en.opensuse.org/SUSE_Linux_Lifetime"&gt;SuSE lifetimes&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-4741883013701063968?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/4741883013701063968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/my-first-bad-ubuntu-experience-long.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4741883013701063968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/4741883013701063968'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/my-first-bad-ubuntu-experience-long.html' title='My first (bad) Ubuntu experience, long live SuSE'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-3276907312026332472</id><published>2006-11-19T15:14:00.001+01:00</published><updated>2010-08-29T20:58:01.112+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>Creating a SuSE installation boot CD</title><content type='html'>Suppose you have a copy of the SuSE 10.0 DVD, but you don't actually have a DVD player on your target computer.  Here is a way to work around that:
&lt;p/&gt;
Requisites:
&lt;br/&gt;- target computer must have a CD player and a BIOS that supports booting from CD
&lt;br/&gt;- target computer must have some means to get to the installation files, preferably these are put on a not-in-the-way partition on the target computer (for example on the second harddisk)
&lt;br/&gt;- a (unix) computer with package mkisofs installed
&lt;br/&gt;- some way to burn an iso image to CD
&lt;p/&gt;
Steps:
&lt;br/&gt;- cd to installfolder/boot, where installfolder is the root of the installation source
&lt;br/&gt;- execute
&lt;div style="white-space: pre; font-family:courier new; border: thin solid 0x333; overflow:auto; height: 40px;" &gt;mkisofs -R -b loader/isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table -o suse10-installation.iso loader&lt;/div&gt;
&lt;br/&gt;- burn the result to a CD
&lt;br/&gt;- make sure the target computer has a means to get to the installation source
&lt;br/&gt;- let the target computer boot from the CD
&lt;p/&gt;
When you are in the installation program you can do a ' back' to get in the installation program's menu. From there you can configure an alternate installation source.
&lt;p/&gt;
Note that if you choose to install from a harddisk partition you should NOT format it, and you should NOT try to mount that partition during the installation. If you want you can mount the partition &lt;span style="font-style: italic;"&gt;after&lt;/span&gt; the installation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-3276907312026332472?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/3276907312026332472/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/creating-suse-installation-boot-cd.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3276907312026332472'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3276907312026332472'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/11/creating-suse-installation-boot-cd.html' title='Creating a SuSE installation boot CD'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5838532683365519937</id><published>2006-10-29T14:52:00.001+01:00</published><updated>2010-08-29T20:58:46.545+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='jruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='camping'/><title type='text'>JRuby on Rails status</title><content type='html'>&lt;a href="http://docs.codehaus.org/display/JRUBY/2006/10/20/JRuby+0.9.1+Released%21"&gt;JRuby 0.9.1&lt;/a&gt; is out for a week. This is the first version in which JRuby really starts to perform and behave properly. Though Ruby on Rails has been reported to work on JRuby for a longer time, this was not repeated by many people; too many loopholes to jump through. However, with the advent of version 0.9.1 there are suddenly many people on the mailing list that are trying and achieving exactly this.
&lt;p/&gt;
Another promising release was done yesterday in Japan: &lt;a href="http://rails-asyncweb.sourceforge.net/"&gt;rails-asyncweb 0.1&lt;/a&gt;, a very fast web server for JRuby on Rails. &lt;a href="http://recompile.net/"&gt;TAKAI, Naoto&lt;/a&gt;'s asyncweb uses &lt;a href="http://asyncweb.safehaus.org/"&gt;AsyncWeb&lt;/a&gt;, a web server that prevents the synchronization pitfall that the Java Servlet API brings. AsyncWeb scales a lot better under load then traditional Java web servers. And the performance rocks! It is about 10 times faster then Webbrick under loads to 10 concurrent users. It will be very interesting to see what happens under much much larger loads. I am also curious how this will compare to &lt;a href="http://mongrel.rubyforge.org/"&gt;Mongrel&lt;/a&gt;, &lt;a href="http://www.zedshaw.com/"&gt;Zed Shaw&lt;/a&gt;'s successor for Webbrick in the C-Ruby world. Another interesting question is whether this will end Mongrel's Java port.
&lt;p/&gt;
Another web framework that works under JRuby is &lt;a href="http://camping.rubyforge.org/files/README.html"&gt;Camping&lt;/a&gt;. Camping is smaller then 4K, but quite powerful. It is a creation of the well known Ruby virtuoso &lt;a href="http://redhanded.hobix.com/"&gt;why the lucky stiff&lt;/a&gt;. If you want to get a feel for what Ruby can do but don't want to learn Ruby on Rails (which I found quite a large task) I recommend you try out Camping. Ola Bini has a &lt;a href="http://ola-bini.blogspot.com/2006/08/jruby-tutorial-2-going-camping.html"&gt;nice tutorial&lt;/a&gt; on getting you started. (Note: you should not use Camping version 1.5 because JRuby does not support continuations.) Another very good &lt;a href="http://www.rubyenrails.nl/articles/2006/10/12/proto-prototype"&gt;Camping tutorial (Dutch)&lt;/a&gt; was written by &lt;a href="http://blog.remvee.net/"&gt;Remco van 't Veer&lt;/a&gt;.
&lt;p/&gt;
&lt;span style="font-style: italic;"&gt;Update 2006-10-30&lt;/span&gt;: added Remco's tutorial.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5838532683365519937?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5838532683365519937/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/10/jruby-on-rails-status.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5838532683365519937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5838532683365519937'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/10/jruby-on-rails-status.html' title='JRuby on Rails status'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-334640581120183108</id><published>2006-10-20T21:07:00.001+02:00</published><updated>2010-08-29T21:00:10.055+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><title type='text'>Wicket autocompletion improvements</title><content type='html'>Here is how I extended Wicket's (version 1.2.2) autocompletion with 2 new features:
&lt;br/&gt;- immediatate open of the drop-down as soon as the text field gets focus
&lt;br/&gt;- define the width of the drop-down independent of the text field
&lt;p/&gt;
As the JavaScript is well hidden within Wicket and is not easy to extend, I tried another approach that worked well. I created the package &lt;tt&gt;wicket.extensions.ajax.markup.html.autocomplete&lt;/tt&gt; and placed my own edit of &lt;tt&gt;wicket-autocomplete.js&lt;/tt&gt;. Because the package is compiled into my WAR I am sure it is the first on the classpath.
&lt;p/&gt;
First of all, in the initialize function I added the following fragment:
&lt;pre&gt; if (obj.className.indexOf('immediateopen') != -1) {
     obj.onfocus=function(event){
         updateChoices();
         return null;
     }
 }
&lt;/pre&gt;
Secondly I changed function showAutoComplete to the following:
&lt;pre&gt; function showAutoComplete(){
     var position=getPosition(wicketGet(elementId));
     var menu=getAutocompleteMenu();
     var input=wicketGet(elementId);
     var rem = input.className.match(/\bmenuwidth(\d+)\b/);
     var menuwidth = (rem == null) ? input.offsetWidth : rem[1];
     menu.show();
     menu.style.left=position[0]+'px'
     menu.style.top=(input.offsetHeight+position[1])+'px';
     menu.style.width=menuwidth+'px';
     visible=1;
     hideShowCovered();
 }
&lt;/pre&gt;
That's it. Its fully backward compatible, except that if you use the classes &lt;tt&gt;immediateopen&lt;/tt&gt; or &lt;tt&gt;menuwidthXXX&lt;/tt&gt; you'll get the new behavior. If you are lazy, you can download the &lt;a href="http://omelet.zapto.org/wicket-autocomplete.js"&gt;complete file&lt;/a&gt;.
&lt;p/&gt;
Example:
&lt;pre&gt;  &amp;lt;input class="inputcode immediateopen menuwidth300" type="text" /&amp;gt;
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-334640581120183108?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/334640581120183108/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/10/wicket-autocompletion-improvements.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/334640581120183108'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/334640581120183108'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/10/wicket-autocompletion-improvements.html' title='Wicket autocompletion improvements'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-939637028274422675</id><published>2006-10-10T19:47:00.001+02:00</published><updated>2010-08-29T21:01:04.565+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming philosophy'/><title type='text'>If programming was as risky as parachute jumping...</title><content type='html'>Last Friday &lt;a href="http://www.finalist.com/en/"&gt;my company&lt;/a&gt; organized a parachute jump. Unfortunately the clouds were, typically for the season, too low and the wind was too hard. But I learned some interesting things anyway as the morning was spend on a crash course testing by Kees Blokland from &lt;a href="http://www.polteq.com/"&gt;PolTeq&lt;/a&gt;.
&lt;p/&gt;
One of the major things for testing is &lt;a href="http://en.wikipedia.org/wiki/Risk#Defined_aspects_of_risk"&gt;risk&lt;/a&gt; analysis; the amount of time spend on testing an aspect of the system is proportional to the risk of that aspect (where risk is chance of failure times impact). For fun we made a risk analysis of parachute tandem jumping. To make it more real the instructor and owner of the jumping company also joined the discussion. He told us what he does to keep jumping safe and the risk low. He named two aspects 1) &lt;span style="font-style:italic;"&gt;attitude&lt;/span&gt;, and 2) an &lt;span style="font-style:italic;"&gt;open culture&lt;/span&gt;. An open culture is important because incidents must always be reported so that they can be analyzed and prevented in the future.
&lt;p/&gt;
The attitude aspect coincided well with a discussion we had earlier about the tendency to not test well. Sometimes the client thinks it is not necessary, sometimes developers don't know how to test properly, and sometimes they don't want to.&lt;br/&gt;
I asked what the instructor did to maintain a good attitude so that we might apply his lessons in the world of software. Here was his answer:
&lt;br/&gt;1. parachute jumping in itself already attracts the right type of people,
&lt;br/&gt;2. everybody understands that safety and risk control is paramount,
&lt;br/&gt;3. maintain maximum openness about incidents,
&lt;br/&gt;4. make an example, even a bruised toe is reported to the authorities, and
&lt;br/&gt;5. take disciplinary measures &lt;span style="font-style:italic;"&gt;only&lt;/span&gt; when there is an attitude problem. These measures are usually drastic, for example a withdrawal of the jumping permit.
&lt;p/&gt;
So what can we learn from this? Here are my ideas derived from each answer:
&lt;br/&gt;1. Attract people with the right mind set. Look out for people that are both result and quality driven.
&lt;br/&gt;2. Train you project managers, and require them to organize risk assessments. In software not everything is deadly dangerous and not everythings needs to be tested.
&lt;br/&gt;3. Use bug metrics. I am not so sure about this one. But you should at least use automated testing.
&lt;br/&gt;4. Even a technical team leader can make bugs. Be sure that everybody knows that.
&lt;br/&gt;5. Don't blame people when they do something wrong. But give the pink slip when they refuse to improve.
&lt;p/&gt;
Even when you will apply these lessons, you will have to realize that only when programming will be as risky as parachute jumping, programs will become bug free.
&lt;p/&gt;
We made the jump 2 day later. It was great and there were no bugs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-939637028274422675?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/939637028274422675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/10/if-programming-was-as-risky-as.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/939637028274422675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/939637028274422675'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/10/if-programming-was-as-risky-as.html' title='If programming was as risky as parachute jumping...'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-50597547077002857</id><published>2006-10-09T15:41:00.001+02:00</published><updated>2010-08-29T21:02:41.005+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming philosophy'/><title type='text'>What makes a programmer?</title><content type='html'>The answer to this rather philosophical question came up yesterday on the way home from my company's parachute jump event (more on this in a later post). My superior explained how he used to program in the past: "Try something, and if it works add more. Things that are used more then once are moved into methods." This approach will work for small projects, but will create a big mess over time. It will definitely create a mess immediately for larger projects. We are lucky that he is a good manager, but also lucky that he does not program for our customers.
&lt;p/&gt;
So how does a good programmer work? In my opinion the sole work of a programmer is to map abstractions onto other abstractions, and to add new abstractions when this is not possible.
&lt;p/&gt;
Lets give a simple example. XML documents are a well known abstraction for a hierarchical structure of labeled values. Files are a well known abstraction for a named sequence of bytes. To persist an XML document one writes code that maps the first abstraction to the second. How to do this is well described in the XML standard, but is definitely not the only way. I have written more performant mappings for specific cases.&lt;br/&gt;
A more complex example: to understand a voice command, one of the mappings might be from a sound sample to vowels and consonants.&lt;br/&gt;
Other types of abstractions are written in functional designs, test documents, etc.
&lt;p/&gt;
So what makes an excellent programmer? An excellent programmer:
&lt;br/&gt;- Works with well known abstractions.
There is not much to know about XML documents and Files. This makes them easy to understand and versatile. RDMSs provide an abstraction for storing tabular data. They are more complex, but well know.
&lt;p/&gt;
- Will create small and consistent new abstractions.&lt;br/&gt;
An excellent programmer will create small levels of abstractions in his system with consistent terminology. For example, while processing sound samples it may be wise to create an intermediate level of abstraction that facilitates working with different sample rates and sample sizes. Consistency can be reached naming things carefully. Simply using the same names for the same things is a good step.
&lt;p/&gt;
- Can work with many levels of abstraction at the same time.&lt;br/&gt;
At times it will be necessary to understand how the different abstraction mappings interact. For example, while persisting a phone book in an XML file, you need to know what each mapping does with character encoding.
&lt;p/&gt;
So how do you do all this? Unfortunately I have no simple receipt as my manager has. I am afraid, excellent programming is still a craft.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-50597547077002857?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/50597547077002857/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/10/what-makes-programmer.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/50597547077002857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/50597547077002857'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/10/what-makes-programmer.html' title='What makes a programmer?'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-1762455133491990608</id><published>2006-10-04T09:25:00.001+02:00</published><updated>2010-08-29T21:03:05.808+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>Top3 reasons for using NetBeans</title><content type='html'>Here is my top 3 reasons for using NetBeans:
&lt;br/&gt;
&lt;br/&gt;3. You are masochistic, and like to change IDEs once every while
&lt;br/&gt;2. JPA support is apparently quite good
&lt;br/&gt;1. You are going to work for Sun&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-1762455133491990608?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/1762455133491990608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/10/top3-reasons-for-using-netbeans.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1762455133491990608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1762455133491990608'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/10/top3-reasons-for-using-netbeans.html' title='Top3 reasons for using NetBeans'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-7420649364441849754</id><published>2006-10-03T14:08:00.001+02:00</published><updated>2010-08-29T21:03:15.736+02:00</updated><title type='text'>Wicket getting even more serious</title><content type='html'>I just saw on the Wicket e-mail list that there is now a company that provides commercial Wicket support. Their name is, guess what: &lt;a href="http://www.wicket-support.com/"&gt;Wicket Support&lt;/a&gt;.
&lt;p/&gt;
Their initial coverage is basically Europe and United States. But they are interested in extending it if the opportunity arrives.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-7420649364441849754?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.wicket-support.com/' title='Wicket getting even more serious'/><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/7420649364441849754/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/10/wicket-getting-even-more-serious.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7420649364441849754'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/7420649364441849754'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/10/wicket-getting-even-more-serious.html' title='Wicket getting even more serious'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-8003699086156691078</id><published>2006-10-02T21:35:00.001+02:00</published><updated>2010-08-29T21:03:53.116+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='book review'/><title type='text'>Wicket getting serious, Pro Wicket - Book Review</title><content type='html'>One of the interesting new web frameworks that saw the light recently is &lt;a href="http://wicket.sourceforge.net/"&gt;Wicket&lt;/a&gt;. Wicket is quite radical in that it relies solely on Java programming and does away with configuration and JSP/PHP/Rhtml type html generation. Although not unique in this approach (e.g. &lt;a href="http://www.barracudamvc.org"&gt;Barracuda&lt;/a&gt;, &lt;a href="http://tapestry.apache.org/"&gt;Tapestry&lt;/a&gt;) many web-developers will have a hard time getting up to full speed quickly. Luckily this has changed with the publication of the first Wicket book: &lt;a href="http://www.apress.com/book/bookDisplay.html?bID=10189"&gt;Pro Wicket&lt;/a&gt; by &lt;a href="http://jroller.com/page/karthikg"&gt;Karthik Gurumurhty&lt;/a&gt;.
&lt;p/&gt;
The book 'Pro Wicket' guides you through the development of a simple and sometimes not so simple web application. Page by page the application is extended and shows more and more Wicket features. Sometimes a 'mistake' is made. That mistake is then later 'corrected'. Unfortunately this makes the book less suitable as a reference book as you always have to flip through the chapter to see if you have the complete example.
&lt;p/&gt;
The contents of the book varies nicely. The basics and way of thinking in Wicket are explained with lots of examples. Also the internationalization features are thoroughly explained (although I find it disturbing that the example mixes up languages and countries). There are well set up explanations and examples on how to integrate popular technologies like Spring and Hibernate. The AJAX examples cover the basics (which are quite good in Wicket) but a lot is left to be discovered. Testing is even less well covered, but at least here some good pointers are given to the manuals. A nice touch is to take a brief preview of Wicket 2.0.
&lt;p/&gt;
The book is completely written in speaking language. The following sentence is typical: "As you might have guessed by now, this method returns false by default.". For some people, like me, this is a turn down, but most are probably okay. What is worse is that on every page you'll find pronouns (like 'it' and 'that') for which it is quite unclear what is referenced.
&lt;p/&gt;
The &lt;a href="http://en.wikipedia.org/wiki/Achilles'_heel"&gt;Achilles' heel&lt;/a&gt; of the book is in the end of chapter 2 where there is a totally incomprehensible explanation on how to use repeating page elements in Wicket. I find it a shame that such an important subject is tucked away at the end of a chapter on a different subject (validation), and that the reader is suddenly confronted with a page full of abstract terms and such a small example.
&lt;p/&gt;
I had a hard time reviewing this book. At many points I thought that the book was not finished enough to be published. Of course this was okay as I was reading the beta, but when the final book came out I noticed that only the code examples had been thoroughly updated. I am afraid that either the publisher pushed this one too hard, or the author just was out of time.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Conclusions&lt;/span&gt;&lt;br/&gt;
Despite some of the remarks above I am going to recommend this book anyway. First of all because it is the &lt;span style="font-style:italic;"&gt;only&lt;/span&gt; Wicket book on the market, secondly because it &lt;span style="font-style:italic;"&gt;does&lt;/span&gt; teach you Wicket. And that is a Good Thing.
&lt;p/&gt;
Thanks to Apress for providing the book for this review.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2006-10-05&lt;/span&gt;: Added link to Karthik Gurumurhty's blog, removed spelling mistake, deleted all comments related to the spelling mistake.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-8003699086156691078?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.apress.com/book/bookDisplay.html?bID=10189' title='Wicket getting serious, Pro Wicket - Book Review'/><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/8003699086156691078/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/09/wicket-getting-serious-pro-wicket-book.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8003699086156691078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/8003699086156691078'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/09/wicket-getting-serious-pro-wicket-book.html' title='Wicket getting serious, Pro Wicket - Book Review'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-2246420233689721835</id><published>2006-09-29T22:07:00.001+02:00</published><updated>2010-08-29T21:04:28.521+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='radiant'/><title type='text'>Back to Radiant</title><content type='html'>In one of my first posts I talked about Radiant. Having tons of other stuff to do, it dropped below my horizon. Because I am still on the mailing list, I saw a presentation passing by from Nathan Wright. The presentation is a beauty for first time Radiant users. I can highly recommend it if you like Ruby and are searching for something to build a simple site.
&lt;p/&gt;
Because I can't find a download, I've put it on my home server for now. So here it is:
&lt;p/&gt;
&lt;a href="http://omelet.zapto.org/radiant_cms.pdf"&gt;Radius, Content Management Simplified&lt;/a&gt; (5.6Mb)
&lt;br/&gt;Denver Derailed meeting September 27, 2006
&lt;br/&gt;Covers Radiant version 0.5.2
By &lt;a href="http://blog.brandalism.com/"&gt;Nathan Wright&lt;/a&gt;
&lt;p/&gt;
&lt;span style="font-style: italic;"&gt;Update 2006-10-02&lt;/span&gt;: Thanks to Luis Lavena for providing a more &lt;a href="#"&gt;&lt;span style="text-decoration: line-through;"&gt;solid server&lt;/span&gt;&lt;/a&gt;.
&lt;p/&gt;
&lt;span style="font-style: italic;"&gt;Update 2006-10-04&lt;/span&gt;: The link above is now pointing to Nathan's own blog.
&lt;p/&gt;
&lt;span style="font-style: italic;"&gt;Update 2007-03-06&lt;/span&gt;: All previous links are now dead. I placed the &lt;a href="http://omelet.zapto.org/radiant_cms.pdf"&gt;copy on my home server&lt;/a&gt; back.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-2246420233689721835?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://blog.brandalism.com/2006/10/4/radiant-presentation-at-derailed' title='Back to Radiant'/><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/2246420233689721835/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/09/back-to-radiant.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2246420233689721835'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/2246420233689721835'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/09/back-to-radiant.html' title='Back to Radiant'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-1881736513309725261</id><published>2006-09-28T13:43:00.001+02:00</published><updated>2010-08-29T21:04:52.858+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><title type='text'>Wicket for BSCs</title><content type='html'>Since I am finally getting to do a Wicket project (more then a year after I first learned about it), I subscribed again to the Wicket user mailing list. Initiated by an e-mail from Erik Brakkee, I started a thread to find out how to convince Big Slow Companies (BSC) to use Wicket and finally leave Struts in peaceful history.
&lt;p/&gt;
There was only one criterion for the items in the list: they must be one-liners that are irrefutable so that there is no place for any FUD.
&lt;p/&gt;
Together we came to the following points:
&lt;p/&gt;
- Wicket enables &lt;span style="font-style:italic;"&gt;scalable development&lt;/span&gt;. It is easy to split the work over many developers, it is even easy to split the work for HTML people and Java people.
&lt;p/&gt;
- Wicket provides a &lt;span style="font-style:italic;"&gt;natural programming paradigm&lt;/span&gt; familiar to Java and other OO developers. There is no need to learn new languages, Java and HTML are sufficient.
&lt;p/&gt;
- The Wicket user e-mail list provides &lt;span style="font-style:italic;"&gt;excellent support&lt;/span&gt;.
&lt;p/&gt;
- Wicket has &lt;span style="font-style:italic;"&gt;excellent feedback&lt;/span&gt; messages in case something goes wrong. Furthermore, Wicket is robust and shows no weird or unexpected behavior.
&lt;p/&gt;
- Wicket provides &lt;span style="font-style:italic;"&gt;excellent clustering&lt;/span&gt; support. When required, it is possible to optimize session usage.
&lt;p/&gt;
Although there was some strong support for including reusability and maintainability I did not do so. I have yet to find BSCs that deeply care about these issues.
&lt;p/&gt;
Thanks go to &lt;a href="http://chillenious.wordpress.com/"&gt;Eelco Hillenius&lt;/a&gt; for his infinite ability to promote Wicket. Thanks to Che Schneider for coming up with the TLA BSC.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-1881736513309725261?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/1881736513309725261/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/09/wicket-for-bscs.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1881736513309725261'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1881736513309725261'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/09/wicket-for-bscs.html' title='Wicket for BSCs'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-3270968507876980478</id><published>2006-09-22T16:05:00.001+02:00</published><updated>2010-08-29T21:05:36.114+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Java voor Excel Quiz</title><content type='html'>Here are some projects:
&lt;br/&gt;- JExcel
&lt;br/&gt;- Java Excel API (aka JExcelApi)
&lt;br/&gt;- JXL
&lt;br/&gt;- POI
&lt;p/&gt;
Which one does not belong in the list?
&lt;p/&gt;
I'll post the answer in a few days with some more information on each project.
&lt;p/&gt;
&lt;span style="font-style:italic;"&gt;Update 2006-09-26&lt;/span&gt;
&lt;p/&gt;
&lt;a href="http://www.jniwrapper.com/pages/jexcel/overview"&gt;JExcel&lt;/a&gt; is a commercial library for for integrating Microsoft Excel into Swing applications. It will actually  show an Excel sheet in your Java app (you do need Excel installed). From your Java code you can change the worksheets. Sounds neat, but I wonder who would want to use this. One thing I found is that the API seems to be a evolution of the following project: Java Excel API.
&lt;p/&gt;
&lt;a href="http://jexcelapi.sourceforge.net/"&gt;Java Excel API&lt;/a&gt; is an open source java library with which you can read, write, and modify Excel spreadsheets. I am currently using this one as it had some better blogs then POI. Although it does it works, I am not completely happy with it. The API is not consistent (for example: you can make something bold, but there is no obvious way to check whether a format is made bold) and the package structure and class names are kind of weird.
&lt;p/&gt;
&lt;a href="http://jxl.sourceforge.net/"&gt;JXL&lt;/a&gt; is an umbrella for a couple of small open source java library with which you can build distributed Java programs with distributed class loading. I did not look much further into this. It seems that it tries to do JavaSpaces like things.
&lt;p/&gt;
&lt;a href="http://jakarta.apache.org/poi/"&gt;POI&lt;/a&gt; is again an open source project with which you can read, write and modify OLE2 documents including Excel and Word files. I have never used this, so I can not compare it to JExcelApi.
&lt;p/&gt;
So the answer is: JXL.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-3270968507876980478?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/3270968507876980478/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/09/java-voor-excel-quiz.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3270968507876980478'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/3270968507876980478'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/09/java-voor-excel-quiz.html' title='Java voor Excel Quiz'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-5390219149711742155</id><published>2006-09-07T21:09:00.001+02:00</published><updated>2010-08-29T21:05:49.696+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wicket'/><category scheme='http://www.blogger.com/atom/ns#' term='portlets'/><title type='text'>More portlet woes</title><content type='html'>I've got my portlet running for some time (see previous post). I even have a weird hack to enable file downloads. The Spring portlet MVC framework helped me a lot to structure the portlet really nice.&lt;br/&gt;
But now a colleague has started to write portlets as well. And guess what, he is as frustrated as I was. To make things worse, even with my previous Pluto experiences, there is nothing I can do for him! It is all so complicated with nondescript exceptions and configurations all over the place.
&lt;p/&gt;
These 2 reasons (bad exceptions and configuration) are exactly the thing why Spring is such a joy to work with compared to using an old style EJB setup.
And, you can even do better. The web framework Wicket gives you no configuration at all! And it still gets better: no more JSPs! Wicket to conquer the world!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-5390219149711742155?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/5390219149711742155/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/09/more-portlet-woes.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5390219149711742155'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/5390219149711742155'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/09/more-portlet-woes.html' title='More portlet woes'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-1178788314539371733</id><published>2006-08-31T20:51:00.001+02:00</published><updated>2010-08-29T21:06:01.220+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Walking the Java Road</title><content type='html'>I am walking the Java road. This week I told a junior programmer that learning Java is not something you start and then complete. Its a road; you learn as long as you are doing it. I am on the Java Road for 5 years now and I am still learning every week, almost every day even.
&lt;p/&gt;
So what about this Ruby Road over there? It is a lot smaller road, but it appears that only fast vehicles are allowed. You really need to steer well, no mediocre developers here, they've crashed way behind you. And when you finally get it, boy, you get to go fast.
&lt;p/&gt;
The major problem with walking on 2 roads is that you only have so much time in your life. I need time for the ones I love and then I need some sleep now and then. I would not mind switching, but then there should be enough work. Unfortunately, all the interesting stuff is still in Java. So for now its Java for me. But I'll keep looking...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-1178788314539371733?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/1178788314539371733/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/08/walking-java-road.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1178788314539371733'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1178788314539371733'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/08/walking-java-road.html' title='Walking the Java Road'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-1580950537751069294</id><published>2006-08-24T23:53:00.001+02:00</published><updated>2010-08-29T21:06:50.750+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='conference'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><title type='text'>Profict Java Summer Camp - AJAX</title><content type='html'>I am just back from &lt;a href="http://www.profict.nl/"&gt;Profict&lt;/a&gt;'s &lt;a href="http://java.profict.nl/"&gt;AJAX day&lt;/a&gt;, a very nice way of promoting one's company among developers. There were 2 speakers: &lt;a href="http://weblogs.java.net/blog/gmurray71/"&gt;Greg Murray&lt;/a&gt;, the AJAX architect from Sun and &lt;a href="http://bram.jteam.nl/"&gt;Bram Smeets&lt;/a&gt;, the &lt;a href="http://getahead.ltd.uk/dwr/"&gt;DWR&lt;/a&gt; expert from Interface 21. In between there was a small product presentation from &lt;a href="http://www.backbase.com/"&gt;BackBase&lt;/a&gt;.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;4 hours for Greg Murray&lt;/span&gt;&lt;br/&gt;
Four hours were filled by a jetlaged Greg. It seems that because he got so much time he could not chose what to tell from the many presentations he brought with him. It was a shame everything was so jumbled up because there were some really nice things in there. Without going into too much detail, here are some of the things he presented.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;jMaki&lt;/span&gt;&lt;br/&gt;
Greg is the main author for jMaki. jMaki is a small toolkit that allows you to wrap other Javascript toolkits (among others Dojo and Zimbra). jMaki comes standard with some nice widgets. Besides the usual datepickers and autocomplete stuff, there were some very nifty widgets that allow you write Mashups with Flickr, Yahoo Geocoder and Google Maps. A Mashup is a mashed combination of several web applications. One of his examples was a reworked Petstore where each pet was indicated on a map.
jMaki is available for JSP and JSF environments. But given the raw components (simply a html, css and js file) you can probably include this stuff in any other framework easily.
&lt;p/&gt;
jMaki also has some other basic stuff. For example an eventing mechanism (modeled after Dojo) and XmlHttpProxy.
XmlHttpProxy is an XmlHttpRequest like feature that will allow you to call services in any domain by proxying the call on a server. This circumvents the security mechanism of Javascript in which you can only do HTTP requests to the domain from which the page was served.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;AJAX, Back button and Anchor links&lt;/span&gt;&lt;br/&gt;
The trick of the day was the use of anchor links (the part after the #). When the user triggers an AJAX action (for example loading a different product), the URL of the currently loaded document is changed, but only in the anchor part. Since only the anchor changes, the browser will not reload the current page, but will add an entry to the page history. This means that suddenly the AJAX enabled page has a bookmarkable URL. In addition, much better back button support becomes possible.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Bram Smeets on DWR&lt;/span&gt;&lt;br/&gt;
The 1 and half hour presentation from Bram was a very nicely done introduction to DWR. In contrast to many other Javascript toolkits, DWR only provides communication between JavaScript and Java. However, as this presentation showed, DWR does so very well. In short, the idea behind DWR is that you can call a Java method directly from Javascript, and then can use the returned objects as if they are plain Javascript objects. If so desired, some utility Javascript functions can easily convert these objects to HTML and put them into the current page.
&lt;p/&gt;
First an example was presented for DWR with plain Java and XML configuration. The few lines of Java and Javascript code on the slides showed that DWR is really a convenient thing to use. One of the killer features is a debug view of all exposed classes and methods.
After a pitch style introduction of Spring, Bram then showed how to configure DWR from Spring. Though the integration is indeed nice, it gives you no advantages when you are not using the Spring bean container for some other reason (which of course there are plenty).
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Reverse AJAX&lt;/span&gt;&lt;br/&gt;
The audience really heated up with the topic of Reverse AJAX (a.k.a. Comet, Long poll and for some even as Pushlets). This experimental DWR feature allows you to execute Javascript from Java. A very very neat example showed a method call on a Java-Scriptaculous 'impersonator' on the server side, with as result that the Scriptaculous effect is applied in the browser. Unfortunately Reverse AJAX is still at an experimental stage. As Bram told me later, this is mainly because it has not been tested properly in many circumstances. In addition, you must be very aware of the load this brings to your server.
&lt;p/&gt;
&lt;span style="font-weight:bold;"&gt;Barbecue&lt;/span&gt;&lt;br/&gt;
The day ended with a nice barbecue in the beautiful garden of &lt;a href="http://static.flickr.com/73/153841303_68ef30d817_m.jpg"&gt;Profict's castle&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-1580950537751069294?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/1580950537751069294/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/08/profict-java-summer-camp-ajax.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1580950537751069294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/1580950537751069294'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/08/profict-java-summer-camp-ajax.html' title='Profict Java Summer Camp - AJAX'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-115494108129632109</id><published>2006-08-07T09:28:00.001+02:00</published><updated>2010-08-29T21:07:46.860+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><title type='text'>Swing Date Pickers</title><content type='html'>Back in April I looked around for a good open source Swing date picker for a big Swing project. I found 6 and I actually tried them all. But in the end we simply stayed with JSpinner components.
&lt;p/&gt;
These were the must-have requirements:
&lt;br/&gt;1. support for typing dates directly (this is, without opening a pop-up)
&lt;br/&gt;2. ability to limit the selectable dates with a lower and/or upper boundary
&lt;br/&gt;3. good event mechanism
&lt;p/&gt;
And some nice-to have requirements:
&lt;br/&gt;4. visual mouse selection
&lt;br/&gt;5. support for multiple date formats
&lt;br/&gt;6. look good
&lt;br/&gt;7. further limitation of selectable days, for example no weekend days
&lt;p/&gt;
These were the date pickers I tried:
&lt;br/&gt;- &lt;a href="http://www.jfree.org/jcommon/index.php"&gt;JCommon&lt;/a&gt;
&lt;br/&gt;- &lt;a href="http://web.ukonline.co.uk/mseries/MDateEntryField.html"&gt;MDateSelector&lt;/a&gt;
&lt;br/&gt;- &lt;a href="https://swingx.dev.java.net/"&gt;SwingX&lt;/a&gt;'s date picker
&lt;br/&gt;- &lt;a href="http://uic.sourceforge.net/"&gt;UICollection&lt;/a&gt;'s date picker
&lt;br/&gt;- &lt;a href="http://www.toedter.com/en/jcalendar/"&gt;JCalendar&lt;/a&gt;
&lt;br/&gt;- And there was a sixth, but I can not find it back anymore.
&lt;p/&gt;
None of these met the must-haves. In particular requirement #1, keyboard support, is only present in the the SwingX component. In contrast good old JSpinner supports #1-#3 splendidly, but fails miserably on #4-#7.
&lt;p/&gt;
However, my search did seem to have some result.
&lt;p/&gt;
It started with comment on an &lt;a href="http://www.javalobby.org/forums/thread.jspa?messageID=92011216"&gt;introduction on the SwingX JXDatePicker&lt;/a&gt; by R.J. Lorimer on &lt;a href="http://www.javalobby.org/"&gt;JavaLobby&lt;/a&gt;. This guy writes good stuff.
Then I thought, why not ask on the SwingX forum directly. So I wrote &lt;a href="http://forums.java.net/jive/thread.jspa?threadID=14774&amp;start=0&amp;tstart=0#105164"&gt;this message&lt;/a&gt; in search of more features. The message was followed by a lively discussion in search of requirements.
&lt;p/&gt;
It seems that the SwingX guys picked it up and are now working on the final killer Swing date picker component!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-115494108129632109?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/115494108129632109/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/08/swing-date-pickers.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/115494108129632109'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/115494108129632109'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/08/swing-date-pickers.html' title='Swing Date Pickers'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27876765.post-115486278604778990</id><published>2006-08-06T12:54:00.001+02:00</published><updated>2010-08-29T21:08:24.942+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='book review'/><title type='text'>The Pragmatic Programmer - Book review</title><content type='html'>A colleague from the company that currently hires me, proudly told that they have a copy of the &lt;a href="http://www.pragmaticprogrammer.com/ppbook/index.shtml"&gt;The Pragmatic Programmer&lt;/a&gt; for every 3 employees. Having just finished the Spring MVC book, I thought lets see what this is about. So I read the book and here I will convince you to read it as well.
&lt;p/&gt;
After only a few chapters 2 strong thoughts came up and remained there for most of the rest:
&lt;br/&gt;1. What a dull book, I am already doing this stuff. But more importantly:
&lt;br/&gt;2. This is Good Stuff, I wish more people would read this book.
&lt;p/&gt;
Though the book is old (2000) it is still very relevant. It contains a large set of well described programming practices and attitudes. You can read the entire book in any order. From experience I can say that what this book says, will really help you build better code more quickly.
&lt;p/&gt;
One point I loved, is when the authors write off the construction metaphor (which is so previous century) in favor of the gardening metaphor. 'Gardening' is much better for explaining to business people why you sometimes need to spend time with no visible improvements.
&lt;p/&gt;
I wondered why I had never seen &lt;a href="http://icontract2.org/"&gt;iContract&lt;/a&gt; before, which is I think a hidden gem of the book. &lt;a href="http://www.toolshed.com/blog/articles/2006/06/30/icontract-lost-now-found"&gt;Apparently&lt;/a&gt; it is only recently resurrected.
&lt;p/&gt;
As a totally unrelated bonus, I finally understand the use of Linda like technologies (&lt;a href="http://segment7.net/projects/ruby/drb/rinda/ringserver.html"&gt;Rinda&lt;/a&gt;/&lt;a href="http://www.dancres.org/cottage/javaspaces.html"&gt;JavaSpaces&lt;/a&gt;).
&lt;p/&gt;
Some minor points:
&lt;br/&gt;- I only noticed one trend away from this book: things like Active Record makes the section "Code generators" a bit redundant. Of course it can still be useful. Just watch out for software in which you don't need it.
&lt;br/&gt;- Exercise 23 states that it is a good idea to set java references to null after you are finished with the object. I disagree. You can &lt;span style="font-style:italic;"&gt;consider&lt;/span&gt; doing this when you are writing code for a particular (old) JVM version that you happen to know intimately. And even then I would say that your methods are too long.
&lt;p/&gt;
Anyways, the book does justice to its title; all tips are practical and executable. The more people will use ideas from this book, the less bad software I will have to work with. So here is my request to all colleague programmers: please &lt;span style="font-weight:bold;"&gt;&lt;a href="http://www.pragmaticprogrammer.com/ppbook/index.shtml"&gt;go and read this book&lt;/a&gt;!&lt;/span&gt;
&lt;p/&gt;
Author blogs: &lt;a href="http://www.toolshed.com/blog/"&gt;Andrew Hunt&lt;/a&gt; and &lt;a href="http://blogs.pragprog.com/cgi-bin/pragdave.cgi"&gt;David Thomas&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27876765-115486278604778990?l=day-to-day-stuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://day-to-day-stuff.blogspot.com/feeds/115486278604778990/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/08/pragmatic-programmer-book-review.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/115486278604778990'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27876765/posts/default/115486278604778990'/><link rel='alternate' type='text/html' href='http://day-to-day-stuff.blogspot.com/2006/08/pragmatic-programmer-book-review.html' title='The Pragmatic Programmer - Book review'/><author><name>Erik van Oosten</name><uri>http://www.blogger.com/profile/15976519439979651010</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
