Writing JSF modules is pretty cumbersome job, with continuous repeating of the similar code: attributes accestors ( with state management ), ResponseWriter calls for html code generation, processing attributes in Tag handlers.
The RichFaces CDK designed to generate that code for any custom modules of JSF framevork: Components, Converters, Validators, Behaviors, Event listeners, Tag handlers, and Configuration files as well. In the ideal case, developer wrote only significant functional code, and lets CDK to take care of rest.
For the JSF component, only two files required: abstract component class and renderer template ( the syntax as close as possible to the JSF composite component ).
Framework takes information for generation from two places: annotated java code and faces-config.xml with cdk-specific extensions.
Cdk uses “coding by convention” to infer default values for omitted options.
CDK expects package and Java class names similar to standard JSF conventions:
It’s recommended to create abstract class that provides only component logic - status changes, validation, model updates, and events broadcast. Any attributes required by that logic can be abstract getters/setters, all necessary code will be generated. If component fires/receives some JSF events, ...Source interface and methods add...Listener, remove...Listener, get...Listener will be also generates, as well as Listener interface, VDL tag, and default implementation. generated UI... class becomes common base for all renderer-specific components, that contain html-related attributes and behavior events. Generated VDL tag links renderer-specific component with JSF renderer.
To simplify CDK code, the only single relation renderer specific Component-Tag-Renderer supported. Any other combinations should be described in faces-config.xml and taglib.xml by hand.
Java class names:
- <prefix>.component.Abstract<Name> - the optional abstract superclass for the JSF component. To avoid manual writing for EL-enabled getters/setters, saveState/restoreState methods, listener-related methods and so on, developer may create that abstract class from which generator should create the implementation.
- <prefix>.component.UI<Name> - Base component class. That class might be generated from an abstract superclass by the CDK or created by the developer. It makes sense to have concrete class that can be created by JSF Application or 'new' operatir in the backing bean.
- <prefix>.component.<markup>.<Markup><RendererName> - renderer-specific generated component. In addition to the base UI Component class it contains Java Bean getter/setter methods for renderer-specific attributes. This class is generated by the CDK.
- <prefix>.<Name> - JSF UI component-type. May be provided in the configuration or calculated from the component class name.
- <prefix>.<Name> - JSF component family. That is common family name for the base UI... component and all renderer-specific component instances. May be provided in the configuration or calculated from the component class name.
- <prefix>.<Markup><RendererName> - renderer-specific JSF component-type. May be provided in the configuration or calculated from the renderer name.
- <prefix>.renderkit.<RendererName>RendererBase - optional Renderer superclass that implements methods required by the generated renderer. Created by the component developer.
- <prefix>.renderkit.<markup>.<RendererName>Renderer – The component renderer. That class might be created by the component developer or generated from template by CDK.
- <prefix>.<RendererName> - JSF renderer-type. Can be provided in the configuration or calculated from the renderer name.
- <prefix>.<markup>.<RendererName>.xml - template for generated renderer class. Provided by the component developer.
- <prefix>.view.jsp.<RendererName>Tag - JSP tag class.
- <prefix>.view.facelets.Abstract<RendererName>Handler - base classes for generated handlers.
- <prefix>.view.facelets.<RendererName>Handler - facelets tag handler class.
- <renderername> - JSP/facelets tag name.
JSF events :
- <prefix>.event.<Event>Event - event class. Provided by the component developer.
- <prefix>.event.<Event>Listener - Event listener interface. Can be generated by CDK
- process<Event> - event processing method name in listener interface.
- <prefix>.event.<Event>Source - Interface for event processing component that contains methods 'add<Event>Listener(<Event>Listener listener)', 'remove<Event>Listener(<Event>Listener listener)'.
- <prefix>.event.<Event>ListenerWrapper - wrapper class that is used to bind listener's EL-expression to user's beans.
- <prefix>.taglib.<Event>ListenerTag - JSP tag class that creates listener instance. Parent component have to implement <Event>Source interface.
- <prefix>.taglib.<Event>ListenerTagHandler - Facelets tag class that creates listener instance.
Definitions that are used in this structure (uppercase character means Java names notation. If the component name is "foo" then <name> means "foo", and <Name> means "Foo"):
<prefix> is a common library name (for example, it is 'javax.faces' for JSF standard components).
<name> is a base component name (for javax.faces.component.UIInput component <name> is "input").
<markup> is a render-kit generated content name, "html" for HTML/XHTML pages, "wml" for mobile content etc.
<rendererName> - is a renderer name for concrete visual component, e.g. "commandButton" , "panelGroup".
<event> is a Faces Event name ("action" for all ActionSource components).
CDK organized as Maven multi-module project. These modules are:
CDK uses its own annotations to provide JSF-related meta-data. Using non-standard annotations has been chosen to avoid interference with JSF annotation scanner. The second reason that CDK annotations can be applied to the abstract class.
All annotations have ‘RetentionPolicy.SOURCE’, so they don’t included into generated classes. Therefore, these annotations are not required at runtime but only for compilation.
Where possible, optional information provided in annotations assigned to the top-level arguments, that lets compiler or IDE editor check these syntax.
Detailed description of all annotations can be found there or in the project javadoc.
JSF requires different code to render HTML attributes. Ones that represents URL’s should be rendered by the ResponseWriter#writeURIAttribute call, others that have associated client-side events should include behavior-generated content, and simple pass-through values rendered direcly by ResponseWriter#writeAttribute with component attribute value - except boolean values, for wich ‘false’ value means the absence of attribute.
To properly generate Java code from the renderer template, CDK parses special extended HTML XML schema and gets instructions how to deal with particular attribute.
Attributes module contains schema parser used to generate Java object and attributes definitions for standard JSF objects.
CDK uses xInclude directives to reuse faces-config fragments. This module forked from the Apache Cocoon 3 source and adapted to use together with JAXB framework.
Generator is the main part of CDK. It parses project sources and generates all files necessary for JSF components: Java classes, faces-config.xml, and taglibs.
It based on the Google uice dependency injection library and can be easy extended to support additional features. Every part of generator configured by the separate Guice module that can be overrided by build tool options.G
Generator class is the main entry point. It gets options from the tool ( Maven plugin, Ant task ), instantiates dependency injection, initializes modules and performs parsing and generation tasks.
The core of CDK is the components library model. it holds all information that necessary to generate CDK output. Every type of information source converted into model objects and merged with the ComponentsLibrary instance. Because there are several sources of information: Java annotations and xml files, each parser just parese its own source and appends information to the main model. Parser should not perform any checks on the model values but only store its own information for future validation. That allows to refine library configuration from additional xml files, that have highest precedence.
Java annotations processor.
JDK 6 AnnotationProcessor#process method is the main execution point. To avoid unnecessary compilation cycles, CDK works with Java models created by JavaCompiler API, after parsing of source files. It allows to use RetentionPolicy.SOURCE annotations ( not visible for reflections ) and integrate CDK into standard Java compilation cycle. The disadvantage of running before java classes compilation is impossibility to use Reflection API. CDK provides JavaSourceUtils class to get necessary information about Java classes using JavaCompiler API.
AnnotationProcessor calls appropriate processors for each type of supported annotations ( org.richfaces.cdk.apt.CdkProcessor instances ), faces-config and renderer template parsers. In the final phase of annotation processing, it calls model validator, and, it there were no errors on previous operations, calls all registered writers to generate source files.
AnnotationProcessor API allows processors to generate new source files and append them to the set of compiled classes. Therefore, generated classes processed by the JavaCompiler in the same call.
XML parser is JAXB-based service that parces faces-config.xml source, or its fragment, and stores information in the Model.
By initial design, Model was supposed to have JAXB annotations and can be directly encoded/decoded into faces-config XML. unfortunately, it was inconvinient for other purposes to repest faces-config schema in the base model, so JAXB module uses its own model ( org.richfaces.cdk.xmlconfig.model ) that represents FacesConfig object and JAXB XmlAdaptors to convert these objects to the base CDK model.
Because Model combines information from the different sources, it doesn’t make sense to check values in the parsers because they would be redefined in the next source. CDK performs all checks for Model consistence in the separate ModelValidator after all sources were processed.
CDK naming conventions described above implemented in the RichFacesConventions class.
For values missed in the model, Validator calls appropriate methods to infer it from the existing information.
Renderer template parser.
Renderer template parser is the JAXB-based xml parser that creates TemplateModel ( org.richfaces.cdk.templatecompiler.model package ), and appends that model to the RendererModel object. Template model realises ‘visitor’ pattern, used for code generation.
EL-expressions in the renderer template parsed by the Jboss EL implementation library, and stored as the Abstract Syntax Tree ( AST ).
Renderer code generator.
CDK creates Renderer source file in the model visitor ( RendererTemplateVisitor ). Visitor creates ‘JavaStatement’ objects ( package org.richfaces.cdk.templatecompiler.statements ) for each element, and appends code generated by statement to the output file. Most of statements use freemaker templates to generate Java code. Also, statement can hold references to its dependecies, used to generate import statements and rendering utility functions. There are two option for utility methods - references to external RendererUtils class ( configured in the package-level annotation TagLibrary or by faces-config extension element ), or generate private methods in the Renderer.
Components Java code generator.
Java sources for all other JSF objects ( Cimponents, Converters, Validators, Behaviors, listeners ) generated from freemaker templates, called with corresponding model object.
faces-config.xml generated by JAXB module, the same used for parsing.
Taglib created by the modelVisitor object. Visitor scans library Model and creates XML tags for all known elements.
CDK uses its own abstract Logger. For each build tool ( Maven, ant ) should be created wrapper for tool specific logger.
Non-fatal errors stored in the Logger object and don’t stop parsing/verificetion procedures, that allows CDK collect as many configuration errors as possible in the single run. If any arror happens during model building, code generation will be skipped.
Maven plugin acts as front-end for the main Generator class. It takes configuration options, creates Generator instance, set build options and runs main build method.
In addition, Maven plugin creates Logger wrapper to send messages from Generator using Maven logging.
The <modules> configuration defines additional Guice ‘Modules’ ( fully classified class names ) that should be processed by Generator before instantiating CDK components, that allows developer to extend or refine any CDK functionality.