Dynamic component IDs for JSF

March 5, 2009

What’s that you say? Dynamic component IDs for JSF? Impossible! The JSF specification clearly states that the “id” attribute must be a hard coded value. Well I say screw that. There are some times where I want to use a variable to define the ID. Like today at work I came across this exact situation. See I made a template for a specific page and used some t:aliasBean tags to set variables for template. But of course this failed when I wanted to use a variable for the id of an h:form component.

The solution… Far simpler than I thought. All it took was a simple custom component that takes the variable and has its’ renderer set the id. Interested? Read on…

There are several ways to do this, but really you just need to make a wrapper component that takes some attribute as a value binding. Then in the component’s renderer grab the first child element and set its ID to whatever value is pointed to by your value binding. So rather than write a component from scratch, all we really need is a something with a value binding so why not just use an existing ValueHolder component like UIOutput. At work I actually made a new component that extends UIOuput and left the class body blank in case I later want to change things. But for this example let’s just use UIOutput as is. We’ll still need a tag class and a renderer class.

// tag
package com.jsfutils;
public class DynamicIdTag extends UIComponentTagBase {
  public String getComponentType() {
    return "com.jsfutils.DynamicId";
  }
  public String getRendererType() {
    return "com.jsfutils.DynamicId";
  }
}

// renderer
packacge com.jsfutils;
public class DynamicIdTagRenderer extends Renderer {
  public boolean getRendersChildren() { return false; }
  
  public void encodeBegin(FacesContext context, UIComponent component) 
                                         throws IOException {
    
    if (component instanceof ValueHolder) {
      // get the dynamic value and validate it    
      String dynamicId = (String) ((ValueHolder)component).getValue();
       
      if (dynamicId == null || dynamicId.equals("")) return;

      // find the first child component
      List children = component.getChildren();
      if (children.isEmpty) return ;
    
      // set the id
      UIComponent firstChild = (UIComponent) children.get(0);
      firstChild.setId(dynamicId);
    }
  }
}

// faces-config
<component>
  <component-type>com.jsfutils.DynamicId</component-type>
  <component-class>javax.faces.component.UIOutput</component-class>
</component>

<render-kit>
  <renderer>
    <renderer-type>com.jsfutils.DynamicId</renderer-type>
    <renderer-class>om.jsfutils.DynamicIdRenderer</renderer-class>
  </renderer>
</render-kit>

// tld
<taglib>
  <tag>
    <name>dynamicId</name>
    <tagclass>com.jsfutils.DynamicIdTag</tagclass>
    <attribute>
      <name>value</name>
      <required>true</required>
      <rtexprvalue>false</rtexprvalue>
    </attribute>
  </tag>
</taglib>

// use in a JSP/JSF page assuming the tag prefix is "jsfutil"
<jsfutil:dynamicId value="#{backingBean.myFormIdValue}">
  <h:form>
    <%-- contents of the form --%>
  </h:form>
</jsfutil:dynamicId>

And there you have it. Dynamic component IDs! When the page loads the form will be rendered with the id specified by the backing bean and of course the actual component ID in the JSF component tree will also have this id.

Advertisements

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”


Developing Java on my Mac

February 27, 2008

I recently made the switch from PC to Mac after many years of Windows and even DOS before that. So my overall opinion on Mac is good. I have however run into some issues doing some simple Java development.

First issue: There is no publicly available JDK from Sun for the Mac. Apple packages a special version of the JDK with OS X and us Mac users can only get Java updates from Apple and not Sun. What does this mean, at the time of writing I’m currently using Java build 1.5.0_13-119 which is some version of Java 5. Not the end of the world but Java 6 has been out for a while and who knows when Apple will release their version. It’s still better than being stuck on Java v1.4.2. I bring this up because at my job I’m doing a project that’s targeted to run on WebSphere v6 which is a kick ass server but only runs Java v1.4.2. If the Mac had limited me to v1.4.2 I would be quite annoyed.

Second issue: Eclipse kind of sucks on OS X. If you’re a Java developer you’ve probably used Eclipse. For personal development it’s generally first choice, or should I say my only choice. So I decided I wanted to screw around with a web idea I had (sorry that one’s confidential, I’ll tell you about when I make millions of dollars off it). So I got Eclipse Europa up and running and what do you know, it looks a lot like Eclipse on the PC except the SWT widgets which are OS X style instead of Windows. So far I like it. In general the keyboard shortcuts work the same. First I just made a simple Java project and did some “Hello World” type of programs just to see how Eclipse handled itself. No problems yet. Web applications… well that was just a whole other story. I started my little web application and was just playing a form page. Got Tomcat 6 up and running. Now maybe 30 minutes in or so I did something, don’t remember exactly what, I think I just opened web.xml and then the beach ball showed.

If you’re unfamiliar with Macs and OS X well there’s this thing we call the beach ball. It’s sort of the equivalent of the hour glass on Windows. It’s this multicolor circle thing that spins and looks like a beach ball. The first few times I saw it I said “hey that’s pretty cool, much nicer than that plain hour glass”. But that wears off fast, and it’s just as annoying as the hour glass because it usually blocks the system until it’s done whatever it’s trying to do.

Soon enough the beach ball disappeared, but so did Eclipse. Yeah it just crashed on me with no error message other than the standard OS X “some program just crashed” message. Not much help. I checked the Eclipse log and saw a bunch of crap I didn’t feel like looking at. So I tried it again. Not long after, it crashed again. This happened a few times. I considered opening an Eclipse bug but then I remembered how annoying bugzilla can be so I didn’t bother. I kind of want to smack whoever came with the phrase “Zarro boogs found”. If you’ve ever used bugzilla you may know what I’m talking about.

I figured I’ll just try making a new workspace and that will fix everything. See I used to work for IBM support, specifically RAD/WSAD which is built on top of Eclipse. You wouldn’t believe how many times creating a new workspace was a solution. Not a very convenient one but it usually fixed a lot of the inconsistent problems. It didn’t even work for me in this case. So end of the story is that Eclipse like to crash a lot on my system. I was over on the macrumors forum and some people say they’ve had no issue but there were a few that agreed Eclipse was a bit problematic, at least on Leopard (latest release of OS X… the one I’m running).

I decided to give Netbeans a try. So far it hasn’t crashed on me. So far I like it. Never did much work with it but heard good things. It has similar features to Eclipse. I’ve been able to get going with Netbeans. The only problem I ran into was getting Tomcat running via Netbean, better yet through the terminal. I’m not 100% sure but I think Eclipse either has its own startup scripts or hooks directly into the Tomcat runtime JARs and therefore requires 0 configuration from the user. Netbeans uses the ‘catalina.sh’ script and was complaining about not being able to execute it. So I opened up the terminal to see for myself and sure enough I didn’t have the correct permissions. After setting the startup script permissions¬† to 755 and trying again it was giving me some warnings about environment variables not being set. I looked at the script and saw it was calling some other scripts. So eventually I just did a ‘chmod 755 *’ and then it all worked just fine.

So in the end looks like I may just have to get used to Netbeans and I’ll be developing away on my Mac.