Wednesday, October 29, 2008

Describe 'what it does', Not 'when you should use it'

Question:
Which one of these two methods will always create a new form and which will always re-use an existing form?

setupForm(RequestContext context)
resetForm(RequestContext context)

Answer:
The opposite to what the method names suggest.

Rant:
Spring is the culprit here - FormAction's setupForm() and resetForm() are badly named in Spring Web Flow.

setupForm() will re-use an existing form if available and resetForm() will discard an existing form and create a new one.

Instead of being named according to what the method does, the methods have been named according to when you should use them.

The distinction is subtle -but Spring should not have assumed it knows when and how I want to call those methods. And apart from that, the naming gives a misleading indication of what it actually does with a form backing object.

Granted, the API comprehensively explains what these methods do (it was updated after some prodding), but the explanation given has to be comprehensive because of the misleading nature of the naming.

Thankfully this is not as much of an issue in Web Flow 2.0. The naming has not been fixed, but you no longer need to call setupForm() at all in your web flow definition since Spring handles creation of FormBackingObjects behind the scenes.

Friday, October 24, 2008

Evaluating Boolean in JSTL sometimes doesn't work

There are a couple of tricks with JSTL when trying to evaluate a Boolean from your model bean.

Trick 1

Don't do this:

<c:when test="${item.locked == 'true'}">

instead, do this:

<c:when test="${item.locked == true}">

or even simpler, this:

<c:when test="${item.locked}">


Trick 2

In your bean, use a primitive boolean, not a java.lang.Boolean.

These all work with JSTL:
boolean isLocked() { return true; }
boolean getLocked() { return true; }
Boolean getLocked() { return Boolean.TRUE; }

But this doesn't:
Boolean isLocked() { return Boolean.TRUE; }

So to avoid any hair-pulling problems, always use primitive booleans if possible.

Wednesday, October 15, 2008

Spring Errors Object: Editing the model during validation

I had a problem where after getting a validation error on a field, I wanted to reset the field to its previous value before displaying the validation errors in the view.

It didn't work! For some reason, the field value displayed in the view was NOT what I set it to in the model.


According to Spring's documentation for org.springframework.validation.Errors:
rejectValue(String field, String errorCode) Register a field error for the specified field of the current object using the given error description.


I always thought that Spring's Errors (more specifically BindException) object somehow used the underlying form object model fields. I guess the fact that you give rejectValue() a field name led me down that path, it must reference the model at some point. So I thought that the data displayed in the view for invalidated fields would come from the model, with the related error messages being provided by the Errors object.

Apparently I was wrong.

It seems that when you register a validation error on a field using Errors, Errors takes a COPY of the data from the model, and that is what gets displayed in your view. So any subsequent changes you make to the model (during that request) will be ignored by the view, and won't be displayed.

I guess this makes sense if the Validation error was a binding failure (like a type mismatch); that way you will always have access to what the user actually entered. But if it is just a validation failure - why store a copy of the object? I guess it comes down to the nature of Errors - it is designed to keep a record of "what was entered" and "what is wrong with it". I never really appreciated that before.

Tuesday, October 14, 2008

Spring Webflow Action-State confusion

Question: Why doesn't setupReferenceData() get called in this block of code?

<action-state id="setupForm">
<action bean="myAction" method="setupForm"/>
<action bean="myAction" method="setupReferenceData"/>
<transition to="displayForm"/>
</action-state>


The answer has to do with the nature of action-state. According to Spring - it actually models the Chain of Responsibility (CoR) pattern. Don't go to sleep yet! I'm not going any further than that into patterns.

Basically it means that "action-state" doesn't execute each action sequentially, like you are used to doing with "start-actions", "entry-actions" and "render-actions". It is quite common to see multiple actions defined and executed sequentially like this:


<render-actions>
<action bean="basicInfoModel" method="setupForm"/>
<action bean="structureBasicInfoModel" method="setupForm"/>
<action bean="structureSelectionModel" method="setupForm"/>
<action bean="addressModel" method="setupForm"/>
</render-actions>


But don't forget - you can't do this sort of thing inside an "action-state". If more than one action is configured they are executed in an ordered chain until one returns a result event that matches a state transition out of the state. In our initial example where there is no "on" attribute defined for the transition, it will match any event and thus the second action is never executed.

So here is what you should do instead. Just give your actions a name, and check for a success event on the second named action.

<action-state id="setupForm">
<action name="setup" bean="myAction" method="setupForm"/>
<action name="referenceData" bean="myAction" method="setupReferenceData"/>
<transition on="referenceData.success" to="displayForm"/>
</action-state>

They have a very liberal definition of "surrounding areas"


This ad is on the side of a bus shelter. Photo taken today in Brisbane, QLD.

Friday, October 10, 2008

Error deploying to WebLogic

Weblogic has a cache that doesn't get cleared when you delete the deployment from the Weblogic Console. This can cause an error like the one below when jars in the project change:

BUILD FAILED
The following error occurred while executing this line:
weblogic.Deployer$DeployerException: weblogic.deploy.api.tools.deployer.DeployerException: Task 5 failed: [Deployer:149026]deploy application projectName on AdminServer

You need to delete Weblogic's cache which is located here:

C:\bea\user_projects\domains\projectDomain\servers\AdminServer\tmp_WL_user

Oh, and make sure you clean everything too before you deploy.

Thursday, October 9, 2008

Problem starting Weblogic in Debug Mode from IntelliJ

When I tried to run Weblogic in debug mode from Intellij using JDK 1.5.0_09, I got a cryptic error like this:
JDWP unable to get necessary JVMTI capabilities

The cause of this is that IntelliJ is appending its own environment variables to the environment variables from the JVM, which is causing duplication of the variable -Xrunjdwp:transport=dt_socket . This duplication causes the error message above. The environment variables in IntelliJ can't seem to be edited (although you can tell it not to pass any variables, this caused me other problems when trying to debug remotely).

What I did to solve this was edit the file C:\bea\user_projects\domains\projectName\bin\startWebLogic.cmd and add the line below to somewhere near the top of the file:

set JAVA_VM=

This will allow IntelliJ to control what environment variables are used, and will remove the double up.

Tuesday, October 7, 2008

Wearing out the W-key (Part 2)

I was half joking about re-mapping Ctrl-W, but it got me thinking.

The happy hacking keyboard moves the Ctrl key from it's regular position to where the Caps Lock key normally is (and incidentally does away with the Caps Lock key completely).

So I thought it might be worth re-mapping the Caps Lock key to act like a third Ctrl key. And so far I like it!

It took a few days to force myself to use it - but I find it much easier on my wrists for common key combinations like Ctrl-W, Ctrl-C and Ctrl-V (try it now - press CapsLock-C). Common Triple key combinations (for Eclipse and IntelliJ) like Ctrl-Alt-L can still be done using the Left Ctrl key.

And I haven't needed to use Caps Lock once!

Easter egg or incomprehensible feature?

I got something weird when using IntelliJ's auto-complete today.

I was in a .jspx file, and using the Ctrl-space suggest feature on an attribute of an imported tag library. It only happened if I put the cursor at the beginning of an existing attribute.


At first I thought it was a joke attribute that a previous developer had added to the tag library. But on closer inspection, it seems that IntelliJ is causing it. "IntellijIdeaRulezzz" is being prepended to the variable name and displayed in the drop down list.

Do you think it is referring to the very last rule in Intellij or is it something more like "Intellij Rulez ok"?