Friday, October 23, 2009

Page links with Wicket

The default urls created by Wicket do not look very nice. For instance, let IndexPage be the home page of our ShopApplication. Assuming shop is your application's context name and wicket the path where you mapped the Wicket filter, then by default, the url for the IndexPage will be akin to
http://somehost/shop/wicket/index?wicket:interface=:0:headerPanel:index2::ILinkListener::. I said ugly didn't I?

Wicket has a mechanism for prettifying URLs. In ShopApplication, you can instruct wicket to "mount" a page on a specific path. Here is the code.
public class ShopApplication extends WebApplication {
@Override
public Class getHomePage() {
return IndexPage.class;
}
@Override
protected void init() {
mountBookmarkablePage("index", IndexPage.class);
}
}
Wow, that was easy!

Now, assume you have a navigation panel, included on all the pages of your application, where you list links to the various pages of your application. Here is the markup:
<table cellpadding="0" cellspacing="0">
<tr align="center">
<td><a class="menuitem" href="index">Index</a></td>
... other pages
</tr>
</table>
We are referencing the mounted path "index" directly. How convenient is that? Although, convenient such references do not work correctly. The navigator will translate it into http://somehost/shop/index instead of the correct http://somehost/shop/wicket/index.

For bookmarkable links such as "index", Wicket offers the <wicket:link> tag instructing Wicket to to automatically create bookmarkable
link components for the links inside the tags. So let's try that.
<tr align="center">
<wicket:link>
<td><a class="menuitem" href="index">Index</a></td>
... other pages
</wicket:link>
</tr>
The above is easy enough and will work, as long as all your pages are in the same java package.

Indeed, the <wicket:link> tag doctors url references with respect to the java page of the current page belongs to, but if the page is located in a different package, then the urls will be rendered incorrectly. Bummer.

So, we seem to be stuck. Googling for "wicket url mountBookmarkablePage" you will find an article by R.J. Lorimer on controlling Wicket URLs which did not go far in reducing my level of confusion. After further investigation, I found an example which caters for the use case we've been discussing so far, that is the nice-url example.

The nice-url example, after mounting the Home page as "/the/homepage/path", simply adds a BookmarkablePageLink to the Home class on another page, namely Page1. Here is the relavant code.
<a wicket:id="homeLink" href="#">[go back]</a>

public class Page1 extends WicketExamplePage {
public Page1(PageParameters parameters) {
add(new BookmarkablePageLink("homeLink", Home.class));
}
}
which magically works, in the sense that when the link for the url is rendered, it will somehow contain the mounted path "/the/homepage/path". How does BookmarkablePageLink know that Home is mounted on "/the/homepage/path"?

It does not know. Instead, it invokes the
urlFor(IPageMap pageMap, Class pageClass, PageParameters parameters)
method defined in its parent class, Component. This method will in turn ask the request cycle the page is associated with to find the correct url. The request cycle will eventually go through all mount points, asking them whether they apply to Home.class. The first mount point which applies will then be asked to encode the Home page, returning the mounted path. The exact details might vary, but from a 100 feet point of view, wicket does reverse lookup to see which mount point maps to a given page which goes to explain the aforementioned magic.

Friday, October 16, 2009

IDEA open sourced

Coming across Cedric Beust's comments on IDEA going open-source, I thought I'd chime in. Cedric makes valid points, in particular about the unfulfilled expectations after open sourcing a product. As he points out, Jetbrains is unlikely to be flooded with patches and additions from the community, floating them back to the top.

Cedric also correctly identifies that open-sourcing software is an attempt to regain mind-share. He unfortunately qualifies the effort as "last-ditch" which is where I disagree. Companies have to make strategic decisions all the time. Companies live and die by strategic decisions. Who would have guessed 10 years ago that IBM would have survived its transition to services as decided by Louis V. Gerstner? Instead of "last-ditch", in my humble opinion "just-in-time" would be a better qualification for Jetbrains' recent decision.

According to scientific surveys conducted around a glass of beer, people who use IDEA on a daily basis absolutely love it. I am not one of them. But when a company enjoys such a dedicated following, it seems premature to predict its imminent doom.

As Cedric put it, I wish the best to IDEA. I really do.

Wednesday, September 02, 2009

How the US is burning the furniture without even realizing it

The US budget deficit has reached astronomical proportions. Admittedly, the same holds true in European countries. However, European economies attract less of the world's savings than the US does.

According to various sources, in 1945 only 10% of the US government's spending was deemed incompressible, in 2009 approx. 80% of government spending is qualified as such. Given that it is politically unacceptable to raise taxes, and given that by definition incompressible spending cannot be reduced, the US government needs to find alternative ways of reducing its deficit in order to keep the ballooning budget deficit under control. If not, the whole financial system is menacing to crumble. One politically cheap way to raise money is to go after tax evasion.

In the latest revision of the Greenbook, the Obama administration proposes new measures to track down and eliminate tax evasion. To keep it short, various financial institutions referred to as "Qualified Intermediaries", even if they are based outside the US, if they serve US persons or deal in US titles (shares and derivatives), are now required to abide by strict and cumbersome rules. For a bank and its clients based outside the US, it is increasingly impractical to deal in US titles or the US banking system in general.

In addition to the Greenbook, the Patriot Act makes it unpleasant to have a banking account in the US. For example, when a person living, say in Denmark, wants to transfer funds from her US account to an account in her name at her home town in the Denmark, she has to justify the transfer with a signed letter (email won't do). From the US view point this is justified by the need to combat terrorism. However, from the view point of that Danish person living in the Denmark, the administrative overhead is cumbersome at best, and plain ridiculous at worst.

For a country which in the past managed to attract 85% of world-wide savings, the Patriot Act combined with latest steps described in the Greenbook, carefully engineered to stop tax evasion under the "no escape" principle, instead of raising more money for the government, are likely to precipitate America's continued impoverishment.

The irony of it all is that various governmental actors are all acting rationally in the best interest of the US but the end result might well be a new world order with the US playing a lesser role.

Saturday, August 15, 2009

GIT migration

A few years ago, I watched Linus Torwald's presentation on git. I was so thoroughly put off by his style that I did not give git another thought for several years, that is until recently. Hearing more and more about git, I recently raised the question of migrating the logback project to git on our mailing list and the ensuing discussion had me convinced.

Here is a recount of the steps I've taken to migrate logback to git from SVN.

First, I installed git on our Linux servers. Nothing newsworthy there. Gitosis' installation on those servers was equally uneventful.

Installing git on Windows was more of an ordeal. Supposedly, on Windows git is officially supported with cygwin, which I've been using daily for over 10 years. Git just did not work me with cygwin. Migrating to cygwin 1.7-beta was not much of a help. Fortunately, the msysgit project provides a solution for Windows.

The next step was to create a git repository from our existing SVN repo which was as easy as invoking "git svn --stdlayout --no-metadata -A authors.txt clone http://svn.qos.ch/repos/logback" where authors.txt is a file mapping login names to user names. The --no-metadata flag tells GIT to drop noisy references to the SVN url and other useless details.

My next step was to allow anonymous reading of the git repository, which as easy as starting git-daemon which was part of the core git package (installed by gentoo).

Git makes it very easy to fork projects. On github, you can track work done on forks, which was one of the arguments that convinced me to switch to git hub. However, pushing to github is slower than pushing to git.qos.ch, our dedicated git server. The idea is to push to git.qos.ch on a regular basis and to githib with lower frequency (say once a week). To add a remote server, the command is "git remote add github git@github.com:ceki/logback.git". Once github is added as a remote server, pushing to it is as easy as invoking "git push github master".

Now that the essential pieces are in place, the next step was to better understand git's philosophy by reading the Pro Git book.

After much of the initial migration was done, Jukka Zitting observed that the branches and tags which existed in the SVN repository were missing. Since I had removed the original git repo which was created from SVN, I had lost the branch and tag information. I had to re-import from SVN. Since the initial import contained metadata which I no longer wanted, my newly imported repository was incompatible with the earlier git repository containing new work but missing the tagging information. I did not want to waste half-a-day's work. I saved the new work with the ' git format-patch -o ~/patches/ 088f.." command, where 088f.. stands for the SHA-1 of my earliest commit since the SVN migration. Such information is very easy to obtain from the git.qos.ch and github servers.

The next step was to create the tags from the latest git repository. Since tags can be created a posteriori, all you need is the SHA-1 of the snapshot you want to tag. The "git for-each-ref refs/remotes/tags" does give you exactly that. Here is sample output:
a8f066010596dc.. commit refs/remotes/tags/SLF4J_1.3.0
260b5f6f9d0f14.. commit refs/remotes/tags/SLF4J_1.3.1
d8eadb09a11dbd.. commit refs/remotes/tags/SLF4J_1.4.0
7011d1f3665e88.. commit refs/remotes/tags/SLF4J_1.4.1
5fd428e3dd03ed.. commit refs/remotes/tags/SLF4J_1.4.2
04a8c2c2cf4230.. commit refs/remotes/tags/SLF4J_1.4.3


Which can be easily massaged into:
git tag -a SLF4J_1.3.0 a8f066010596dc -m "SLF4J_1.3.0"
...
...

Once the tags are created locally, they can be pushed remotely with "git push origin --tags"

Tuesday, July 14, 2009

The quest for an AC Adapter for my Dell laptop (Part II)

The UPS delivery person left an InfoNotice(SM) at our door step on Friday the 10th July. The InfoNotice contained a tracking number which could be used at the UPS web-site to change the parameters of delivery. One of the proposed options was to return the package to the sender, which I quickly selected. By returning the erroneous package, Dell would get their merchandise back, proceed to reimburse me and the matter would be settled.

I was quite surprised to find a second InfoNotice in my mailbox on Monday the 13th. I called up UPS to learn why my cancellation request had no impact. The UPS customer service representative explained that UPS had a special deal with Dell. Only Dell could cancel a delivery; the recipient, the end-customer in this case, was not authorized to cancel a delivery. I called Dell to ask them to cancel but was told that canceling policy was an internal UPS matter. When I explained that UPS had told me to call them (Dell) because they were the only party which was able to act on the matter, the answer was invariably: "it is an internal UPS matter, call UPS".

So I called UPS again to explain the situation. The UPS representative told me that Dell was purposefully denying their capability to resolve the matter because they did not wish to cancel the order.

Since I am at the office during the day, I asked UPS if I could change the delivery address to my office, which is one of the options explicitly mentioned on the InfoNotice. The UPS representative told me that only Dell could change the final delivery address.

To be continued...

Monday, July 06, 2009

The quest for an AC Adapter for my Dell laptop (Part I)

The AC adapter for my laptop from Dell recently had its cable severely twisted so as to stop charging the laptop. The adapter is marked as belonging to the PA-9 family. I called Dell technical support and explained the problem to a technician. I gave him the serial number of my laptop which identified both my laptop and the buyer (me). He said that they could ship a replacement adapter forthwith. I asked the technician several times to confirm that item he was sending me was a PA-9. He said that it was indeed, and that I had nothing to worry about.

A few hours later, I received a written confirmation for my order. Lo and behold, the order was for a Kensington 450-11303 an adapter for the cigarette lighter of a car. I immediately wrote back to cancel the order. When Dell support wrote back, they claimed that the order had already shipped and could not be canceled. Moreover, Dell was not willing to take any further action, such as recognizing their error, before the shipment arrived. I am afraid that their strategy for dealing with this issue will be to drag it on until I give up. Anyway, time will tell.

Labels: ,

Friday, June 05, 2009

Biased Locking in Java SE 6.0

As a result of the ensuing discussion in response to this blog entry, it is now pretty clear that the issue described herein is not specific to Java SE 6. Please do not reach any conclusions without reading the entire post, including the comments, and especially the comments.

Joern Huxhorn recently informed logback developers that locking in Java SE 6.0 was unfair. Indeed, Java documentation regularly mentions that one should not make assumptions about Java's thread scheduler. For example, the last paragraph of Thread Priority on the Solaris™ Platform, states:
Likewise, no assumptions should be made about the order in which threads are granted ownership of a monitor or the order in which threads wake in response to the notify or notifyAll method. An excellent reference for these topics is Chapter 9, "Threads," in Joshua Bloch's book "Effective Java Programming Language Guide."
If you actually bother to read the book, in Item 51, Joshua Bloch writes:
When multiple threads are runnable, the thread scheduler determines which threads get run and for how long. Any reasonable JVM implementation will attempt some sort of fairness when making this determination.
While Joshua Bloch's statement above is specifically about the JVM scheduler, I think that the notion of "some sort of fairness" applies to locks as well. The general principle is the same.

Here is a small java application called LockingInJava which illustrates the locking behavior in question.

public class LockingInJava implements Runnable {

static int THREAD_COUNT = 5;
static Object LOCK = new Object();
static Runnable[] RUNNABLE_ARRAY = new Runnable[THREAD_COUNT];
static Thread[] THREAD_ARRAY = new Thread[THREAD_COUNT];

private int counter = 0;

public static void main(String args[]) throws InterruptedException {
printEnvironmentInfo();
execute();
printResults();
}

public static void printEnvironmentInfo() {
System.out.println("java.runtime.version = "
+ System.getProperty("java.runtime.version"));
System.out.println("java.vendor = "
+ System.getProperty("java.vendor"));
System.out.println("java.version = "
+ System.getProperty("java.version"));
System.out.println("os.name = "
+ System.getProperty("os.name"));
System.out.println("os.version = "
+ System.getProperty("os.version"));
}

public static void execute() throws InterruptedException {
for (int i = 0; i < THREAD_COUNT; i++) {
RUNNABLE_ARRAY[i] = new LockingInJava();
THREAD_ARRAY[i] = new Thread(RUNNABLE_ARRAY[i]);
}
for (Thread t : THREAD_ARRAY) {
t.start();
}
// let the threads run for a while
Thread.sleep(10000);

for (int i = THREAD_COUNT - 1; i <= 0; i--) {
THREAD_ARRAY[i].interrupt();
}
Thread.sleep(100); // wait a moment for termination, to lazy for join ;)
}

public static void printResults() {
for (int i = 0; i < RUNNABLE_ARRAY.length; i++) {
System.out.println("runnable[" + i + "]: " + RUNNABLE_ARRAY[i]);
}
}

public void run() {
for (;;) {
synchronized (LOCK) {
counter++;
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
break;
}
}
}
}

public String toString() {
return "counter=" + counter;
}
}
When run under Sun's JDK version 1.6.0_11 on a 64bit Dual Core AMD Opteron running Linux, here is what gets printed on the console.
java.runtime.version = 1.6.0_11-b03
java.vendor = Sun Microsystems Inc.
java.version = 1.6.0_11
os.name = Linux
os.version = 2.6.25-gentoo-r6
runnable[0]: counter=1002
runnable[1]: counter=0
runnable[2]: counter=0
runnable[3]: counter=0
runnable[4]: counter=0
Notice how only the first thread gets any work done. All the other threads are completely starved while waiting for the lock. The same application run with JDK 1.5 or older will have much more uniform results. Some threads will get more often access to the lock than others, but all threads will get some access. With JDK 1.6, access to locks is dispensed in a biased fashion. In other words, the thread currently holding the lock will always be favored compared to other competing threads. Sun calls it biased locking. It purportedly makes better use of CPU capabilities.

As for the argument about no guarantees offered about fairness, while true in letter, it is invalid in spirit. The argument is too punctilious and formalistic. Any developer reading the documentation will naturally assume that while there are no guarantees, the synchronization mechanism will not actively impede fairness.

Biased locking, as introduced in JDK 1.6, will affect thousands of unsuspecting applications. The fact that the bias is intentional does not make it less damaging. It's still a bug.