DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • Leveraging Weka Library for Facebook Data Analysis
  • Scalable Rate Limiting in Java With Code Examples: Managing Multiple Instances
  • How To Get and Set PDF Form Fields in Java
  • Microfrontends for Quarkus Microservices

Trending

  • Wild West to the Agile Manifesto [Video]
  • Traffic Management and Network Resiliency With Istio Service Mesh
  • Docker and Kubernetes Transforming Modern Deployment
  • Five Free AI Tools for Programmers to 10X Their Productivity
  1. DZone
  2. Coding
  3. Java
  4. Creating JSF Pages with Pure Java Code

Creating JSF Pages with Pure Java Code

With a few tricks, you can make writing JSF in Java quite convenient.

Gabor Farkas user avatar by
Gabor Farkas
·
Aug. 03, 13 · Tutorial
Like (0)
Save
Tweet
Share
32.91K Views

Join the DZone community and get the full member experience.

Join For Free

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. 

Java (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Leveraging Weka Library for Facebook Data Analysis
  • Scalable Rate Limiting in Java With Code Examples: Managing Multiple Instances
  • How To Get and Set PDF Form Fields in Java
  • Microfrontends for Quarkus Microservices

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: