Alexander Smirnov’s personal Weblog

April 6, 2011

RichFaces CDK – how to create JSF component

Filed under: Java, Java server faces — alexsmirnov @ 3:48 pm
JSF component if pretty complicated beast. In the worst case, you have to create UIComponent class, with a lot of attributes with special getter’s and setters that interacts with StateHelper,  renderer-specific class that contains similar attributes for html-related parameters ( even worse, they require special code if you wish to use attributes access optimization in Renderer ) and defines JSF Behavior events for them, create Renderer class that generates html code using spaghetti of startElement/writeAttribute/writeText/endElement calls, describe these classes in the faces-config.xml, create Facelets taglib.xml to define component as VDL tag, write TagHandler class. If your component fires FacesEvents for some listeners, you also need Listener interface and listener TagHandler with default ListenerWrapper.
Similar work required for the ClientBehavior, except renderer-specific implementation.
Faces Converters and Validators a little bit simple because they don’t require separate renderers and cannot broadcast events.
RichFaces uses special Component Development Kit ( CDK ) that takes rid for most of these tasks and lets developers to concentrate on component functionality only.
In the best case, developer has to only create abstract component class, with functional code only, and Renderer template that describes generated Html with Facelets-like syntax. Everything else generated by the tool.

Define Java Package as Components Library

By default, all components in the same projects included to the same library. Developer can provide package-level annotation to define library for all components which belong to that package
package-info.java:
@TagLibrary(uri=”http://richfaces.org/test”,shortName=”testLib”)
package org.richfaces.cdk.test.component;
import org.richfaces.cdk.annotations.TagLibrary;

Abstract Component Class

Most JSF components contains only a few lines of functional code. With cdk, developer can only create java class with such functionality and lets boilerplate to be generated.

Base Class

The base class can be abstract if it need references to generated methods. That class should be annotated with @JsfComponent annotation that describes generation details:
@JsfComponent(type = “foo.Bar”,
family=”foo.Bar”,
description=@Description(displayName=”Bar Component”,
largeIcon=”large.gif”,
smallIcon=”spall.png”),
generate=”foo.component.UIBar”,
facets=@Facet(name=”caption”,
generate=true,
description=@Description(“Caption Facet”)),
fires={@Event(FooEvent.class),
@Event(value=BarEvent.class,listener=BarListener.class)},
interfaces=Ext.class,         tag=@Tag(name=”abbr”,generate=true,handler=”foo.facelets.barHandler”),
attributes={“core-props.xml”,
“events-props.xml”,
“i18n-props.xml”},
renderer=@JsfRenderer(type=”foo.HtmlBarRenderer”)),
)
public abstract class AbstractBarComponent extends UIComponentBase

For type-safe definition, optional annotations defined as @JsfComponent attributes, so Java compoler can check their usage and code competition available in modern editors. All annotation attributes are optional and can be inferred by CDK:

  • type() defines JSF component type, used as key when framework creates component instance. Its value can be also defined by public constant COMPONENT_TYPE or inferred from the class name by Naming Conventions#.
  • family() defines component family, common for the group of similar components ( for example, UICommand, HtmlCommandButton and HtmlCommandLink all share “javax.faces.Command” family ).
  • description() defines optional description of component, used by JSF tools and documentation generator. Full description comes from the class JavaDoc comment or can be defined by value() attribute.
  • generate() defines the fully qualified name for concrete component class. There are two options for default value of that attribute. If @JsfAnnotation applied to the abstract class, the name of generated class will be inferred from Naming Conventions. Otherwise, if annotated class is concrete, CDK will not generate concrete component class and use annotated class as component implementation.
  • facets()  defines component facets. It’s array of @Facet annotations. Facet#generate() attribute tells CDK to generate getter and setter for facet.
  • fires() is array of @Event annotations which define JSF events fired by components. It defines FacesEvent class for event, listener and source interface. For each event, CDK generates add/remove/get<Event>Listener methods defined by the source interface, creates eventListener tag handler and creates listener instance that can be binded to EL-expression.
  • tag() is array of @Tag annotations. Using multiply tags for single component is necessary to define tags for different VDL ( View Description Language ), or make aliases. Tag links JSF component and its renderer. If no tag defined for component, but there is Renderer associated with component, Facelets tag will be generated. Tag name, handler class and library will be inferred.
  • attributes() contains array of strings, each contains the name of faces-config.xml fragment with attributes definitions, that lets CDK to reuse attributes definition in different components. CDK looks for these files in project classpath META-INF/cdk/attributes folder, and provides set of such files for the most html elements and standard components.
  • renderer() associates component with Renderer implementation. There are two options to link component with renderer#: by the renderer type or by the template name. For type, CDK doesn’t check existence of the target renderer, because it would be defined in another module. For template name, CDK enforces both renderer and component to share the same renderer type and family.
  • interfaces() attribute contains array of Java interface classes that should be implemented by generated component, that provides another way to reuse attributes definitions in different components, similar to ‘attributes()’ option. That’s preferred method for user attributes, because it allows type-safe check. The result is the same as including these interfaces in ‘implements’ keyword for component class, but it does not enforce component class to be abstract, and can be used to define interfaces implemented by the renderer-specific components ( see late ).

Renderer Specific Components

In JSF, the single component can have more then one renderer, to allow different client-side representations of the same functionality. For example, base command UICommand component can be rendered as html button ( <input> element ) or link ( <a> element ). Because these representations can have different attributes and ClientBehavior events, JSR best practices recomment to have renderer-specific component that contains bean attributes for specific implementation. CDK follows that structure and lets developer to define renderer-specific components in the base class using @JsfComponent#components() attribute.
This attribute contains array of @RendererSpecificComponents annotations which have the same set of attributes as @JsfComponent, except the family() that should be shared by all components. These annotations lets to define third level of generated classes, specific to the particular renderer.

Define Attributes

JSF component attributes not are plain bean getters and setters, they can contains code to evaluate EL-expressions, and to persist value in the View state. Also, some attributes can be associated with ClientBehavior events. CDK generates all code to make attributes compatible with JSF spec.
To define component attribute, developer can annotate class field or getter method with @Attribute annotation. The method can be abstract, that allows to use that annotation in the interfaces or abstract classes:
@Attribute(
aliases={@Alias(“getAction”)},
defaultValue=”null”,
description=@Description(),
events={@EventName(value=”click”,defaultEvent=true)},
hidden=false,
literal=false,
passThrough=false,
readOnly=true,
required=false,
generate=true,
suggestedValue=”#{foo}”,
signature = @Signature(parameters = {String.class},
returnType = Boolean.class))
public abstract MethodExpression getMethodExpression();
Same as for the @JsfComponent, all annotation attributes are optional.

  • aliases() lets to define different names for the single attribute. In the example above, CDK will generate public MethodExpression getAction(){ return getMethodExpression();}
  • defaultValue() should contain valid Java expression evaluated to the default attribute value.
  • description() contains attribute description for IDE and documentation, the same annotation used elsewhere in CDK. By default, CDK uses method JavaDoc comment to generate description.
  • events() contains array of ClientBehavior events definition. The value of EventName annotation is name itself, and defaultEvent defines marks event as component default#.
  • hidden() attribute tells CDK to remove attribute from tag.
  • literal() disables EL- expressions for attribute.
  • passThrough() defines attribute to be rendered as html attribute without conversion. Attributes with ClientBehavior events always threated as passed through.
  • readOnly disables generation for the setter method.
  • requires() enforces tag handler to check what developer provided explicit value for that attribute.
  • geterate() enforces CDK to generate getter and setter methods for attribute, even if concrete method already exist. If omitted, CDK checks ‘abstract’ modifier for getter and setter methods and generate implementation for abstract methods only.
  • suggestedValue can be used in tools for code competition.
  • signature() only used for attributes with MethodExpression type to define signature of the target method.

Define Facets

Component facets can be defined in the two ways: in the facets() attribute of component annotations ( described above ) or by the getter method @Facet annotation:
@Facet(generate=true,
description=@Description())
public abstract UIComponent getHeader();
The ‘name()’ attribute is optional in this case, CDK uses bean attribute name instead. Same as for the attribute, it uses JavaDoc comment to generate description and generate implementation for abstract methods.

Advertisement

1 Comment »

  1. thank you

    Comment by خرید vpn — December 17, 2014 @ 3:52 am


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: