Tuesday, July 13, 2010

Eclipse RCP product export and ${target.os}

It's a fine day today. My small little Eclipse-RCP app is looking all fine and healthy.

I open my plugin.xml and click on the round, green "Launch an Eclipse application" icon and it works great.
Sometimes I use Run->Run History->and pick the latest one, and again, it works flawlessly.

Cool. Now let's package it up in an EXE.

Here's the problem.

You'd think it's just a matter of creating a product configuration file, right? Just right click on the plugin project, choose New->Product Configuration. Then select "Use a launch configuration" and point it to our trusted, tested, working launch configuration.

Do it, and it won't work. I tried it with Eclipse 3.5 (Galileo) as well as Helios, and it just doesn't work.

You application refuses to start. You look at the logs in the "configuration/" folder and you see cryptic stuff like this:

org.osgi.framework.BundleException: The activator org.eclipse.ui.internal.WorkbenchPlugin for bundle org.eclipse.ui.workbench is invalid


java.lang.NoClassDefFoundError: org.eclipse.swt.SWTError


Well, the SWT jars are there. The Windows-specific ones are there too. I *have* done a Synchronize before export, I did choose to include all dependencies, including the optional ones.

The problem, as it happens to be, is a tiny little thing called the "Program Arguments".
This is what you get by default from the new Product Configuration wizard:



And this says that the product is to be launched on an OS named "${target.os}". I have heard of Windows XP, Linux, AIX, Solaris, but never something claled ${target.os}.

Of course, the launch configuration has the same string and it works fine.. but it gets replaced by the actual value of the OS when it runs. But the poor product configuration doesn't know that and treats it verbatim.

I'm running on Windows XP, not ${target.os}. So it doesn't recognize the target platform. It doesn't recognize the platform-specific SWT JARs, and it fails.

I tired for a minute or so to figure out what the right values for these variables were for Windows XP / J2SE 5.0, but I couldn't get them. Deleting them (blanking out the Program Arguments box completely) worked for me, though. So I won't care about it until I come across a problem with it again.

Sunday, July 11, 2010

What's wrong with the JSF 2 ui:repeat

The single little thing that's been able to hold my attention repeatedly over the past few weeks in JSF 2 has been the "ui:repeat" tag.

To begin with, it's quite simple. It's so very simple that it's authors probably find it too trivial to explain what it does. This is all you get in its tag library documentation:



Ok. So it's an alternative to c:forEach, or h:dataTable, right? Maybe they just created this tag for some political reasons(if there're two more tags in the same spec and the same impl for the same purpose, you do get the doubt), but that's fine with me as long as it works.

"As long as it works".. hmm... yes.. and that's the tricky part. It seems to be that there are a number of issues with ui:repeat. Most importantly, when you have nested ui:repeats, things start behaving real crazy.

OK. So what if it has bugs? It's an alternative to c:forEach and h:dataTable, right? Let's use c:forEach instead.

And so, when you start using c:forEach, you realize that it's a totally different tag, with a totally different purpose.

Ok. So ditch that c:forEach. Let's see what the bugs with ui:repeat and apply patches ourself.

The first thing - ui:repeat doesn't recognize model updates.
The fix is quite simple. In the process method of UIRepeat, just add a simple check (as suggested by the proposed patch on the bug report):

if (PhaseId.RENDER_RESPONSE.equals(phase) && !hasErrorMessages(faces)) {
if(isNestedInIterator()){
this.childState = null;
}
}


And the second thing - ui:repeat botches up input fields - checkboxes, textboxes and all sorts of EditableValueHolders in general. That has also been reported.
The fix is pretty simple, though if you are like me, new to JSF, and stuck with it, with no other way out, you'd need to hit your head against the code for anywhere between three to five days before you figure it out.

In the populate method of the SavedState inner class, just change this one line:
this.value = evh.getValue();
to
this.value = evh.getLocalValue();




The root cause of this second bug is an API design flaw -
The JSF ValueHolder interface has two methods getValue and setValue, but interestingly, they are not complementary. That is, if you use the setValue method and push in some value, you may not get that same value out of the getValue.
Why? Because that's how it has been designed.

Defies conventions and common-sense expectations? Certainly does. Bad API? Definitely.

The method ought to be named setLocalValue, not setValue.

I wonder how these seemingly simple bugs in code and documentation seem to be around for years in such a high quality, high visibility software library.