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

duminică, 14 decembrie 2014

OmniFaces Approach for Declaring Custom Components Attributes

When we write custom components, we need to be sure that we follow the right techniques. Only then we can focus on implementing custom components with a "useful" behavior, that are not just didactical examples. But, learning the right techniques is not very simple because there are many aspects to keep in mind and many examples on Internet doesn't really respect the written and unwritten (convensions) rules.
An important aspect of custom components involves managing their state. JSF 2.0 comes with the StateHelper interface, which basically allows us to store, read, and remove data across multiple requests (postbacks). This means we can use it to preserve states of the components. This involves storing the component's attributes also. OmniFaces source code provides us clear hints of how we should declare the attributes of a custom components using the StateHelper interface. OmniFaces, defines a helper class for StateHelper that uses generic type-inference to make code that uses the StateHelper slightly less verbose - personally, I prefer to use it instead of StateHelper. The source code is listed below:

package org.omnifaces.util;

import java.io.Serializable;

import javax.faces.component.StateHelper;

public class State {

 private final StateHelper stateHelper;

 public State(StateHelper stateHelper) {
  this.stateHelper = stateHelper;
 }

 @SuppressWarnings("unchecked")
 public <T> T get(Serializable key) {
  return (T) stateHelper.eval(key);
 }

 @SuppressWarnings("unchecked")
 public <T> T get(Serializable key, Object defaultValue) {
  return (T) stateHelper.eval(key, defaultValue);
 }

 @SuppressWarnings("unchecked")
 public <T> T put(Serializable key, T value) {
  return (T) stateHelper.put(key, value);
 }

}

Per example, we can focus on the OmniFaces CommandScript component, and notice that:

·         The attributes are declared in a private Java enum type - important to notice that the set of predefined constants (the attributes) are declared in lowercase, because they have to match the attribute names

private enum PropertyKeys {
  name, execute, render, onbegin, oncomplete;
}

·         Further, OmniFaces calls the getStateHelper() to obtain the state:

private final State state = new State(getStateHelper());

·         The third and the last step consist in declaring the getters and the setters for the attributes. In the setters method, OmniFaces uses the StateHelper.put() method, as shown in the following code:

public void setName(String name) {
 state.put(PropertyKeys.name, name);
}

public void setExecute(String execute) {
 state.put(PropertyKeys.execute, execute);
}

public void setRender(String render) {
 state.put(PropertyKeys.render, render);
}

public void setOnbegin(String onbegin) {
 state.put(PropertyKeys.onbegin, onbegin);
}

public void setOncomplete(String oncomplete) {
 state.put(PropertyKeys.oncomplete, oncomplete);
}

These examples uses behind the scene the StateHelper method Object put(Serializable key, Object value) method, but StateHelper also has a method Object put(Serializable key, StringmapKey, Object value), which can be used to store values that would otherwise be stored in a Map instance variable. Moreover, StateHelper has a method named void add(Serializable key, Object value) that can be used to preserve values which would otherwise be stored in a List instance variable.

For the getters, OmniFaces uses the StateHelper methods, Object eval(Serializable key) and Object eval(Serializable key, Object defaultValue). The first one is wrapped in State as, Object get(Serializable key), and the second one is wrapped in State as,  Object get(Serializable key, Object defaultValue). The later, search for the key and if it can't find it, then the default value (defaultValue) is returned. This is a very useful approach because it spears us to perform null value checks. Besides these methods, StateHelper also has the Object get(Serializable key) method.

public String getName() {
 return state.get(PropertyKeys.name);
}

public String getExecute() {
 return state.get(PropertyKeys.execute, "@this");
}

public String getRender() {
 return state.get(PropertyKeys.render, "@none");
}

public String getOnbegin() {
 return state.get(PropertyKeys.onbegin);
}

public String getOncomplete() {
 return state.get(PropertyKeys.oncomplete);
}

Notice that for the attributes that comes with default values, OmniFaces uses the StateHelper method Object eval(Serializable key, Object defaultValue), while for those that doesn't need a default value (do not understand that an attribute that doesn't have a default value, cannot be required; the name attribute is required!), OmniFaces uses the StateHelper method Object StateHelper.eval(Serializable key).
In order to remove an entry from StateHelper, we can call the StateHelper method Object remove(Serializable key) or Object remove(Serializable key,Object valueOrKey).


If an attribute is required, you have to perform a specific check. One place where this check can take place is in the custom component renderer. Per example, the CommandScript component has the required name attribute, and the check is performed in the encodeBegin() method, like this:

@Override
public void encodeBegin(FacesContext context) throws IOException {

 ...
 String name = getName();
 if (name == null) {
     throw new IllegalArgumentException(ERROR_MISSING_NAME);
 }
 ...

Moreover, here it is a good place to check if the provided value respects a pattern:

 ...
 private static final Pattern PATTERN_NAME = Pattern.compile("[$a-z_][$\\w]*",   
  Pattern.CASE_INSENSITIVE);
 ...
 if (!PATTERN_NAME.matcher(name).matches()) {
    throw new IllegalArgumentException(String.format(ERROR_ILLEGAL_NAME, name));
 }
 ...


In some cases, these checks can be performed via ComponentHandler.getRequiredAttribute() method, which will perform a check at runtime and will throw a TagException.

Niciun comentariu :

Trimiteți un comentariu

JSF BOOKS COLLECTION

Postări populare

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors