; last updated - 9 minutes read

Composite components: a JSF 2.0 highlight

They say it's hard to write a JSF component.

That's rubbish. JSF has been designed with user defined components in mind. Beyondjava.net has a couple of articles on the topic, and my AngularFaces Github repository has a couple of working components you can use as a base. You'll see creating JSF components isn't rocket science at all.

But it true, it isn't done in a minute, either. Thus JSF 2.0 introduced a great feature to create user-defined components. Simple components are created in no time. As for not-so-simple components - well, they are not created in time. I'll return to that later. First I want to tell you the success story. It's an inspiring story to tell.

Why don't you just write a composite component?

Maybe you don't already know that you want to write a composite component. What about a little experiment? Have a look at your JSF pages (or whichever GUI you use, for that matter). Print them in small letter, pin the print-out upside-down at the wall and look at it from some distance. Now you can see the structure of the code without being distracted from the content.

It's kind of geometrical, isn't it? That's the common patterns of your code shining through. You'll find groups of similar components repeated over and over again. If you don't, you've probably forgotten to format the code nicely. Or you've already read this article :).

What do you do when you find yourself repeating over and over? Yeah, refactoring. In JSF terms this usually means writing a component.

So why don't you just write a JSF 2.0 composite component? They say it's the easiest way to write a JSF component.

Getting started with JSF 2.0 composite components

Basically creating a JSF 2.0 component is just extracting a group of JSF components into a file, giving it a name and calling the group by the name. It's pretty much the same your Java IDE's "extract method" refactoring does.

Plus the XML overhead. First, you have to put the extracted code into an XML file that has to obey a couple of rules. It's a little boilerplate code you'll quickly get used to. Regular readers of BeyondJava.net know I prefer to avoid boilerplate code, but it's not a showstopper, either.

A very useful example might by an equivalent of the AngularFaces <a:body> component. As it happens, I chose to implement it in native Java in AngularFaces, but in this particular case I might have implemented a composite components just as well. I'll take it to another level by adding the <h:form> tag:

Note I use my own naming conventions for both the PrimeFaces library and JSF 2.2 path-through attributes. I prefer prime: and ng:, respectively. The standard conventions for the libraries seem to be p: in both cases.

Store the file in a subfolder of the "resources" folder of your web application. The file name becomes the component name, and the subfolder name becomes the name of your own component library. You can assign a different library shortcut later, as I did with the PrimeFaces library above, but usually, it's better to name them all the same. So your web application now has a file \resources\af\form.xhtml.

Looking at the file you'll notice 34 lines of code, the majority of which are boilerplate code. On the long run, those twenty-three lines of boiler-plate code are negligible. Mind you, that component saves you ten lines of JSF code every time you write an AngularFaces JSF view. Using the component is as simple as can be:

>

You have to add your composite component library to the JSF view's header, and that's it. Your composite components are used exactly the same way Mojarra or PrimeFaces components are used. In our case, you refer to the component by writing <af:body>.

Passing parameters

You can even pass parameters to your composite component, and that's where the trouble begins. At first glance, everything is simple. Parameters are defined in the interface section of the composite component:

To use the parameter value within the composite component you can u se the EL expression

#{cc.attrs.<parameterName>}:

So far, so good. So what?

JSF 2.0 composite component parameters can be tricky. They are not just call-by-name parameters. It took me a while to find out why this snippet doesn't work:

There's nothing wrong with the snippet. But it won't work unless you tell JSF the parameter refers to a Java function. So the interface section has to look like so:

Note that the method signature is just that: a signature. It does not refer to a real function. You have to give the proper types (java.lang.String), but the name of the action doesn't have to be the name of a real method.

The JSF view using the component have to add the brackets to the method parameter. If you omit the brackets, JSF erroneously looks for a getter.

In this particular example, there's an alternative. You can also define the parameter as an actionSource:

Nested expressions

Another caveat are nested expressions. Today I wanted to define a component consisting of two standard buttons. My idea was just to pass the bean name as a parameter. The methods within the bean should have standard names. So I came up with this solution:

No go. This translates to action="myBean.saveAction". That's a fixed name, not an expression language expression calling a bean's method.

The other approach didn't work either.

EL doesn't allow for nested expressions.

As far as I know, there's simply no solution to the problem. I had to choose the workaround: passing each method name individually as a parameter.

Conditional code

It's even possible to use conditions in composite components. The code may be ugly as hell, but it works:

Again, you have to add a library to the composite components header:

The catch

The problem with the c library is it doesn't belong to JSF. It's a JSP library. In most cases, it works, but you should always keep in mind JSP tags are evaluated at a different time than their JSF counterparts. This might lead to all kinds of strange behaviors.

But the real problem with the c library is its verbosity. You can do everything: conditional statements, loops, everything you need. But your code is going to be extremely bloated.

That's the reason why I frequently say "no" when I asked to write a composite component. Sure, composite components are really nifty tools, but they are heavily restricted. They are not meant to create complex components. Complex components - and that's the interesting ones - should be written as traditional, Java-centric JSF components.

Another example that can't be implemented using composite components are labelled input texts. Actually, OIO managed to create a nice labelled input text:

click to see OIO's composite component

(see the article at How to highlight invalid components in JSF)

If you try to put the labels in front of the input field and the error message behind it, you'll inevitably fail. Usually, labels, fields, and error messages are aligned by means of a table-like structure - be it an HTML table, a panel grid or div tags. However, composite components are treated as a single component by JSF. Put a composite component in a panel grid, and you'll find the label, the input field and the error message cramped into the same HTML cell (the same <td>). There's no way to avoid this behavior. Most people get very pensive when I show them the problem ("there has to be a solution!"), but having tried quite a few approaches I'm pretty sure it simply can't be done.

On the other side this kind of components are created very easily by using the older JSF component framework. Actually that's what the AngularFaces InputText does:

@FacesComponent("de.beyondjava.InputText") public class NGInputText extends org.primefaces.component.inputtext.InputText implements SystemEventListener, NGUIComponent { public static final String COMPONENT_FAMILY = "javax.faces.Input"; public NGInputText() { FacesContext context = FacesContext.getCurrentInstance(); UIViewRoot root = context.getViewRoot(); root.subscribeToViewEvent(PreRenderViewEvent.class, this); } private void insertLabelBeforeThisInputField() { OutputLabel l = new OutputLabel(); l.setFor(getId()); l.setValue(getLabel()); List tree = getParent().getChildren(); for (int i = 0; i < tree.size(); i++) { if (tree.get(i) == this) { tree.add(i, l); break; } } } private void insertMessageBehindThisInputField() { NGMessage l = new NGMessage(); l.setFor(getId()); l.setDisplay("text"); l.setTarget(this); List tree = getParent().getChildren(); for (int i = 0; i < tree.size(); i++) { if (tree.get(i) == this) { tree.add(i + 1, l); break; } } } @Override public boolean isListenerForSource(Object source) { return (source instanceof UIViewRoot); } @Override public void processEvent(SystemEvent event) throws AbortProcessingException { if (!FacesContext.getCurrentInstance().isPostback()) { if (!(getParent() instanceof Column)) { insertLabelBeforeThisInputField(); insertMessageBehindThisInputField(); } } } }

Putting it all together

I'm very fond of JSF 2.0's composite component framework. However, almost every time I try to use it it sucks. It's meant to do simple things, but my idea of creating a framework isn't restricted to solving simple problems. I want to solve the hard and nasty problems. I don't want my team to repeat the same errors over and over again if I can help them by providing a framework.

So in most cases I abandon the composite component approach pretty soon. Nonetheless it's a very nifty approach allowing you to create really nice components. Actually, it offers more options than I told you. Have a look at Oracle's documentation to learn about the more exotic parameter types, such as clientBehavior, valueHolder, editableValueHolder, facet, insertFacet and renderFacet. It's astonishing such a well-thought library lacks the flexibility I need so often.


Comments