HTML5 Zone is brought to you in partnership with:

Gabor has posted 5 posts at DZone. View Full User Profile

Creating JSF pages with pure Java code

08.02.2013
| 16308 views |
  • submit to reddit
Many times I find xhtml templating hard to maintain, so in a recent project I decided to give a try to creating complex JSF component trees from Java code. This article will give you a quick overview about the techniques, benefits and drawbacks of this approach. Xhtml and EL is not java code (well, thank you, Captain Obvious) and thus it needs further IDE support for proper refactoring, searching, navigation, autocompletion and type-safety. IntelliJ IDEA gives you a very good one, and there's JBoss Tools for Eclipse (though I find it very slow and unstable yet). Still, I find using pure Java code better in many cases. The other point is the declarative templating JSF2 and Facelets give you. I just don't like it. After a certain complexity, Java code is just easier to read, navigate and refactor. When you use procedural UI construction, composition, aggregation and other stuff are expressed as normal Java language constructs, and you have procedural control over them.

Creating components

So let's give it a go. I haven't tried creating entire JSF views from java code, the frames are still in xhtml. When there's more html content than components, xhtml is easier to write, but when there are more components, I turn to Java code. I just insert a component "placeholder" in my xhtml template:
<h:panelGroup binding="#{myBacking.panel1}"/>
And in the backing class:
class MyBacking {
    private HtmlPanelGroup panel1;
   
    @PostConstruct
    public void init() {
        panel1 = new HtmlPanelGroup();
        panel1.setId("panel1");
        HtmlOutputLabel label = new HtmlOutputLabel();
        label.setValue("hello world");
        panel1.getChildren().add(label);
    }
    public void getPanel1() {
        return panel1;
    }
    public void setPanel1(HtmlPanelGroup panel1) {
        // do nothing
    }
}
So this will present a simple panel in the component tree. You create any component you want, and not just standard JSF components: we use PrimeFaces components from Java code as well.

Value Binding

Value bindings can be created from EL strings in Java:
/**
* @param el EL string WITH the #{} marks
*/
public static ValueExpression createFromEL(String el) {
FacesContext context = FacesContext.getCurrentInstance();
ELContext elContext = context.getELContext();
return FacesContext.getCurrentInstance().getApplication().getExpressionFactory()
.createValueExpression(elContext, el, Object.class);
}
But this is not the way you should generally go, because EL strings in Java code are no better. The ValueExpression class can be extended quite easily to provide some basic functionality.

Binding to static or local values

import javax.el.ELContext;
import javax.el.ValueExpression;

public abstract class DirectValueExpression<Type> extends ValueExpression {
	
	private Class<Type> type;
	
	public static <T> DirectValueExpression<T> of(final T value) {
		return new DirectValueExpression<T>((Class)value.getClass()) {
			@Override
			public T getValue() {
				return value;
			}
		};
	}

	public DirectValueExpression(Class<Type> type) {
		this.type = type;
	}
	
	public abstract Type getValue();
	
	public void setValue(Type value) {
		// do nothing
	}

	@Override
	public Object getValue(ELContext context) {
		return getValue();
	}

	@Override
	public void setValue(ELContext context, Object value) {
		setValue((Type) value);
	}

	@Override
	public boolean isReadOnly(ELContext context) {
		return true;
	}

	@Override
	public Class<?> getType(ELContext context) {
		return type;
	}

	@Override
	public Class<?> getExpectedType() {
		return type;
	}

	@Override
	public String getExpressionString() {
		return null;
	}

	@Override
	public boolean equals(Object obj) {
		return this == obj;
	}

	@Override
	public int hashCode() {
		return 0;
	}

	@Override
	public boolean isLiteralText() {
		return false;
	}
	
}

You can use the static of method to create bindings to constants:

behavior.setValueExpression("process", DirectValueExpression.of("panel"));

And you can bind to a field in your class:

		new DirectValueExpression<String>(String.class) {
			public String getValue() {
				return name; 
			}
			@Override
			public void setValue(String value) {
				name = value;
			}
		};

Binding to properties of context objects

If you want to bind to properties of backings that are not accessible from the method you're in, there's a convenient way for that too.

public abstract class BasicValueExpression<Root, Type> extends ValueExpression {

private Class<Type> valueClass;
	private String rootName;
	private Class<Root> rootClass;

	private ELContext currentContext;

	public BasicValueExpression(Class<Root> backingClass, Class<Type> valueClass) {
		this.rootName = backingClass.getSimpleName().substring(0, 1).toLowerCase()
		        + backingClass.getSimpleName().substring(1);
		this.rootClass = backingClass;
		this.valueClass = valueClass;
	}

	public BasicValueExpression(String rootName, Class<Root> rootClass, Class<Type> valueClass) {
		this.rootName = rootName;
		this.rootClass = rootClass;
		this.valueClass = valueClass;
	}

	public BasicValueExpression(UIData uiData, Class<Root> rootClass, Class<Type> valueClass) {
		this(uiData.getVar(), rootClass, valueClass);
	}

	public BasicValueExpression(UIRepeat uiRepeat, Class<Root> rootClass, Class<Type> valueClass) {
		this(uiRepeat.getVar(), rootClass, valueClass);
	}

	protected abstract Type getFromRoot(Root root);

	@Override
	public Object getValue(ELContext context) {
		currentContext = context;
		Root root = getRoot(context);
		return getFromRoot(root);
	}
	
public <T> T resolve(String name, Class<T> cls) {
		ValueExpression rootExpression = FacesContext.getCurrentInstance().getApplication().getExpressionFactory()
		        .createValueExpression(currentContext, "#{" + name + "}", cls);
		return (T) rootExpression.getValue(currentContext);
	}

	private Root getRoot(ELContext context) {
		ValueExpression rootExpression = FacesContext.getCurrentInstance().getApplication().getExpressionFactory()
		        .createValueExpression(context, "#{" + rootName + "}", rootClass);
		Root root = (Root) rootExpression.getValue(context);
		return root;
	}
	
	protected void setOnRoot(Root root, Type value) {
		// do nothing silently, if not overriden
	}

	@Override
	public void setValue(ELContext context, Object value) {
		Root root = getRoot(context);
		setOnRoot(root, (Type) value);
	}

	@Override
	public boolean isReadOnly(ELContext context) {
		return true;
	}

	@Override
	public Class<?> getType(ELContext context) {
		return valueClass;
	}

	@Override
	public Class<?> getExpectedType() {
		return valueClass;
	}

	@Override
	public String getExpressionString() {
		return null;
	}

	@Override
	public boolean equals(Object obj) {
		return false;
	}

	@Override
	public int hashCode() {
		return 0;
	}

	@Override
	public boolean isLiteralText() {
		return false;
	}

	public ELContext getCurrentContext() {
		return currentContext;
	}
}

With this class, binding starts from a specific root expression of type Root and finally resolves to the target expression of type Type. You also have to define the name of the root object in the root context. For backings, we use the naming convention of making the first letter of the backing class name lowercase. Inside repeat components, the name of the root object is usually in the "var" property of the repeat component. For example:

		ValueExpression titleBinding = new BasicValueExpression<MyBacking, String>(MyBacking.class,
		        String.class) {
			@Override
			protected String getFromRoot(MyBacking root) {
				return root.getDialogTitle();
			}
		};

Other ways

Well, this is a way more code than writing "#{myBacking.dialogTitle}" - that's for sure. But this is type-safe, you can bravely refactor your code and discover property usage with your plain Java IDE.

We have another method to more easily, still type-safely bind values in JSF, but that will be for another article some weeks later.

EL also supports automagically processing Bean Validation annotations. If we write our own value expressions, we lose this functionality. We can quite easily add bean validation to our bindings too, but I leave that topic also to the forementioned future article.

Action Listeners

In EL, you would create method bindings. In Java code, you can just implement the ActionListener interface as you would do in Swing. I haven't yet met a case when actually creating an EL method binding from Java code was necessary.

So adding behaviour to buttons is simple:

		button.addActionListener(new ActionListener() {
			@Override
			public void processAction(ActionEvent event) throws AbortProcessingException {
				System.out.println("button clicked");
			}
		});

Special xhtml elements

Some xhtml elements don't have JSF component equivalents, they are special instructions. When the xhtml engine finished constructing and composing the view source, it runs tag handlers to create the component tree in Java objects. The tag f:attribute for example is a special tag that is handled by com.sun.faces.facelets.tag.jsf.core.AttributeHandler.

Static text

Facelets converts static html content to a special component named UIInstructions, but those classes are private for facelets. But we're not lost, a very simple component will do the same for us:

public class TextLiteral extends UIComponentBase {
	
	private String text;
	
	public TextLiteral(String text) {
		this.text = text;
	}
	
	@Override
	public String getFamily() {
		return "textLiteral";
	}
	
	@Override
	public void encodeEnd(FacesContext context) throws IOException {
		context.getResponseWriter().append(text);
	}

}

Adding ajax behaviour

The tags f:ajax and p:ajax are also special tags. Their handlers add AjaxBehaviours to their parent components. To add PrimeFaces AjaxBehavior (that is similar to the standard JSF AjaxBehavior) to a component, just do the following:

			AjaxBehavior behavior = (AjaxBehavior) FacesContext.getCurrentInstance().getApplication().createBehavior(AjaxBehavior.BEHAVIOR_ID);
			behavior.setValueExpression("update", DirectValueExpression.of("@form"));
			behavior.setValueExpression("process", DirectValueExpression.of("@form"));
			calendar.addClientBehavior("dateSelect", behavior);

Adding standard JSF ajax behaviour is similar.

Summary

When we started using these techniques, the code first looked quite bloated indeed. But I think that with a few tricks, you can make writing JSF in Java quite convenient, and it turned out in a larger project at Doctusoft Ltd that after a specific complexity, having most of the UI stuff in Java really makes templating, code reuse and maintenance a lot easier. 

Published at DZone with permission of its author, Gabor Farkas.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Henk De Boer replied on Sat, 2013/08/03 - 4:27am

This is technically dynamic tree component manipulation. Very nice explanation though!

See also this blog entry: http://blog.kennardconsulting.com/2010/10/safely-manipulating-component-tree-with.html and one that specifically focusses on creating tables dynamically: http://balusc.blogspot.com/2006/06/using-datatables.html#PopulateActionDatatable

Taking this concept one step further, it's also possible in JSF to create your own VDL that allows you to directly author the tree in Java (no Facelets involved anywhere), see:

http://jdevelopment.nl/authoring-jsf-pages-pure-java and http://jdevelopment.nl/single-class-pure-java-jsf-application

The JSF spec lead (Ed Burns) has created something similar as well, see https://java.net/projects/jsf-extensions/sources/svn/show/trunk/code/run-time/javajsf 



Tom Hombergs replied on Sun, 2013/08/04 - 1:52pm

I agree with you that Java code is better to maintain than (X)HTML code because of IDE support. It' also more fun, I think :). 

There are Java web frameworks that are designed with exactly this notion in mind. Have a look at Wicket. From what you're saying, I believe it would suit you very much :). Another one is Google Web Toolkit. I think it's much easier using (almost) pure Java with those frameworks than with JSF because it just is more "natural".

Gabor Farkas replied on Tue, 2013/08/13 - 2:50am

Henk: thanks for the great links! It's good to see that there are others preferring this way too!

Tom: yup, I personally don't like JSF, but once we have to use it, I try to make it more comfortable :)

Roger Keays replied on Wed, 2014/05/21 - 12:39am

Thanks for the article. Coding in Java is so much better than coding in xml (which is inevitably what ends up happening with declarative languages).

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.