Using JSF’s managed beans to access constants

December 19, 2008

So I’ve been working on this JSF project for a while. Like most Java projects we have a few constant classes where we keep some constant values (no surprise there). Until now we’ve typically had trouble using those constants in a JSP page as the JSF EL has no way to access any class. You’re pretty much stuck with the managed beans. So often we would just have a method on the managed bean called getConstantValue() which would return the constant value we wanted to use. We had also written some custom components and just set rtexprvalue to true which allowed us to access a constant with . However today I ran into an issue where I need to get some constant values and it didn’t seem practical to have a bunch of getters on the managed bean. I had an idea though. The JSF EL has no real way of calling a method with parameters, but you can access keys in a Map. So essentially this means you can have an JSF EL expression like #{bean.myMap[‘key’]} which will translate into bean.getMyMap.get(key). Looks like we can pass a parameter after all. How do we take advantage of this? Easy really… you just need to create your own Map implementation and do whatever you want in the “get” method.

So now I just need a function to access my constants. I decided that if I want to access a constant called MY_VALUE from a class called Constants easiest way would be to pass a String “Constants.MY_VALUE” and use reflection on the other side to get the value. But then I realized that using reflection would require a fully qualified class name which will make the code look very cluttered and ugly. So instead I decided to keep a Map of class names and their corresponding classes.

    
private final static Map CONSTANT_CLASSES;
static {
    CONSTANT_CLASSES = new HashMap();
    CONSTANT_CLASSES.put("Constants", Constants.class);
}

With this I can pass “Constants” by itself and let the map worry about the fully qualified class name. The disadvantage to this is if I decide to add another constants class I have to change this class too. However realistically it’s not very often that you’ll be adding new constants classes.

Next is the actual get method that will retrieve the value. Reflection is still the way to go here:

 public Object get(Object key) {
	Object value = null;

	String[] asString = ((String) key).split("\\u002e");

	if (asString.length != 2) {
		throw new PropertyNotFoundException(
			"Invalid key format for key \"" + key + "\"");
	}

	Class clazz = (Class) CONSTANT_CLASSES.get(asString[0]);

	if (clazz == null) {
		throw new PropertyNotFoundException("Class \"" + 
			clazz + "\" is not a defined as a Constants class");
	}

	try {
		Field field = clazz.getDeclaredField(asString[1]);
		int mods = field.getModifiers();

		if (Modifier.isPublic(mods) && Modifier.isFinal(mods) 
						&& Modifier.isStatic(mods)) {
			value = clazz.getDeclaredField(asString[1]).get(null);
		} else {
			throw new PropertyNotFoundException("No \"public " +
			final static\" field exists for the key " + key);
		}
	} catch (Exception e) {
		throw new PropertyNotFoundException(e);
	}
	return value;
}

Now I was concerned about the performance of this since reflection has been known to be a bit slower. Although I doubt any significant performance will arise from using this I still thought it would be safer to add some sort of cache to this. It’s a really simple cache. It’s just a HashMap which will store the key parameter and map it to the value retrieved using reflection. So really reflection will only be use the first time a particular constant is used. After that it will just be a get() call on a HashMap. The code for this is almost identical except for the cache part.

public Object get(Object key) {
	Object value = CACHE.get(key);

	if (value == null) {
		String[] asString = ((String) key).split("\\u002e");

		if (asString.length != 2) {
			throw new PropertyNotFoundException(
				"Invalid key format for key \"" + key + "\"");
		}

		Class clazz = (Class) CONSTANT_CLASSES.get(asString[0]);

		if (clazz == null) {
			throw new PropertyNotFoundException("Class \"" + clazz + 
				"\" is not a defined as a Constants class");
		}

		try {

			Field field = clazz.getDeclaredField(asString[1]);
			int mods = field.getModifiers();

			if (Modifier.isPublic(mods) && Modifier.isFinal(mods) 
						&& Modifier.isStatic(mods)) {
				value = clazz
					.getDeclaredField(asString[1])
					.get(null);

				CACHE.put(key, value);
			} else {
				throw new PropertyNotFoundException(
				"No \"public final static\" field exists for the key " + key);
			}

		} catch (Exception e) {
			throw new PropertyNotFoundException(e);
		}
	}
	return value;
}

This is what the full class looks like:

public class MbConstants {
	private final static Logger log;

	// Map to cache retrieved values
	private final static Map CACHE;

	/*
	 Constant classes must be defined here!
	 */
	private final static Map CONSTANT_CLASSES;
	static {
		log = Logger.getLogger(MbConstants.class);

		CACHE = new HashMap();

		CONSTANT_CLASSES = new HashMap();

	}

	/**
	 * An anonymous Map type that will be used to get values from the Constant
	 * classes. The first time a value is accessed it will be retrived using
	 * reflection and stored in the cache. Subsequent requests for the same key
	 * will just come from the cache. Since the JSF EL has no way of passing
	 * parameters to a function using a Map is the next best alternative since
	 * we can do #{bean.map['key'} which translates to Map#get(key).
	 */
	private final Map constants = new Map() {
		public Object get(Object key) {
			Object value = CACHE.get(key);

			if (value == null) {
				String[] asString = ((String) key).split("\\u002e");

				if (asString.length != 2) {
					log.error("Invalid key format for key \"" + key + "\"");
					throw new PropertyNotFoundException(
							"Invalid key format for key \"" + key + "\"");
				}
				Class clazz = (Class) CONSTANT_CLASSES.get(asString[0]);
				if (clazz == null) {
					log.error("Class \"" + clazz + "\" is not a defined as a Constants class");
					throw new PropertyNotFoundException("Class \"" + clazz + "\" is not a defined as a Constants class");
				}
				try {
					Field field = clazz.getDeclaredField(asString[1]);
					int mods = field.getModifiers();
					if (Modifier.isPublic(mods) && Modifier.isFinal(mods) && Modifier.isStatic(mods)) {
						value = clazz.getDeclaredField(asString[1]).get(null);
						CACHE.put(key, value);
					} else {
						throw new PropertyNotFoundException("No \"public final static\" field exists for the key " + key);
					}
				} catch (Exception e) {
					log.error("Unable to find constant value with key " + key);
					log.error(e.getMessage(), e);
					throw new PropertyNotFoundException(e);
				}

			}
			return value;
		}

		public boolean containsKey(Object key) {
			return false;
		}

		public boolean containsValue(Object value) {
			return false;
		}

		public Set entrySet() {
			return null;
		}

		public Collection values() {
			return null;
		}

		public void clear() {
		}

		public boolean isEmpty() {
			return false;
		}

		public Set keySet() {
			return null;
		}

		public Object put(Object key, Object value) {
			return null;
		}

		public void putAll(Map t) {
		}

		public Object remove(Object key) {
			return null;
		}

		public int size() {
			return 0;
		}

	};

	public Map getConstants() {
		return constants;
	}
}

The “immediate” problem with JSF

December 10, 2008

So if you’ve developed with JSF before there’s a good chance you’ve run into problems using the “immediate” attribute. In case you’re unfamiliar with the immediate attribute here’s a quick description. Hopefully you’re already familiar with the JSF lifecycle which looks like this:

  1. Restore view
  2. Apply request values
  3. Process validation
  4. Update model
  5. Invoke application
  6. Render response

Typically you’ll have a form with a few fields. You may decided to add some validation to a field or make a field required. Now when you submit your form if any validations or conversions fail in the process validation phase of the JSF lifecycle you’ll skip straight to the render response phase. The same view will be rendered again and if you use the h:message or h:messages components the validation errors will be displayed. This is a great feature of JSF, but most forms also have a cancel button. However clicking the cancel button will still invoke the process validation and show you validation errors.

Enter the “immediate” attribute. On the cancel button you can specify an attribute called “immediate” and set its’ value to true. What this will do is tell JSF to skip to the invoke application phase right after the apply request values phase. Now we’re skipping over the validation and update phases and so we don’t need to worry about validation errors when clicking a cancel button.

A more complex scenario. This is a problem I faced the other day. I created a form that uses a drop down list as one of the fields. The user will then have an option to add items to the drop down list if the item they are looking for is not there. To accomplish this there is a command link that the user can click which will navigate away from the form. The user can then update the drop down list and return to the form. I’ve used the Tomahawk saveState component on the form page and the page that manages the drop down so that when I come back to the form, any data previously entered will still be there. Later on I added validation to the form and guess what? When I clicked that link to manage the drop down list I got validation errors instead of the page I expected to see. Then I thought to myself, “wait a minute, I just need to use the immediate attribute on that command link… problem solved”… or so I thought. Using the immediate attribute got rid of the validation problem but since I’m now skipping over the update model phase any data previously entered in the form is lost.

I did some searching around and found no easy solution to this problem so I came up with my own… and here it is.

I created an action listener on the bean for the command link which will find all UIInputs in the form and manually call their validator methods. If the data is valid then I call the update method as well. To my surprise this simple solution actually worked on the first try. For now it assumes that all the inputs are direct children of the form however it can be modified to handle all child components. So here it is.

public void saveRawFields(ActionEvent e) {
	UIComponent component = e.getComponent();

	while (!(component instanceof HtmlForm || component == null)) {
		component = component.getParent();
	}

	if (component == null) {
		throw new NullPointerException("component is null, "
				+ "maybe it's not inside a form?");
	}

	FacesContext context = FacesContext.getCurrentInstance();
	List children = component.getChildren();
	for (UIComponent c : children) {
		if (c instanceof UIInput) {
			UIInput input = (UIInput) c;

			input.processValidators(context);

			if (input.isValid()) {
				input.processUpdates(context);
			}
		}
	}
}

For some more info on the immediate attribute check out “How The Immediate Attribute Works”