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

miercuri, 30 septembrie 2015

JSF and Strategy design pattern - part III (JSF implementation)

Read also:

When we are talking about the strategy pattern in JSF source code, we need to focus on the rendering mechanism. Basically, the rendering mechanism 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. JSF supports two kinds of rendering models:

·         direct implementation model -UI components implementation is also responsible to render themselves;
·         delegated implementation model - UI components implementation delegates the rendering process to a separate renderer associated with the component.

The second approach (delegated implementation model) is our centerpiece. For example, check out the JSF component UISelectBoolean source code. Almost immediately you can notice that this component has a rendered identified as javax.faces.Checkbox - the setRendererType() method instructs JSF which renderer should be associated with the current component; when we pass null, it instruct JSF that the component belongs to direct implementation model:

// Mojarra 2.2.9 source code -  javax.faces.component.UISelectBoolean
public class UISelectBoolean extends UIInput {

 public static final String COMPONENT_TYPE = "javax.faces.SelectBoolean";
 public static final String COMPONENT_FAMILY = "javax.faces.SelectBoolean";

 public UISelectBoolean() {
  super();
  setRendererType("javax.faces.Checkbox");
 }
 ...
}

Further, the renderer associated with this component is named, CheckboxRenderer:

// Mojarra 2.2.9 source code - com.sun.faces.renderkit.html_basic.CheckboxRenderer
public class CheckboxRenderer extends HtmlBasicInputRenderer {
 ...
 @Override
 public void decode(FacesContext context, UIComponent component) {
  ...
 }

 @Override
 public void encodeBegin(FacesContext context, UIComponent component)
  ...
 }
 ...
}

Well, this model leverages the strategy design pattern. Mainly, a renderer can be seen as an algorithm specialized in rendering a component. But, JSF allows us to have (register) multiple such algorithms for a single component (multiple renderers) and to dynamically vary the algorithm. Multiple renderers are registered via JSF, RenderKit. At application startup time, JSF  implementation parses the configuration file (faces-config.xml) and associates those renderers to the UI components.

For a better understanding, let's see how we can follow the strategy pattern and the JSF API constraints to write custom renderers.

Write Custom Renderer Skeleton

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 thegetWrapped() 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 viagetRendersChildren() 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. A detailed dissertation about them is the OmniFacesCustom Components Approach post. OmniFaces provides solid techniques for writing custom components and renderers!

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.

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

DummyComponent 
@FacesComponent(value = DummyComponent.COMPONENT_TYPE, createTag = true)
public class DummyComponent extends UIComponentBase {

 public static final String COMPONENT_FAMILY = "jsf.components";
 public static final String COMPONENT_TYPE = "jsf.components.DummyComponent";

 public DummyComponent() {
  setRendererType(DummyRenderer.RENDERER_TYPE);
 }

 @Override
 public String getFamily() {
  return COMPONENT_FAMILY;
 }
}

DummyRenderer
@FacesRenderer(componentFamily = DummyComponent.COMPONENT_FAMILY, rendererType = DummyRenderer.RENDERER_TYPE)
public class DummyRenderer extends Renderer {

 public static final String RENDERER_TYPE = "jsf.renderers.DummyRenderer";
 private static final Logger LOG = Logger.getLogger(DummyRenderer.class.getName());

 public DummyRenderer(){       
 }
   
 @Override
 public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException {
  return super.getConvertedValue(context, component, submittedValue);
 }

 @Override
 public boolean getRendersChildren() {
  return super.getRendersChildren();
 }

 @Override
 public String convertClientId(FacesContext context, String clientId) {
  return super.convertClientId(context, clientId);
 }   

 @Override
 public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
  LOG.info("--- Rendering DummyComponent - encodeBegin() method ---");
  super.encodeBegin(context, component);
 }
   
 @Override
 public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
  LOG.info("--- Rendering DummyComponent - encodeChildren() method ---");
  super.encodeChildren(context, component);
 }
   
 @Override
 public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
  LOG.info("--- Rendering DummyComponent - encodeEnd() method ---");
  super.encodeEnd(context, component);
 } 

 @Override
 public void decode(FacesContext context, UIComponent component) {
  LOG.info("--- Rendering DummyComponent - decode() method ---");
  super.decode(context, component);
 }
}

·         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.

e.g. Create and render a component that extends the UIOutput (source code

DummyComponent
@FacesComponent(createTag = true)
public class DummyComponent extends UIOutput { 
   
 // inherits the UIOutput component-family: javax.faces.Output
 //default renderer-type: javax.faces.Text is replaced by DummyRenderer
   
 public DummyComponent() {
  setRendererType(DummyRenderer.RENDERER_TYPE);        
 }       
}

DummyRenderer
@FacesRenderer(componentFamily = DummyComponent.COMPONENT_FAMILY, rendererType = DummyRenderer.RENDERER_TYPE)
public class DummyRenderer extends TextRenderer {

 private static final Logger LOG = Logger.getLogger(DummyRenderer.class.getName());
 public static final String RENDERER_TYPE = "jsf.renderers.DummyRenderer";

 public DummyRenderer(){          
 }

 @Override
 public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
  LOG.info("--- Rendering DummyComponent - encodeChildren() method ---");
  super.encodeChildren(context, component); // calls the TextRenderer#encodeChildren()
 }

 @Override
 public boolean getRendersChildren() {
  LOG.info("--- Rendering DummyComponent - getRendersChildren() method ---");
  return super.getRendersChildren(); // calls the TextRenderer#getRendersChildren()
 }

 @Override
 protected void getEndTextToRender(FacesContext context, UIComponent component, String currentValue) throws IOException {
  LOG.info("--- Rendering DummyComponent - getEndTextToRender() method ---");
  super.getEndTextToRender(context, component, currentValue); // calls the TextRenderer#getEndTextToRender()
 }

 @Override
 public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
  LOG.info("--- Rendering DummyComponent - encodeBegin() method ---");
  super.encodeBegin(context, component); // calls the TextRenderer#encodeBegin()
 }       
}

·         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)

DummyRenderer
public class DummyRenderer extends TextRenderer {

 private static final Logger LOG = Logger.getLogger(DummyRenderer.class.getName());
 public static final String RENDERER_TYPE = "jsf.renderers.DummyRenderer";

 public DummyRenderer(){           
 }

 @Override
 public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
  LOG.info("--- Rendering DummyComponent - encodeChildren() method ---");
  super.encodeChildren(context, component); // calls the TextRenderer#encodeChildren()
 }

 @Override
 public boolean getRendersChildren() {
  LOG.info("--- Rendering DummyComponent - getRendersChildren() method ---");
  return super.getRendersChildren(); // calls the TextRenderer#getRendersChildren()
 }

 @Override
 protected void getEndTextToRender(FacesContext context, UIComponent component, String currentValue) throws IOException {
  LOG.info("--- Rendering DummyComponent - getEndTextToRender() method ---");
  super.getEndTextToRender(context, component, currentValue); // calls the TextRenderer#getEndTextToRender()
 }

 @Override
 public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
  LOG.info("--- Rendering DummyComponent - encodeBegin() method ---");
  super.encodeBegin(context, component); // calls the TextRenderer#encodeBegin()
 }       
}

faces-config.xml
...
<application>
 <render-kit>           
  <renderer>
   <component-family>javax.faces.Output</component-family>
   <renderer-type>javax.faces.Text</renderer-type>
   <renderer-class>jsf.renderers.DummyRenderer</renderer-class>
  </renderer>
 </render-kit>
</application>
...

Write Custom RenderKit Skeleton

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. Or, you can write a custom RenderKit to add support for HTML5 specific attributes, like the OmniFaces Html5RenderKit (nevertheless, this will be removed starting with OmniFaces 3.0).

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

e.g. log how JSF renderers are added/delegated by JSF (source code)

DummyRenderKit
public class DummyRenderKit extends RenderKitWrapper {

 private static final Logger LOG = Logger.getLogger(DummyRenderKit.class.getName());
 private RenderKit renderKit;

 public DummyRenderKit() {
 }

 public DummyRenderKit(RenderKit renderKit) {
  LOG.log(Level.INFO, "Default RenderKit instance provided by JSF: {0}", renderKit.getClass().getSimpleName());
  this.renderKit = renderKit;
 }

 @Override
 public Renderer getRenderer(String family, String rendererType) {
  LOG.log(Level.INFO, "--- Delegating renderer of type {0} for component in family {1} ---", new Object[]{rendererType, family});
  return getWrapped().getRenderer(family, rendererType);
 }

 @Override
 public void addRenderer(String family, String rendererType, Renderer renderer) {
  LOG.log(Level.INFO, "--- Adding the renderer of type {0} for component in family {1} ---", new Object[]{rendererType, family});
  getWrapped().addRenderer(family, rendererType, renderer);
 }

 @Override
 public RenderKit getWrapped() {
  return renderKit;
 }
}

faces-config.xml
...
<render-kit>
 <render-kit-class>
  jsf.renderkits.DummyRenderKit
 </render-kit-class>
</render-kit>
...

e.g. instruct JSF to render all components of a family via a common custom renderer (we simply apply a common CSS style to all components fromjavax.faces.Input family) (source code)

DummyRenderer
@ResourceDependencies({
 @ResourceDependency(name = "css/styles.css", library = "default", target = "head")
})
@FacesRenderer(componentFamily = "javax.faces.all.Input", rendererType = DummyRenderer.RENDERER_TYPE)
public class DummyRenderer extends RendererWrapper {

 private static final Logger LOG = Logger.getLogger(DummyRenderer.class.getName());
 public static final String RENDERER_TYPE = "javax.faces.all";
 private Renderer renderer;

 public DummyRenderer() {
 }

 public DummyRenderer(Renderer renderer) {
  this.renderer = renderer;
 }

 @Override
 public boolean getRendersChildren() {
  LOG.info("--- Rendering DummyComponent - getRendersChildren() method ---");
  return getWrapped().getRendersChildren(); // calls the wrapped#getRendersChildren()
 }

 @Override
 public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
  LOG.info("--- Rendering DummyComponent - encodeBegin() method ---");
  getWrapped().encodeBegin(context, component); // calls the wrapped#encodeBegin()        
 }

 @Override
 public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
  LOG.info("--- Rendering DummyComponent - encodeChildren() method ---");
  getWrapped().encodeChildren(context, component); // calls the wrapped#encodeChildren()
 }

 @Override
 public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
  LOG.info("--- Rendering DummyComponent - encodeEnd() method [adding the common CSS style] ---");
  ResponseWriter responseWriter = context.getResponseWriter();
  responseWriter.writeAttribute("class", "inputs", "class");
  getWrapped().encodeEnd(context, component); // calls the wrapped#encodeChildren()
 }

 @Override
 public Renderer getWrapped() {
  return renderer;
 }
}

DummyRenderKit
public class DummyRenderKit extends RenderKitWrapper {

 private static final Logger LOG = Logger.getLogger(DummyRenderKit.class.getName());
 private RenderKit renderKit;

 public DummyRenderKit() {
 }

 public DummyRenderKit(RenderKit renderKit) {
  LOG.log(Level.INFO, "Default RenderKit instance provided by JSF: {0}", renderKit.getClass().getSimpleName());
  this.renderKit = renderKit;
 }   
   
 @Override
 public Renderer getRenderer(String family, String rendererType) {       
  // instruct JSF to use the DummyRenderer for each component that belongs to the "javax.faces.Input" family
  if (family.equals("javax.faces.Input")) {
      LOG.log(Level.INFO, "--- Delegating renderer of type {0} for component in family {1} ---", new Object[]{rendererType, family});
      Renderer renderer = getWrapped().getRenderer(family, rendererType);
      return new DummyRenderer(renderer);
  }
  return getWrapped().getRenderer(family, rendererType);
 }
   
 @Override
 public RenderKit getWrapped() {
  return renderKit;
 }
}

e.g. 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)

DummyRenderer
@FacesRenderer(componentFamily = "javax.faces.cool.Output", rendererType = DummyRenderer.RENDERER_TYPE)
public class DummyRenderer extends RendererWrapper {

 private static final Logger LOG = Logger.getLogger(DummyRenderer.class.getName());
 public static final String RENDERER_TYPE = "javax.faces.cool.Text";
 private Renderer renderer;

 public DummyRenderer() {
 }

 public DummyRenderer(Renderer renderer) {
  this.renderer = renderer;
 }

 @Override
 public boolean getRendersChildren() {
  LOG.info("--- Rendering DummyComponent - getRendersChildren() method ---");
  return getWrapped().getRendersChildren(); // calls the TextRenderer#getRendersChildren()
 }

 @Override
 public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
  LOG.info("--- Rendering DummyComponent - encodeBegin() method ---");
  getWrapped().encodeBegin(context, component); // calls the TextRenderer#encodeBegin()
 }
   
 @Override
 public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
  LOG.info("--- Rendering DummyComponent - encodeChildren() method ---");
  getWrapped().encodeChildren(context, component); // calls the TextRenderer#encodeChildren()
 }

 @Override
 public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
  LOG.info("--- Rendering DummyComponent - encodeEnd() method ---");
  getWrapped().encodeEnd(context, component); // calls the TextRenderer#encodeEnd()
 }

 @Override
 public Renderer getWrapped() {       
  return renderer;
 }
}

DummyRenderKit
public class DummyRenderKit extends RenderKitWrapper {

 private static final Logger LOG = Logger.getLogger(DummyRenderKit.class.getName());
 private RenderKit renderKit;

 public DummyRenderKit() {
 }

 public DummyRenderKit(RenderKit renderKit) {
  LOG.log(Level.INFO, "Default RenderKit instance provided by JSF: {0}", renderKit.getClass().getSimpleName());
  this.renderKit = renderKit;
 }

 @Override
 public void addRenderer(String family, String rendererType, Renderer renderer) {
  // notify when our custom dummy renderer is registered by JSF
  if(family.equals("javax.faces.cool.Output") &&(rendererType.equals(DummyRenderer.RENDERER_TYPE))){
     LOG.info("Your dummy renderer was registered !");
  }
  super.addRenderer(family, rendererType, renderer);
 }
   
 @Override
 public Renderer getRenderer(String family, String rendererType) {       
  // instruct JSF to use the DummyRenderer instead of TextRenderer, but pass a TextRenderer instance to it
  if (family.equals("javax.faces.Output") && (rendererType.equals("javax.faces.Text"))) {
      LOG.log(Level.INFO, "--- Delegating renderer of type {0} for component in family {1} ---", new Object[]{rendererType, family});
      Renderer renderer = getWrapped().getRenderer(family, rendererType);
      return new DummyRenderer(renderer);
  }
  return getWrapped().getRenderer(family, rendererType);
 }
   
 @Override
 public RenderKit getWrapped() {
  return renderKit;
 }
}

Write Custom RenderKitFactory Skeleton

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 RenderKits8 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. A detailed dissertation about them is the OmniFaces Custom Components Approach post. OmniFaces provides solid techniques for writing custom components and renderers!

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

e.g. replace the default JSF RenderKit with the DummyRenderKit (source code)

DummyRenderKitFactory
public class DummyRenderKitFactory extends RenderKitFactory {

 private static final Logger LOG = Logger.getLogger(DummyRenderKitFactory.class.getName());
 private RenderKitFactory renderKitFactory;

 public DummyRenderKitFactory() {
 }

 public DummyRenderKitFactory(RenderKitFactory renderKitFactory) {
  this.renderKitFactory = renderKitFactory;
 }

 @Override
 public void addRenderKit(String renderKitId, RenderKit renderKit) {
  LOG.log(Level.INFO, "Register RenderKit with ID: {0}", renderKitId);
  getWrapped().addRenderKit(renderKitId, renderKit);
 }

 @Override
 public RenderKit getRenderKit(FacesContext context, String renderKitId) {
  LOG.log(Level.INFO, "Delegate DummyRenderKit instead of RenderKit with ID: {0}", renderKitId);
  RenderKit renderKit = getWrapped().getRenderKit(context, renderKitId);
  return (HTML_BASIC_RENDER_KIT.equals(renderKitId)) ? new DummyRenderKit(renderKit) : renderKit;
 }

 @Override
 public Iterator<String> getRenderKitIds() {
  return getWrapped().getRenderKitIds();
 }

 @Override
 public RenderKitFactory getWrapped() {
  return renderKitFactory;
 }
}

DummyRenderKit
public class DummyRenderKit extends RenderKitWrapper {

 private static final Logger LOG = Logger.getLogger(DummyRenderKit.class.getName());
 private RenderKit renderKit;

 public DummyRenderKit() {
 }

 public DummyRenderKit(RenderKit renderKit) {
  LOG.log(Level.INFO, "Default RenderKit instance provided by JSF: {0}", renderKit.getClass().getSimpleName());
  this.renderKit = renderKit;
 }

 @Override
 public Renderer getRenderer(String family, String rendererType) {
  LOG.log(Level.INFO, "--- Delegating renderer of type {0} for component in family {1} ---", new Object[]{rendererType, family});
  return getWrapped().getRenderer(family, rendererType);
 }

 @Override
 public void addRenderer(String family, String rendererType, Renderer renderer) {
  LOG.log(Level.INFO, "--- Adding the renderer of type {0} for component in family {1} ---", new Object[]{rendererType, family});
  getWrapped().addRenderer(family, rendererType, renderer);
 }

 @Override
 public RenderKit getWrapped() {
  return renderKit;
 }
}

faces-config.xml
...
<factory>
 <render-kit-factory>
  jsf.renderkitfactories.DummyRenderKitFactory
 </render-kit-factory>
</factory>
...

Niciun comentariu :

Trimiteți un comentariu

JSF BOOKS COLLECTION

Postări populare

OmniFaces/JSF Fans

Visitors Starting 4 September 2015

Locations of Site Visitors