If you are interested in following along in your IDE, you can get the code below on github.
The Component Class
To start, we need a component class. The component class itself will be generated by theRichFaces CDK. However we do define a base class from which the generated class will extend. We'll see why this is the case when we get to more complex components. But for now, let's start with this base class:
AbstractHello.java
package ca.bleathem.richfaces.hello.component; import org.richfaces.cdk.annotations.*; @JsfComponent( type = "ca.bleathem.richfaces.hello.Hello", family = "ca.bleathem.text", renderer = @JsfRenderer(type = "ca.bleathem.hello"), tag = @Tag(name="hello")) abstract public class AbstractHello extends javax.faces.component.UIComponentBase { }
Here we have an abstract class annotated with @JsfComponent. This annotation marks the class as a CDK class, and will be consumed by the CDK pre-processor. The CDK uses the attributes of the annotation to construct the necessary classes, and the necessary xml configuration. Creating JSF components is verbose, but we don't have to worry about the verbosity - the CDK takes care of it for us!
Let's look more closely at the @JsfComponent annotation, and the meaning of each of it's attributes. I'll start by pointing out that the annotation supports many more attributes, but we are only covering here the bare minimum required to get a simple hello world component created.
- type
- the value of the type attribute is how JSF identifies out component. It will be used in the generated faces-config file, and in the generated xml taglib file.
- family
- the value of the family attribute is used by JSF to recognize "like" components. We'll get into details of this in future posts. This attribute is in fact optional, and the value can be generated by naming conventions. However, I prefer to specify it explicitly.
- renderer
- the value of the renderer attribute is used to match our component to an appropriate renderer. We'll create the renderer next!
- tag
- the value of the tag attribute will be the actual facelet tag that we use in our facelet files.
The last aspect of out component class that I want to point out is the "extends UIComponentBase" part. Every JSF UI component must extend this class, or one of it's descendants. We'll be able to save ourselves alot of work when we get to more complex components, by judiciously choosing the right class to extend.
The Renderer
Next, let's look at the JSF Renderer:
hello.template.xml
<?xml version="1.0" encoding="UTF-8"?> <cdk:root xmlns="http://jboss.org/schema/richfaces/cdk/xhtml-el" xmlns:cdk="http://jboss.org/schema/richfaces/cdk/core" xmlns:c="http://jboss.org/schema/richfaces/cdk/jstl/core" xmlns:cc="http://jboss.org/schema/richfaces/cdk/jsf/composite" xmlns:javaee="http://java.sun.com/xml/ns/javaee"> <cc:interface> <cdk:class>ca.bleathem.richfaces.hello.renderkit.HelloRenderer</cdk:class> <cdk:superclass>javax.faces.render.Renderer</cdk:superclass> <cdk:renderer-type>ca.bleathem.hello</cdk:renderer-type> </cc:interface> <cc:implementation> Hello World! (from a RichFaces CDK component) </cc:implementation> </cdk:root>
If you are familiar with JSF Component development, you may be surprised to see that our Renderer is not a java class. On the other hand, you may find that it looks quite similar to a JSF 2 composite component. It has a "<cc:interface>" section, and "<cc:implementation>" section, both of which are analogous to the facelet file of a composite component. In fact, you can prototype your components as composite components, then migrate to CDK components when you find you need the extra flexibility the CDK provides.
As we did for the component class, let's dissect the cdk tags found in our renderer template.
- <cdk:class>
- this is the name of the JSF Renderer class that will generated.
- <cdk:superclass>
- the class that will be extended by our generated JSF Renderer class. Here we are explicitly stating the default for reference when we get into more complex components.
- <cdk:renderer-type>
- this is how JSF identifies our Renderer. It will be used in the generated faces-config file, and in the generated xml taglib file.
And there we have it, a JSF Component, and it's associated Renderer! -- err almost. There is one final piece missing. We need to specify a name space for our component - the namespace that will be used on facelet pages that refer to our components. Fortunately, the CDK also makes this easy for us. All we need is a package-info.java in the java package of our component:
package-info.java
@TagLibrary(uri="http://bleathem.ca/test",shortName="testLib") package ca.bleathem.richfaces.hello.component; import org.richfaces.cdk.annotations.TagLibrary;
Here you can see the @TagLibrary annotation that specifies the namespace of our component.
Now we can build a jar, include it in our application, and use our hello world component! Go ahead and run the build. If you are curious, take a look in the target/generated-sources folder. There you'll see all the JSF classes and xml files that the CDK generated for us. Those are all classes and xml files we would have had to write ourselves if we weren't using the CDK.
Well, a hello world JSF component - not really useful... but there is more coming! The next entry in this series will use the CDK to create an input component, and from there we'll get into some more sophisticated components!