Sunday, December 27, 2009

Do transactions re-associate detached objects?

It appears that wrapping a manager method in a transaction via Hibernate's AOP functionality, does not automatically re-associate any detached objects passed into the method to the session. You'll need to do this manually.

Thursday, December 24, 2009

A Spring WebFlow Gotcha

If your flow definition has a element and its "if" attribute contains two or more clauses joined by the AND logical connector, make sure to use & amp;& amp; (no spaces) instead of &&, because && breaks XML validation.

Wednesday, December 16, 2009

Hibernate Intricacies

Forgive me please if the following post is not 100% clear.

Imagine this: You've got a Proposal object with a List<Resource> field. Each Resource has a Map<ResourceAttribute> field. You're using Hibernate, and the Resource.hbm.xml contains a <map> element with cascade="all-delete-orphan". If you call Session.save(resource) for a particular resource belonging to a proposal, and later call Session.update(proposal) for that proposal, you will get the following exception - Don't change the reference to a collection with cascade="all-delete-orphan".

Obviously you haven't directly changed the resource's reference to its map of attributes, but, somehow, by saving the resource directly, instead of jumping up a level and saving the proposal to which it belongs, you're causing Hibernate to consider the reference changed.

Tuesday, December 8, 2009

Extending JavaScript's onsubmit method

Suppose your page has a form, and you want to extend its onsubmit method programatically. Here's one way to go about it:

form = document.getElementById("form");
form.realSubmit = form.onsubmit;
form.onsubmit = function () {
  alert("before the original onsubmit");
  this.realSubmit();
}

(Based on Orc Scorcher's response to this thread on www.webdeveloper.com)

Monday, December 7, 2009

A Queue of AJAX Requests

Till now, I hadn't written code to fire off a number of AJAX requests with callbacks in rapid succession. The code wasn't working as hoped, and usually only the last callback would succeed. This is because a new AJAX request is being made before the callback for the previous request has finished executing. Due to their being only one XMLHttpRequest, concurrency issues prevail. A possible solution involves creating a queue for the requests and only firing the next request once the callback for the previous one has completed.

Wednesday, November 4, 2009

Javascript tip

This one had me stumped for 30 minutes or so. When including a JavaScript file in your HTML, make sure you provide open and close script tags. The abbreviated single tag with a backslash at the end doesn't work. At least in Firefox 3.5.4.

Wednesday, October 21, 2009

Tip: Spring Webflow 2.0

Unlike the flowExecutionUrl variable which is exposed in your JSP and can be used like so - ${flowExecutionUrl} - the messageContext variable is not exposed.

Wednesday, September 16, 2009

SWFUpload and Java

SWFUpload is a great file upload technology. Here's a link to minimum server requirements for using it in a Java context.

Sept. 17, 2009 UPDATE: I've been looking into whether someone has come up with a solution to SWFUpload's cookie bug in a Java context - so far no luck. Apparently, appending ;jsessionid=XXX to the upload url used to work in an earlier SWFUpload version / Flash 9. Doesn't work for me. Also, I tried creating a Filter which returned a HttpServletWrappedRequest with an overwritten getCookies() method that would add the 'jessionid' request parameter (if provided) as a cookie. That didn't work either.

Wednesday, July 15, 2009

An application of Function Queries in Solr

Suppose you have a set of Solr documents. You wish to push the subset of documents having a particular field with value X to the top of the search results, effectively superseding Solr's default scoring mechanism. For example, your index contains a collection of computers for sale and the particular field's name is "operatingsystemtype" . Possible values for this field are: 1 (Windows XP), 2 (Vista), 5 (Mac OS 10), and 7 (Debian). You wish to push all the documents with operatingsystemtype = 5 to the top.

One way to implement this is to embed a _val_ hook right after the q parameter in the HTTP query string you send to Solr. For example: http://search.buycomputers.com/select/?q=(price:[1000 TO 1500])+_val_="map(operatingsystemtype,5,5,1000)"&fl=id,name,price,details.
  • Note the '+' instead of the usual '&'.
  • The map function will map all values between 5 and 5 inclusive to 1000. (See here for more details.) Although this 1000 is normalized to a smaller value, it is added to the document's score. Basically, all documents with operatingsystemtype = 5 will have a very large constant added to their score, effectively pushing all these documents to the top.
  • Please note that this method relies on Solr's default sort (i.e. score desc). If you provide a sort parameter, thus overriding the default order, this method won't work.
  • This is called a Function Query in Solr parlance.

Wednesday, June 17, 2009

Tip: Eclipse auto-compile errors

If your Eclipse project has auto-compile errors popping up all over the place, and Eclipse can't even comprehend simple Java imports, make sure rt.jar, the Java run-time library, is in your project's Libraries path. Go to Project -> Properties -> Java Build Path -> Libraries and make sure rt.jar is there. If it isn't, add it. This fixed the problem for me.

Friday, June 12, 2009

Tips: Spring MVC and messages.properties

Tip #1:

If a message in your messages.properties file contains single quotes, you may find that these do not display properly. The trick is to use Unicode hex values instead. That said, avoid using \u0027, the Unicode hex value for apostrophe, as it may not work correctly. (It didn't for me.) Use \u2018 and \u2019 instead. These are, respectively, the left single quotation mark and the right single quotation mark.

So, for example, instead of...
errors.required.parameter=Required parameter '{0}' is not present.
... use...
errors.required.parameter=Required parameter \u2018{0}\u2019 is not present.

Tip #2:

If you pass a number parameter into a message in your messages.properties file, it will automatically be formatted as a number and commas will be inserted. For example, 12300 will be displayed as 12,300. If this number is an identifier, and you don't want commas to be inserted, convert the number to a string before passing it to your translation mechanism.

Example:

The message definition is:
errors.proposal.not.found=Proposal {0} not found.

Code:
Long proposalId = 227128;
String errorMessage= org.springframework.context.MessageSource.getMessage
("errors.proposal.not.found", new Object[] {
proposalId}, Locale.getDefault());

Error message:
Proposal 227,128 not found.

Change your code to:
org.springframework.context.MessageSource.getMessage
("errors.proposal.not.found", new Object[] {
proposalId.toString()}, Locale.getDefault());

Now the error message is:
Proposal 227128 not found.

Tuesday, June 9, 2009

User-friendly binding error messages in Spring MVC

In some cases, Spring's binding error messages are not fit to display to the user. How can we get around this? If we override SimpleFormController.onBindAndValidate() in our controller, in the hopes of modifying the BindException parameter accordingly, we'll be hard-pressed to find methods allowing us to do so.

One way to display user-friendly binding error messages is to override SimpleFormController.onBindAndValidate() as follows:

@Override
protected void onBindAndValidate(HttpServletRequest request, Object command,
BindException errors) throws Exception {
if (errors.hasErrors()) {
Map errorsMap = new HashMap();
List errorList = errors.getAllErrors();
for (FieldError error : errorList) {
String msg = "";
try {
msg = this.translate(error.getCode(), error.getArguments());
} catch (Exception e) {}
if (msg.contains("quantity")) {
msg = msg.replace(error.getField(), "quantity");
} else if (msg.contains("unitPrice")) {
msg = msg.replace(error.getField(), "unit price");
}
msg += ".";
errorsMap.put(error.getField(), msg);
}
request.setAttribute("errorsMap", errorsMap);
}
}
This creates a map of user-friendly error messages, keyed by the field names with which the error messages are associated. You can then use it in your JSP, possibly with the tag.

Monday, April 20, 2009

Tip: Velocity

If you are using the #if #else #end construct provided by Velocity, remember to put a space after the #else. If you don't, Velocity will ignore the #else and consider the statement to be a simple #if #end.

Thursday, April 16, 2009

Use BigDecimal for financial calculations

It is common to use the Double object or the double primitive type for financial calculations. Since you have no control over rounding behavior, mistakes may arise in your calculations. You might charge customers more (or less) than they owe. Use BigDecimal to solve this problem. For a clear, thorough explanation, see this document.

Monday, April 6, 2009

Tip: Tomcat startup exception

If, while starting up Tomcat, you get this error - SEVERE: Exception loading sessions from persistent storage - try to delete the SESSIONS.ser from your $CATALINA_HOME/work directory.

Friday, March 20, 2009

Iterating over maps in JSTL

For a simple and clear explanation of how to iterate over maps in JSTL, see this thread and scroll down to hoffmandirt's answer from May 13th, 2008.

Wednesday, February 11, 2009

JSTL's expression language and refactoring

Suppose you've got an object of class Person with a getName() method. JSTL's expression language allows you to access this field in your JSP with the following notation - ${person.name} - assuming you've passed the JSP a person variable pointing to an instance of Person. For example, you might use: .

This is a convenient feature, but it has a downside: Suppose your application grows and you now support first and last names. Person.getName() used to return a first name, but now you wish to change the method name to getFirstName(). This is a simple refactoring to do using a modern IDE - the IDE will find every mention of getName() in your code, and change it to getFirstName(). That's not enough, however. Remember to also search through your JSP's for references to this method and change them too.