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, 19 februarie 2015

Using OmniFaces Components.getValue() to understand the request/submitted/new/local/model values


[OmniFaces utilities] The getValue() method returns the value of the given editable value holder component without the need to know if the given component has already been converted/validated or not. Note that it thus returns the unconverted submitted string value when the conversion/validation hasn't been taken place for the given component and it returns the converted object value -if applicable- when conversion/validation has been taken place for the given component.

In this post, we will use an OmniFaces utilities method from Components class to make a quick analyze of what is happening with a piece of information (value) provided by the user via an EditableValueHolder (e.g. UIInput) during JSF phases.

This method is named, getValue() and you can see its source in the below figure:


So, basically this method returns the result obtained via getSubmittedValue() or getLocalValue(). The OmniFaces documentation says: "Returns the value of the given editable value holder component without the need to know if the given component has already been converted / validated or not. Note that it thus returns the unconverted submitted string value when the conversion / validation hasn't been taken place for the given component and it returns the converted object value -if applicable- when conversion/validation has been taken place for the given component."

Well, for a novice, this explanation may sound pretty confusing. If you are not very familiar with the differences between submitted value and local value, or you worked with the user provided inputs only via getters and setters then this post may help you to clear up these things. So, let's see an example that will make us believe that this method is pretty dummy. In a JSF page you may have this - just a simple EditableValueHolder (UIInput) we an attached custom converter and validator:

<h:form id="myForm">
 Player Name:
 <h:inputText id="nameId" value="#{playersBean.name}" converter="myConverter" validator="myValidator"/>
 <h:commandButton id="maxBtnId" value="Save" action="#{playersBean.save()}"/>
</h:form>

The custom converter and validator indicated above were "patched" with some SOPs and will help us to see how the converter and validator is called in conjunction with JSF phases:
·         custom converter (converts between lower and upper case)

@FacesConverter(value = "myConverter")
public class MyConverter implements Converter {

 @Override
 public Object getAsObject(FacesContext fc, UIComponent uic, String string) {
  System.out.println("MY CONVERTER: getAsObject()");
  return string.toUpperCase();
 }

 @Override
 public String getAsString(FacesContext fc, UIComponent uic, Object o) {
  System.out.println("MY CONVERTER: getAsString()");
  return o.toString().toLowerCase();
 }
}

·         custom validator (this is a dummy validator)

@FacesValidator(value = "myValidator")
public class MyValidator implements Validator {

 @Override
 public void validate(FacesContext fc, UIComponent uic, Object o) throws ValidatorException {
 System.out.println("MY VALIDATOR: validate()");       
 }
}

Finally, the managed bean relevant code is:

import org.omnifaces.util.Components;
import org.omnifaces.util.Faces;
...
@Named
@SessionScoped
public class PlayersBean implements Serializable {

 private String name;

 public void save(){
  // some saving stuff
  EditableValueHolder  uiInputName = (EditableValueHolder)Faces.getViewRoot().findComponent("myForm:nameId");
  String uiInputNameValue = Components.getValue(uiInputName);
  System.out.println("VALUE RETURNED BY OmniFaces#getValue(): " + uiInputNameValue);
  System.out.println("VALUE RETURNED BY GETTER: " + getName());
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
}

If you check the highlighted code you can see how the Components.getValue() method was called for the EditableValueHolder represented by our UIInput component. Now, let's say that we typed in the text box the name rafael nadal, and we clicked the Save button. The big question now is: after we submit the form (at postback), what will display the SOPs from our custom converter/validator and the two SOPs from the save() method above? Well, here it is the output:

MY CONVERTER: getAsObject()
MY VALIDATOR: validate()
VALUE RETURNED BY OmniFaces#getValue(): null
VALUE RETURNED BY GETTER: RAFAEL NADAL
MY CONVERTER: getAsString()

Well, the OmniFaces getValue() returned null, while the getter method returned exactly what we have expected (the rafael nadal text in uppercase)! Repeating the scenario will reveal the same result every time! So, what is going on? Is the OmniFaces getValue() method useless ?! Obviously, not! The issue consist in the fact that we did not correctly understood what the method does, and we have implemented a wrong scenario. If you have expected to see the same result returned by getValue() and the getter method, then you have to read further.
With a little work, we can easily write e simple phase listener that will help us to debug the JSF phases. The idea is to call the OmniFaces getValue() method before and after each JSF phase, and reveal the results via some simple SOPs:

public class DebugPhaseListener implements PhaseListener {

 public DebugPhaseListener() {
 }

 @Override
 public void afterPhase(PhaseEvent event) {
  System.out.println("AFTER PHASE # " + event.getPhaseId());
  UIViewRoot root = Faces.getViewRoot();
  if (root != null) {
      UIComponent comp = root.findComponent("myForm:nameId");
      if (comp != null) {
          Object value = Components.getValue((EditableValueHolder) comp);
          System.out.println("VALUE # " + value);
      }
  } else {
      System.out.println("NO VIEW ROOT AVAILABLE !");
  }
 }

 @Override
 public void beforePhase(PhaseEvent event) {
  System.out.println("BEFORE PHASE # " + event.getPhaseId());
  UIViewRoot root = Faces.getViewRoot();
  if (root != null) {
      UIComponent comp = root.findComponent("myForm:nameId");
      if (comp != null) {
          Object value = Components.getValue((EditableValueHolder) comp);
          System.out.println("VALUE # " + value);
      }
  } else {
      System.out.println("NO VIEW ROOT AVAILABLE !");
  }
 }

 @Override
 public PhaseId getPhaseId() {
  return PhaseId.ANY_PHASE;
 }
}

In addition, we have " patched" with a SOP the OmniFaces getValue() method itself:
public static <T> T getValue(EditableValueHolder component) {           
 Object submittedValue = component.getSubmittedValue();
 System.out.println("OmniFaces Components.getValue(): SUBMITTED VALUE: " + submittedValue + "            LOCAL VALUE: "+component.getLocalValue());
 return (T) ((submittedValue != null) ? submittedValue : component.getLocalValue());
}

Is time to see what is really happening behind the scene. We repeated the scenario, and the outputs are discussed below:

·         check RESTORE_VIEW phase
Before this phase, there is no view root (is null), so the getValue() is not called. Right after this point, the component tree will be build:
BEFORE PHASE # RESTORE_VIEW 1
NO VIEW ROOT AVAILABLE !

After this phase, the component tree was restored (built), but the request parameter (request value) have not been extracted yet, so, at this point, the submitted value and the local value are both null:
AFTER PHASE # RESTORE_VIEW 1
OmniFaces Components.getValue(): SUBMITTED VALUE: null   LOCAL VALUE: null
VALUE # null

·         check APPLY_REQUEST_VALUES phase
Well, we are just after the RESTORE_VIEW and before the APPLY_REQUEST_VALUES, so we have the same output:
BEFORE PHASE # APPLY_REQUEST_VALUES 2
OmniFaces Components.getValue(): SUBMITTED VALUE: null   LOCAL VALUE: null
VALUE # null

In this phase, the decode() method is called and the request parameter is stored locally via setSubmittedValue() method (this value is known as submitted value). This means that the stored value is exactly the value provided by the user. It was not converted or validated. After this phase, you can notice that the getValue() returns the submitted value (e.g. rafael nadal text):
AFTER PHASE # APPLY_REQUEST_VALUES 2
OmniFaces Components.getValue(): SUBMITTED VALUE: rafael nadal     LOCAL VALUE: null
VALUE # rafael nadal

·         check PROCESS_VALIDATIONS phase
Well, we are just after the APPLY_REQUEST_VALUES and before the PROCESS_VALIDATIONS, so we have the same output:
BEFORE PHASE # PROCESS_VALIDATIONS 3
OmniFaces Components.getValue(): SUBMITTED VALUE: rafael nadal      LOCAL VALUE: null
VALUE # rafael nadal

In this phase, the converters and validators are called. The converted and validated value is further stored as local value via setValue() and setLocalValueSet() flag method (a submitted value that was converted and validated, but not stored yet as local value is known as new value). Moreover, the earlier submitted value is reset by calling setSubmittedValue(null). Notice that, after this phase, the OmniFaces getValue() reveals the local value, which was converted, validated and stored, and the submitted value, which was available after APPLY_REQUEST_VALUES phase, but is null now:
MY CONVERTER: getAsObject()
MY VALIDATOR: validate()
AFTER PHASE # PROCESS_VALIDATIONS 3
OmniFaces Components.getValue(): SUBMITTED VALUE: null      LOCAL VALUE: RAFAEL NADAL
VALUE # RAFAEL NADAL

·         check UPDATE_MODEL_VALUES phase
Well, we are just after the PROCESS_VALIDATIONS and before the UPDATE_MODEL_VALUES, so we have the same output:
BEFORE PHASE # UPDATE_MODEL_VALUES 4
OmniFaces Components.getValue(): SUBMITTED VALUE: null    LOCAL VALUE: RAFAEL NADAL
VALUE # RAFAEL NADAL

In this phase, the data model value is updated from the local value and the local value is reset to null. Actually, at this point Mojarra calls the resetValue() method, which will reset the submitted value (which is null anyway), the local value and the flag indicating that the local value was set. So, after this phase, the OmniFaces getValue() will return always null (both, submitted and local value were reset):
AFTER PHASE # UPDATE_MODEL_VALUES 4
OmniFaces Components.getValue(): SUBMITTED VALUE: null      LOCAL VALUE: null
VALUE # null

·         check INVOKE_APPLICATION phase
Well, during this phase, JSF will invoke the PlayersBean.save() method. Remember that, from this method, we have called the OmniFaces getValue() method. Now, we know why the returned result was invariably null, and why the getter returns the expected value:
BEFORE PHASE # INVOKE_APPLICATION 5
OmniFaces Components.getValue(): SUBMITTED VALUE: null            LOCAL VALUE: null
VALUE # null
OmniFaces Components.getValue(): SUBMITTED VALUE: null            LOCAL VALUE: null
VALUE RETURNED BY OmniFaces#getValue(): null
VALUE RETURNED BY GETTER: RAFAEL NADAL

AFTER PHASE # INVOKE_APPLICATION 5
OmniFaces Components.getValue(): SUBMITTED VALUE: null            LOCAL VALUE: null
VALUE # null

·         check RENDER_RESPONSE phase
Well, we are just after the INVOKE_APPLICATION and before the RENDER_RESPONSE, so we have the same output. During this phase the converter getAsString() method is called:
BEFORE PHASE # RENDER_RESPONSE 6
OmniFaces Components.getValue(): SUBMITTED VALUE: null            LOCAL VALUE: null
VALUE # null
MY CONVERTER: getAsString()

After this phase, we have the same output:
AFTER PHASE # RENDER_RESPONSE 6
OmniFaces Components.getValue(): SUBMITTED VALUE: null            LOCAL VALUE: null
VALUE # null

The request-response cycle is over (the postback passed through all JSF phases). Now you know what the OmniFaces getValue() method returns depending on the current JSF phase, and you know how to correctly use it in your projects. Moreover, you know what is happening with a piece of information provided by the user from the point of submission to the response:

For sake of completeness, you may be interesting in reading "JSFConverters and Validators Demystified"

Niciun comentariu :

Trimiteți un comentariu

JSF BOOKS COLLECTION

Postări populare

OmniFaces/JSF Fans

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors