Creating JSF Pages with Pure Java Code
With a few tricks, you can make writing JSF in Java quite convenient.
Join the DZone community and get the full member experience.
Join For FreeMany 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}"/>
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
}
}
Value Binding
/**
* @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.
Opinions expressed by DZone contributors are their own.
Comments