Archive for the ‘Development’ category

Musings of a SpringOne 2009 Attendee – Day 4

March 10th, 2010

This is the last and final part on my SpringOne 2009 experience. It’s late catching up to the 3 earlier posts but it’s here now. This post summarizes the sessions I attended from day 4 and wraps up with a summary of my take aways. If you want to catch up here are the three earlier posts:

  1. Musings of a SpringOne 2009 Attendee Day 1
  2. Musings of a SpringOne 2009 Attendee Day 2
  3. Musings of a SpringOne 2009 Attendee Day 3

Read on for day 4.

» Read more: Musings of a SpringOne 2009 Attendee – Day 4

Rendering Global t:messages After Redirect

March 8th, 2010

A common problem when working with JSF is getting global info messages  via <t:messages globalOnly="true"> or <f:messages globalOnly="true"> to display messages set in the previous request when you have a <redirect/> in your faces-config for a particular page You will not see your <t:messages> that are set on the previous page.

The Problem

For instance, say you have two pages – page1.xhtml and page2.xhtml. In your faces-config.xml, you will have 2 entries.

<navigation-case>
	<from-outcome>page1</from-outcome>
	<to-view-id>/pages/page1.xhtml</to-view-id>
	<redirect/>
</navigation-case>
 
<navigation-case>
	<from-outcome>page2</from-outcome>
	<to-view-id>/pages/page2.xhtml</to-view-id>
	<redirect/>
</navigation-case>

Let’s say page1Bean is called in page1 and you want it to display messages in page2. So your bean would have something like this:

public class page1Bean {
	...
	public String page1Action{
		...
		/** Add message */
		FacesMessage facesMessage = new FacesMessage();
		...
		FacesContext.getCurrentInstance().addMessage(null, facesMessage);
		...
		return "page2";
	}
	...
}

Your page2.xhtml has like the following somewhere in it.

<t:messages globalOnly="true">

One would expect that this would display the message on the next page, however these messages are request scoped and so are not available in this second request and you do not see your message unless you remove the <redirect/> on page2.

Fortunately, there is a nifty and quick solution. Basically, it is a phase listener that saves the messages from the previous request and then restores them just before the RENDER_RESPONSE phase of the second request.

The Solution

To solve the problem, save the following class into some package where your faces-config will be able to access it

package com.myproject.web.jsf.phaselistener;
 
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
 
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
 
/**
 * Enables messages to be rendered on different pages from which they were set.
 * To produce this behaviour, this class acts as a <code>PhaseListener</code>.
 *
 * This is performed by moving the FacesMessage objects:
 *
	<li>After each phase where messages may be added, this moves the messages from
 * the page-scoped FacesContext to the session-scoped session map.
 *</li>
	<li>Before messages are rendered, this moves the messages from the session-scoped
 * session map back to the page-scoped FacesContext.
 *</li>
 * Only messages that are not associated with a particular component are ever
 * moved. These are the only messages that can be rendered on a page that is different
 * from where they originated.
 *
 * To enable this behaviour, add a <code>lifecycle</code> block to your
 * faces-config.xml file. That block should contain a single <code>phase-listener</code>
 * block containing the fully-qualified classname of this file.
 *
 * EDIT: This code was minimally modified by Max Kuipers to address some of the Java 1.6
 * compiler warnings.  All code was originally written by Jesse Wilson.
 *
 * @author <a href="mailto:jesse@odel.on.ca">Jesse Wilson</a>
 * @author <a href="mailto:mkuipers@sourceallies.com">Max Kuipers</a>
 */
 
public class MessageHandler implements PhaseListener {
	private static final long serialVersionUID = 1L;
 
	/**
	 * a name to save messages in the session under
	 */
	private static final String sessionToken = "MULTI_PAGE_MESSAGES_SUPPORT";
 
	/**
	 * Return the identifier of the request processing phase during which this
	 * listener is interested in processing PhaseEvent events.
	 */
	public PhaseId getPhaseId() {
		return PhaseId.ANY_PHASE;
	}
 
	/**
	 * Handle a notification that the processing for a particular phase of the
	 * request processing lifecycle is about to begin.
	 */
	public void beforePhase(PhaseEvent event) {
 
		if(event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
			FacesContext facesContext = event.getFacesContext();
			restoreMessages(facesContext);
		}
	}
 
	/**
	 * Handle a notification that the processing for a particular phase has just
	 * been completed.
	 */
	public void afterPhase(PhaseEvent event) {
 
		if(event.getPhaseId() == PhaseId.APPLY_REQUEST_VALUES ||
				event.getPhaseId() == PhaseId.PROCESS_VALIDATIONS ||
				event.getPhaseId() == PhaseId.INVOKE_APPLICATION) {
 
			FacesContext facesContext = event.getFacesContext();
			saveMessages(facesContext);
		}
 
	}
 
	/**
	 * Remove the messages that are not associated with any particular component
	 * from the faces context and store them to the user's session.
	 *
	 * @return the number of removed messages.
	 */
	private int saveMessages(FacesContext facesContext) {
		// remove messages from the context
		List messages = new ArrayList();
		for(Iterator i = facesContext.getMessages(null); i.hasNext(); ) {
			messages.add(i.next());
			i.remove();
		}
		// store them in the session
		if(messages.size() == 0) {
			return 0;
		}
		Map sessionMap = facesContext.getExternalContext().getSessionMap();
		// if there already are messages
		@SuppressWarnings("unchecked")
		List existingMessages = (List) sessionMap.get(sessionToken);
		if(existingMessages != null) {
			existingMessages.addAll(messages);
		}
		else {
			sessionMap.put(sessionToken, messages); // if these are the first messages
		}
 
		return messages.size();
	}
 
	/**
	 * Remove the messages that are not associated with any particular component
	 * from the user's session and add them to the faces context.
	 *
	 * @return the number of removed messages.
	 */
	private int restoreMessages(FacesContext facesContext) {
		// remove messages from the session
		Map sessionMap = facesContext.getExternalContext().getSessionMap();
		@SuppressWarnings("unchecked")
		List messages = (List)sessionMap.remove(sessionToken);
		// store them in the context
		if(messages == null) {
			return 0;
		}
		int restoredCount = messages.size();
		for(Iterator i = messages.iterator(); i.hasNext(); ) {
			facesContext.addMessage(null, i.next());
		}
 
		return restoredCount;
	}
}

And then add something like the following in your faces-config.xml. Be sure to replace the example package location with your own.

<lifecycle>
	<phase-listener>com.myproject.web.jsf.phaselistener.MessageHandler</phase-listener>
</lifecycle>

The forum post that documented this solution is here.

Developing a multithreaded test harness

March 5th, 2010

You can’t ignore the fact that web servers are multithreaded. We can hide as much as we want, but sooner or later you’ll find yourself in the situation where your application works fine during development and testing; but once it hits production you start hearing about “funny” things happening. While there are plenty of tools that can be used to simulate multiple users, they aren’t always the easiest to run locally and they seem to take to much time to modify while hot on the trail of a multithreading bug. Here I’ll discuss the approach I took when I was recently faced with this situation.
» Read more: Developing a multithreaded test harness

Simple Subversion Branching and Merging

March 3rd, 2010

Branching and merging in Subversion is a great way to work on large new features without disrupting mainline development on trunk.  However, it has a reputation for being so difficult that many developers never take advantage of it.  In this post I’ll show just how easy it really is thanks to some newer features in Subversion and Subclipse (a Subversion plug-in for Eclipse).
» Read more: Simple Subversion Branching and Merging

Java EE 6 and Scala

February 22nd, 2010

Last weekend while pondering the question “Is Scala ready for the enterprise?” I decided to write a simple Java EE 6 app entirely in Scala, without using any Java. I had three main reasons for doing this: one was just to see how easy/difficult it would be to write everything in Scala (it was easy).  Another was to document the process for others journeying down the same road (the entire project is on github).  Finally, I wanted to identify advantages of using Scala instead of Java that are specific to Java EE apps (I found several).
» Read more: Java EE 6 and Scala

Running a Technical Book Club – Take 1

February 18th, 2010

Last year I coordinated a technical book club here at Source Allies. This was my first experience doing one and I wanted to share my experience for the benefit of others who may be looking at starting one.

The fact that we even started a book club was a big positive because it is one great way to geek out with very smart people. You get the opportunity to voice your opinion on a certain topic and hear counter-points or similar views that expand your own perspective. » Read more: Running a Technical Book Club – Take 1

Replacing and Patching Java Application and Core classes

February 15th, 2010

Why would you ever need that?

Say you get a jar file. After using the jar for a while you realise that there is a bug in a class in the jar file. Unfortunately you also find out that the jar is no longer supported and there is no way you will get a fix from the author (who is long gone fishing).

In order to solve this issue, you first need to get the source of the class. If you are lucky enough and the author did not obfuscate the class file you can decompile it with a decompiler (my favourite one is JD-GUI).

» Read more: Replacing and Patching Java Application and Core classes

IP Addresses in PHP/MySQL

February 12th, 2010

I’ve been working on a web-based tool that stores, among other network-related things, IP addresses. When I first started I stored each IP address as four TINYINTS (0-255 for each octet):

mysql> DESC ipaddresses;
+----------+---------------------+------+-----+---------+----------------+
| FIELD    | Type                | NULL | KEY | DEFAULT | Extra          |
+----------+---------------------+------+-----+---------+----------------+
| id       | int(10) UNSIGNED    | NO   | PRI | NULL    | AUTO_INCREMENT | 
| A        | tinyint(3) UNSIGNED | NO   |     | NULL    |                | 
| B        | tinyint(3) UNSIGNED | NO   |     | NULL    |                | 
| C        | tinyint(3) UNSIGNED | NO   |     | NULL    |                | 
| D        | tinyint(3) UNSIGNED | NO   |     | NULL    |                | 
+----------+---------------------+------+-----+---------+----------------+
5 rows IN SET (0.00 sec)
 
mysql> SELECT * FROM ipaddresses WHERE id=1
+----+----+----+----+-----+
| id | A  | B  | C  | D   |
+----+----+----+----+-----+
|  1 | 10 | 20 | 30 | 131 |
+----+----+----+----+-----+
1 row IN SET (0.02 sec)

As I started manipulating these addresses I found it awkward to do common binary math (like bitwise ANDs). I decided instead to store these 32-bit values as unsigned integers (of length 32). To make my life easier yet, MySQL and PHP both have native functions to convert IP addresses between my old and new formats to make this migration extremely easy.
» Read more: IP Addresses in PHP/MySQL

Sonar – Code Quality Analysis Tool

February 9th, 2010

What is Sonar?

Sonar is a web based code quality analysis tool for Maven based Java projects. It covers a wide area of code quality check points which include: Architecture & Design, Complexity, Duplications, Coding Rules, Potential Bugs, Unit Test etc. Sonar has a rich set of features like what you would get with different tools such as Covertura, PMD, FindBugs, Check Styles combined.

http://sonar.codehaus.org/

Setting up Sonar

Is trying to learn a new language every year worth it?

January 15th, 2010

While spending time recently looking for something new to learn that looked interesting, and it still being so close to new years, I was reminded of a bit of advice from the book “The Pragmatic Programmer,” learn a new language every year. But is learning a new language every year actually helpful?
» Read more: Is trying to learn a new language every year worth it?