My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

.

.

.

.

.

.

.

.


[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians

Twitter

joi, 25 februarie 2016

JSF linking together components, renderers and render kits

Introduction

In order to understand what a render kit is we have to be aware of some major notions that are very important in this context. For a better understanding please check out the below picture and identify the notions described here, and the relationships between them. The subject of this picture is the OmniFaces DeferredScript component, but don't worry, you don't have to understand that component. The role and functionality of this component is not our goal. We use it because it is a professional approach for writing custom components and sustains the topic of this article by exposing best practices of accomplishing such tasks.


A JSF component is annotated with @FacesComponent and it is characterized by three coordinates (we won't take here into account the declarative approach):

component-family -This is a piece of information that groups more components under the same family/category (e.g. ScriptFamily - a family of components that deals with scripts). Typically, a family of components are logically related, but there is no written rule. Nevertheless, a family name can be represented by the package name that holds the classes of the related components (e.g. org.omnifaces.component.script). It is a common practice that the classes of the components that are related to be placed in the same package, so the package name can be considered a family. But, again, there is no rule to sustain this practice. In order to expose into public is family a component will override the getFamily() method. Since JSF 2.2, the component-type can be omitted in @FacesComponent, because JSF will determine it like this (ComponentConfigHandler class):

...
String value = ((FacesComponent) annotation).value();
if (null == value || 0 == value.length()) {
    value = target.getSimpleName();
    value = Character.toLowerCase(value.charAt(0)) + value.substring(1);
}
...

component-type - This is a piece of information that uniquely identifies a component and can be used as the argument of the Application.createComponent(java.lang.String) method for creating instances of the UIComponent class. JSF uses the component-type for creating components. Typically a component type will be the fully qualified named of the component class (e.g. org.omnifaces.component.script.DeferredScript). There is a common practice to define the component-type as a static final string directly in component class and to name it COMPONENT_TYPE. Some developers tend to place the component-type string directly in annotation, which somehow restricts the programmatic access to this information, since, by default, there is no public UIComponent.getComponentType() method to override. Components of different types can be grouped in families.

renderer-type - This is a piece of information that uniquely identifies a renderer (e.g. org.omnifaces.DeferredScript). A component will used its setRendererType() method to point to the render-type that should render this component. Components can call this method as setRendererType(null) to point out that they will render themselves. But, by delegating the rendering to a separate renderer, the component makes itself more versatile because multiple renderers would be able to render it to different clients.

If you extend UIInput, you will inherit its type and family, but if you extend UIComponentBase, then you need to explicitly provide the component-type and component-family.

A Renderer is not selected based on the component-type and renderer-type! Is selected based on component-family and renderer-type, which allows a renderer to be used for multiple components in the same family. The component-type is used for creating components in view root!

Overview of Renderer

What is the main goal of a renderer ?
Is responsible to decode the values from the incoming request and to encode the values to be displayed by transforming the component tree into the HTML markup that will be displayed to the client machine. Shortly, to transform a JSF component in markup (e.g. HTML, XML, etc).

When you commonly need a custom renderer ?
When you need to render a custom component (brand new or extension of a built-in one), because none of the built-ins do what you want to achieve.

When you want to alter the look/functionality of an built-in component.

What should I know before start writing a custom renderer ?
Mainly you need to know that a renderer extends directly/indirectly the Renderer class.
Starting with JSF 2.2, you can extend RendererWrapper, which is a simple implementation of Renderer. JSF 2.2 comes with many wrappers, which are simple implementations of what they are wrapping, and they help developer to override only the necessary methods, and to provide specialized behavior to an existing wrapped instance (e.g. RendererWrapper can be used for providing specialized behavior to an existing Renderer instance). The wrapped instance is available via the getWrapped() method.

The main three methods of a Renderer are encodeBegin(), encodeChildren() and encodeEnd(). By default, JSF calls them in this order, and the first one usually renders the beginning of the markup - "open" tags (e.g. <head>, <input>, <form>, etc), the second one  renders the children (this is configurable via getRendersChildren() flag), and the last one is ending the markup - "close" tags (e.g. </head>, </input>, </form>).

In order to link a component with a renderer, you should know how to work with the UIComponent.setRenderType() method and with the component-family, component-type and renderer-type artifacts as level of annotations or with the <render-kit>  and <renderer> tags in faces-config.xml.

How do I usually write a Renderer skeleton ?
•when you write a brand new component (extending UIComponentBase), you will extend the Renderer class directly and override most of its methods. Usually in these cases you will link the custom component with the renderer via annotations and setRendererType() method.


More examples:

Create and render a brand new component (source code).

When you write a custom component which extends a built-in component, you usually extend the renderer of the built-in component also - directly (most probably) or indirectly. Usually in these cases you will link the custom component with the renderer via the setRenderType() method. Of course, you can also use the renderer of the built-in component without any modifications (source code).
When you just want to alter a built-in component at rendering level, you usually extend the built-in renderer and you instruct JSF to use your renderer instead of the default one via faces-config.xml, <renderer> tag. E.g. use a custom Renderer for the JSF UIOutput component (source code). In order to run this application you have to keep in mind that we are extending a Mojarra renderer (com.sun.faces.renderkit.html_basic.TextRenderer), so you need to:

- manually install the JSF 2.2 JAR so your IDE will find the TextRenderer.
- in pom.xml, the entry for JSF 2.2 should have its scope set to provided, as it shouldn't be copied into the deployment.

Overview of RenderKit

What is the main goal of a RenderKit ?
While the Renderer class converts the internal representation of UI components into markup (e.g. HTML), RenderKit represents a collection of Renderer instances capable to render JSF UI component's instances for a specific client (for example, a specific device). Each time JSF needs to render a UI component, it will call the RenderKit.getRenderer() method which is capable of returning an instance of the corresponding renderer based on two arguments that uniquely identifies it: the component-family and the renderer-type. Moreover, RenderKit registers renderers via RenderKit.addRenderer() method based on the component-family, renderer-type and Renderer instance. When we write a correct renderer (respecting the JSF specification) JSF will automatically find it and register it for us.

When you commonly need a custom RenderKit ?
You may use a custom RenderKit to instruct JSF to delegate renderers in a specific approach. Per example, a custom RenderKit can choose the right renderer depending on device (PC, tablet, iPhone, etc). Or, you may have a custom Renderer that extends the RendererWrapper, and use a custom RenderKit to pass an instance of an existing Renderer to the custom Renderer.

What should I know before start writing a custom RenderKit ?
Mainly, you need to know that a custom RenderKit extends directly/indirectly the RenderKit class.

Starting with JSF 2.0, you can extend RenderKitWrapper, which is a simple implementation of RenderKit. Via RenderKitWrapper, you can provide a specialized behavior to an existing RenderKit instance. The wrapped instance is available via the getWrapped() method.

The main two methods of a RenderKit are addRenderer() and getRenderer(). By overriding these methods, you can take control over the Renderers registration and delegation. Of course, there are many other useful methods listed in documentation.

How do I usually write a RenderKit skeleton ?
Usually, you will extend the RenderKitWrapper class, override the necessary methods, and configure it in the faces-config.xml via <render-kit> tag.

So, as you can see in figure below, the RenderKit sits between components and renderers and act as a conductor:

Now, we know that each component is rendered after its component-family and renderer-type passes through the RenderKit.getRenderer() method:

public abstract Renderer getRenderer(java.lang.String family,
                                     java.lang.String rendererType)

This method match the correct renderer , like this:

private ConcurrentHashMap<String, HashMap<String, Renderer>> rendererFamilies =
  new ConcurrentHashMap<String, HashMap<String, Renderer>>();
...
HashMap<String,Renderer> renderers = rendererFamilies.get(family);
return ((renderers != null) ? renderers.get(rendererType) : null);

So, in order to obtain its renderer, each component must reveal its family (COMPONENT_FAMILY) and renderer-type to this method (with a simple custom RenderKit you can check out the JSF/OmniFaces components families and renderer types). Programmatically, a family, component-family, is obtained via UIComponent.getFamily(), and the renderer-type via UIComponent.getRendererType():

public abstract java.lang.String getFamily()
public abstract java.lang.String getRendererType()

Now, JSF search through available renderers that was added via RenderKit.addRenderer().  JSF has inspected faces-config.xml file for:

<render-kit>
    <renderer>
        <component-family>component-family</component-family>
        <renderer-type>renderer-type</renderer-type>
        <renderer-class>RendererClass</renderer-class>
    </renderer>
</render-kit>

 and all classes annotated with @FacesRenderer:

@FacesRenderer(componentFamily=ComponentClass.COMPONENT_FAMILY, rendererType= RendererClass.RENDERER_TYPE)
public class RendererClass extends Renderer {
 public static final String RENDERER_TYPE = "renderer-type";
 ...
}

Optionally, Facelets can be also informed by the render type in *taglib.xml. When you do that, you instruct Facelets to create a component of the given component-type. The component class is annotated with @FacesComponent or has been defined in faces-config.xml. In addition Facelets will set to the given renderer type.

<tag>
 ...
 <component>
  <component-type>component-type</component-type>
  <renderer-type>renderer-type</renderer-type>
 </component>
 ...
</tag>

Ok, so now let's have several examples of custom RenderKits:

Log how JSF renderers are added/delegated by JSF (source code).

Instruct JSF to render all components of a family via a common custom renderer (we simply apply a common CSS style to all components from javax.faces.Input family) (source code).

Instruct JSF to render UIOutput components via a custom renderer that was registered for other type of components, but pass to it an instance of the original renderer (source code).

Overview of a RendererKitFactory

What is the main goal of a RenderKitFactory ?
It manages (register/provide) instances of available RenderKits.

When you commonly need a custom RenderKitFactory ?
You may use a custom RenderKitFactory to instruct JSF to delegate RenderKits in a specific approach. By default, JSF has a single RenderKit, identified by an ID, HTML_BASIC_RENDER_KIT. But, if want to write another RenderKit then you can programmatically choose between them using a custom RenderKitFactory.

What should I know before start writing a custom RenderKitFactory ?
Mainly, you need to know that a render kit factory extends directly/indirectly the RendererKitFactory class.

The main two methods of a RenderKitFactory are addRenderKit() and getRenderKit() . By overriding these methods, you can take control over the RenderKits registration and delegation. The render kits IDs can be obtained via getRenderKitIds() method. If this factory has been decorated, the implementation doing the decorating may override the getWrapped() method to provide access to the implementation being wrapped.

In order to link a component with a renderer, you should know how to work with the UIComponent.setRenderType() method and with the component-family, component-type and renderer-type artifacts as level of annotations or with the <render-kit>  and <renderer> tag in faces-config.xml.

How do I usually write a RenderKitFactory skeleton ?
Usually, you will extend the RenderKitFactory class, override the necessary methods, and configure it in the faces-config.xml via <render-kit-factory> tag.

For example, replace the default JSF RenderKit with the DummyRenderKit (source code).

Niciun comentariu :

Trimiteți un comentariu

JSF BOOKS COLLECTION

Postări populare

Visitors Starting 4 September 2015

Locations of Site Visitors