The JSF lifecycle will be explained and debugged here. We'll also check what happens if you add immediate="true" to the UIInput and UICommand and what happens when a ConverterException and ValidatorException will be thrown.
The JSF lifecycle contains 6 phases:
1. Restore view
2. Apply request values
3. Process validations
4. Update model values
5. Invoke application
6. Render response
You can use a PhaseListener to trace the phases of the JSF lifecycle and execute some processes where required. But you also can use a PhaseListener to debug the phases to see what is happening in which phase. Here is a basic example of such a LifeCycleListener:
package mypackage;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
public class LifeCycleListener implements PhaseListener {
public void beforePhase(PhaseEvent event) {
System.out.println("BeforePhase: " + event.getPhaseId());
}
public void afterPhase(PhaseEvent event) {
System.out.println("AfterPhase: " + event.getPhaseId());
}
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
}
Add the following lines to the faces-config.xml to activate the LifeCycleListener.
<lifecycle>
<phase-listener>mypackage.LifeCycleListener</phase-listener>
</lifecycle>
This produces like the following in the system output:
BeforePhase: RESTORE_VIEW 1
AfterPhase: RESTORE_VIEW 1
BeforePhase: APPLY_REQUEST_VALUES 2
AfterPhase: APPLY_REQUEST_VALUES 2
BeforePhase: PROCESS_VALIDATIONS 3
AfterPhase: PROCESS_VALIDATIONS 3
BeforePhase: UPDATE_MODEL_VALUES 4
AfterPhase: UPDATE_MODEL_VALUES 4
BeforePhase: INVOKE_APPLICATION 5
AfterPhase: INVOKE_APPLICATION 5
BeforePhase: RENDER_RESPONSE 6
AfterPhase: RENDER_RESPONSE 6
To trace all phases of the JSF lifecycle, here is some sample code which represents simple JSF form with a fake converter and validator and the appropriate backing bean. The code sample can be used to give us more insights into the phases of the JSF lifecycle, to understand it and to learn about it.
The test JSF file: test.jsp
<h:form>
<h:inputText
binding="#{myBean.inputComponent}"
value="#{myBean.inputValue}"
valueChangeListener="#{myBean.inputChanged}">
<f:converter converterId="myConverter" />
<f:validator validatorId="myValidator" />
</h:inputText>
<h:commandButton
value="submit"
action="#{myBean.action}" />
<h:outputText
binding="#{myBean.outputComponent}"
value="#{myBean.outputValue}" />
<h:messages />
</h:form>
The fake converter: MyConverter.java
package mypackage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
public class MyConverter implements Converter {
public Object getAsObject(FacesContext context, UIComponent component, String value) {
System.out.println("MyConverter getAsObject: " + value);
return value;
}
public String getAsString(FacesContext context, UIComponent component, Object value) {
System.out.println("MyConverter getAsString: " + value);
return (String) value;
}
}
The fake validator: MyValidator.java
package mypackage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
public class MyValidator implements Validator {
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException
{
System.out.println("MyValidator validate: " + value);
}
}
The backing bean: MyBean.java
package mypackage;
import javax.faces.component.html.HtmlInputText;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.event.ValueChangeEvent;
public class MyBean {
// Init ----------------------------------------------------------------------------------------
private HtmlInputText inputComponent;
private String inputValue;
private HtmlOutputText outputComponent;
private String outputValue;
// Actions ------------------------------------------------------------------------------------
public void action() {
outputValue = inputValue;
log("succes");
}
// Getters ------------------------------------------------------------------------------------
public HtmlInputText getInputComponent() {
log(inputComponent);
return inputComponent;
}
public String getInputValue() {
log(inputValue);
return inputValue;
}
public HtmlOutputText getOutputComponent() {
log(outputComponent);
return outputComponent;
}
public String getOutputValue() {
log(outputValue);
return outputValue;
}
// Setters ------------------------------------------------------------------------------------
public void setInputComponent(HtmlInputText inputComponent) {
log(inputComponent);
this.inputComponent = inputComponent;
}
public void setInputValue(String inputValue) {
log(inputValue);
this.inputValue = inputValue;
}
public void setOutputComponent(HtmlOutputText outputComponent) {
log(outputComponent);
this.outputComponent = outputComponent;
}
// Listeners ----------------------------------------------------------------------------------
public void inputChanged(ValueChangeEvent event) {
log(event.getOldValue() + " to " + event.getNewValue());
}
// Helpers ------------------------------------------------------------------------------------
private void log(Object object) {
String methodName = new Exception().getStackTrace()[1].getMethodName();
System.out.println("MyBean " + methodName + ": " + object);
}
}
The minimal faces configuration: faces-config.xml
<converter>
<converter-id>myConverter</converter-id>
<converter-class>mypackage.MyConverter</converter-class>
</converter>
<validator>
<validator-id>myValidator</validator-id>
<validator-class>mypackage.MyValidator</validator-class>
</validator>
<managed-bean>
<managed-bean-name>myBean</managed-bean-name>
<managed-bean-class>mypackage.MyBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
The first call should output at least:
BeforePhase: RESTORE_VIEW 1
AfterPhase: RESTORE_VIEW 1
BeforePhase: RENDER_RESPONSE 6
MyBean getInputComponent: null
MyBean setInputComponent: javax.faces.component.html.HtmlInputText@2a9fca57
MyBean getInputValue: null
MyBean getOutputComponent: null
MyBean setOutputComponent: javax.faces.component.html.HtmlOutputText@13bbca56
MyBean getOutputValue: null
AfterPhase: RENDER_RESPONSE 6
1. Restore view.
As the UIViewRoot is empty, there is nothing to do in here.
2. Apply request values.
This phase is skipped because there is no form submit.
3. Process validations.
This phase is skipped because there is no form submit.
4. Update model values.
This phase is skipped because there is no form submit.
5. Invoke application.
This phase is skipped because there is no form submit.
6. Render response.
The components are created for the first time and stored in the UIViewRoot and set in the eventual component bindings. If the component binding getters returns precreated components (precreated in e.g. the constructor) and not null, then those will be used, otherwise JSF will create new components and set it in the eventual component bindings. The values to be shown are retrieved from the value binding getters in the backing bean. If the values aren't set yet, they defaults to null. The binding attribtues are not required by the way. Only use them if you actually need the component in the backing bean. In this article they are included just to demonstrate what all happens in the lifecycle.
After doing a refresh in the browser in the same user session, the output should look like at least:
BeforePhase: RESTORE_VIEW 1
MyBean setInputComponent: javax.faces.component.html.HtmlInputText@2a9fca57
MyBean setOutputComponent: javax.faces.component.html.HtmlOutputText@13bbca56
AfterPhase: RESTORE_VIEW 1
BeforePhase: RENDER_RESPONSE 6
MyBean getInputValue: null
MyBean getOutputValue: null
AfterPhase: RENDER_RESPONSE 6
1. Restore view.
The components are restored in the UIViewRoot and set in the eventual component bindings.
2. Apply request values.
This phase is skipped because there is no form submit.
3. Process validations.
This phase is skipped because there is no form submit.
4. Update model values.
This phase is skipped because there is no form submit.
5. Invoke application.
This phase is skipped because there is no form submit.
6. Render response.
The values to be shown are retrieved from the value binding getters in the backing bean. If the values aren't set yet, they defaults to null.
The form submit with the value "test" entered should output at least:
BeforePhase: RESTORE_VIEW 1
MyBean setInputComponent: javax.faces.component.html.HtmlInputText@2a9fca57
MyBean setOutputComponent: javax.faces.component.html.HtmlOutputText@13bbca56
AfterPhase: RESTORE_VIEW 1
BeforePhase: APPLY_REQUEST_VALUES 2
AfterPhase: APPLY_REQUEST_VALUES 2
BeforePhase: PROCESS_VALIDATIONS 3
MyConverter getAsObject: test
MyValidator validate: test
MyBean getInputValue: null
MyBean inputChanged: null to test
AfterPhase: PROCESS_VALIDATIONS 3
BeforePhase: UPDATE_MODEL_VALUES 4
MyBean setInputValue: test
AfterPhase: UPDATE_MODEL_VALUES 4
BeforePhase: INVOKE_APPLICATION 5
MyBean action: succes
AfterPhase: INVOKE_APPLICATION 5
BeforePhase: RENDER_RESPONSE 6
MyBean getInputValue: test
MyConverter getAsString: test
MyBean getOutputValue: test
AfterPhase: RENDER_RESPONSE 6
1. Restore view.
The components are restored in the UIViewRoot and set in the eventual component bindings.
2. Apply request values.
Nothing to see here. Behind the scenes the submitted form values are obtained as request parameters and set in the relevant components in the UIViewRoot, for example inputComponent.setSubmittedValue("test").
3. Process validations.
The submitted values are passed through the converter getAsObject() method and validated by the validator. If the conversion and validation succeeds, then behind the scenes the inputComponent.setValue(submittedValue) and inputComponent.setSubmittedValue(null) will be executed. Finally the valueChangeListener method is invoked.
4. Update model values.
The converted and validated values will now be set in the value binding setters of the backing bean. E.g. myBean.setInputValue(inputComponent.getValue()).
5. Invoke application.
The real processing of the form submission happens here.
6. Render response.
The values to be shown are retrieved from the value binding getters in the backing bean. If a converter is definied, then the value will be passed through the converter getAsString() method and the result will be shown in the form.
Add immediate="true" to UIInput only
Extend the h:inputText in the test.jsp with immediate="true":
...
<h:inputText
binding="#{myBean.inputComponent}"
value="#{myBean.inputValue}"
valueChangeListener="#{myBean.inputChanged}"
immediate="true">
...
The form submit with the value "test" entered should output at least:
BeforePhase: RESTORE_VIEW 1
MyBean setInputComponent: javax.faces.component.html.HtmlInputText@2a9fca57
MyBean setOutputComponent: javax.faces.component.html.HtmlOutputText@13bbca56
AfterPhase: RESTORE_VIEW 1
BeforePhase: APPLY_REQUEST_VALUES 2
MyConverter getAsObject: test
MyValidator validate: test
MyBean getInputValue: null
MyBean inputChanged: null to test
AfterPhase: APPLY_REQUEST_VALUES 2
BeforePhase: PROCESS_VALIDATIONS 3
AfterPhase: PROCESS_VALIDATIONS 3
BeforePhase: UPDATE_MODEL_VALUES 4
MyBean setInputValue: test
AfterPhase: UPDATE_MODEL_VALUES 4
BeforePhase: INVOKE_APPLICATION 5
MyBean action: succes
AfterPhase: INVOKE_APPLICATION 5
BeforePhase: RENDER_RESPONSE 6
MyBean getInputValue: test
MyConverter getAsString: test
MyBean getOutputValue: test
AfterPhase: RENDER_RESPONSE 6
1. Restore view.
The components are restored in the UIViewRoot and set in the eventual component bindings.
2. Apply request values.
Behind the scenes the submitted form values are obtained as request parameters and set in the relevant components in the UIViewRoot, for example inputComponent.setSubmittedValue("test"). The submitted values are immediately passed through the converter getAsObject() method and validated by the validator. If the conversion and validation succeeds, then behind the scenes the inputComponent.setValue(submittedValue) and inputComponent.setSubmittedValue(null) will be executed. Finally the valueChangeListener method is invoked. This all happens in this phase instead of the Process validations phase due to the immediate="true" in the h:inputText.
3. Process validations.
Nothing to see here. The conversion and validation is already processed in the Apply request values phase, before the values being put in the components. This is due to the immediate="true" in the h:inputText.
4. Update model values.
The converted and validated values will now be set in the value binding setters of the backing bean. E.g. myBean.setInputValue(inputComponent.getValue()).
5. Invoke application.
The real processing of the form submission happens here.
6. Render response.
The values to be shown are retrieved from the value binding getters in the backing bean. If a converter is definied, then the value will be passed through the converter getAsString() method and the result will be shown in the form.
Back to top
Add immediate="true" to UICommand only
Extend the h:commandButton in the test.jsp with immediate="true" (don't forget to remove from h:inputText):
...
<h:commandButton
value="submit"
action="#{myBean.action}"
immediate="true" />
...
The form submit with the value "test" entered should output at least:
BeforePhase: RESTORE_VIEW 1
MyBean setInputComponent: javax.faces.component.html.HtmlInputText@2a9fca57
MyBean setOutputComponent: javax.faces.component.html.HtmlOutputText@13bbca56
AfterPhase: RESTORE_VIEW 1
BeforePhase: APPLY_REQUEST_VALUES 2
MyBean action: succes
AfterPhase: APPLY_REQUEST_VALUES 2
BeforePhase: RENDER_RESPONSE 6
MyBean getOutputValue: null
AfterPhase: RENDER_RESPONSE 6
1. Restore view.
The components are restored in the UIViewRoot and set in the eventual component bindings.
2. Apply request values.
The real processing of the form submission happens here. This happens in this phase instead of the Invoke application phase due to the immediate="true" in the h:commandButton. The UIInput components which don't have immediate="true" set will be ignored completely and therefore their values will not be converted and validated.
3. Process validations.
This phase is skipped due to the immediate="true" in the h:commandButton.
4. Update model values.
This phase is skipped due to the immediate="true" in the h:commandButton.
5. Invoke application.
This phase is skipped due to the immediate="true" in the h:commandButton.
6. Render response.
The values to be shown are retrieved from the value binding getters in the backing bean.
Take care: as the Update model values phase is skipped, the value bindings aren't been set and the value binding getters will return null. The UIInput components which don't have immediate="true" set will be ignored completely and therefore you cannot retrieve the values from the component or value bindings anyway. They will all remain the original values.
Back to top
Add immediate="true" to UIInput and UICommand
Extend the h:inputText as well as the h:commandButton in the test.jsp with immediate="true":
...
<h:inputText
binding="#{myBean.inputComponent}"
value="#{myBean.inputValue}"
valueChangeListener="#{myBean.inputChanged}"
immediate="true">
...
<h:commandButton
value="submit"
action="#{myBean.action}"
immediate="true" />
...
The form submit with the value "test" entered should output at least:
BeforePhase: RESTORE_VIEW 1
MyBean setInputComponent: javax.faces.component.html.HtmlInputText@2a9fca57
MyBean setOutputComponent: javax.faces.component.html.HtmlOutputText@13bbca56
AfterPhase: RESTORE_VIEW 1
BeforePhase: APPLY_REQUEST_VALUES 2
MyConverter getAsObject: test
MyValidator validate: test
MyBean getInputValue: null
MyBean inputChanged: null to test
MyBean action: succes
AfterPhase: APPLY_REQUEST_VALUES 2
BeforePhase: RENDER_RESPONSE 6
MyConverter getAsString: test
MyBean getOutputValue: null
AfterPhase: RENDER_RESPONSE 6
1. Restore view.
The components are restored in the UIViewRoot and set in the eventual component bindings.
2. Apply request values.
Behind the scenes the submitted form values are obtained as request parameters and set in the relevant components in the UIViewRoot, for example inputComponent.setSubmittedValue("test"). The submitted values are immediately passed through the converter getAsObject() method and validated by the validator. If the conversion and validation succeeds, then behind the scenes the inputComponent.setValue(submittedValue) and inputComponent.setSubmittedValue(null) will be executed. Also the valueChangeListener method is invoked. This all happens in this phase instead of the Process validations phase due to the immediate="true" in the h:inputText. Finally the real processing of the form submission happens here. This happens in this phase instead of the Invoke application phase due to the immediate="true" in the h:commandButton.
3. Process validations.
This phase is skipped due to the immediate="true" in the h:commandButton.
4. Update model values.
This phase is skipped due to the immediate="true" in the h:commandButton.
5. Invoke application.
This phase is skipped due to the immediate="true" in the h:commandButton.
6. Render response.
The values to be shown are retrieved from the value binding getters in the backing bean. If a converter is definied, then the value will be passed through the converter getAsString() method and the result will be shown in the form.
Take care: as the Update model values phase is skipped, the value bindings aren't been set and the value binding getters will return null. But the values are still available by the relevant components in the UIViewRoot. In this case you can retrieve the input value from inputComponent.getValue() in the action() method. The new input value is also available by the ValueChangeEvent in the inputChanged() method.
Back to top
Conversion error
Let's see what happens if a conversion error will occur. Change the getAsObject() method of MyConverter.java as follows (and remove the immediate="true" from the test.jsp file):
package mypackage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
public class MyConverter implements Converter {
public Object getAsObject(FacesContext context, UIComponent component, String value) {
System.out.println("MyConverter getAsObject: " + value);
throw new ConverterException("Conversion failed.");
}
...
}
The form submit with the value "test" entered should output at least:
BeforePhase: RESTORE_VIEW 1
MyBean setInputComponent: javax.faces.component.html.HtmlInputText@2a9fca57
MyBean setOutputComponent: javax.faces.component.html.HtmlOutputText@13bbca56
AfterPhase: RESTORE_VIEW 1
BeforePhase: APPLY_REQUEST_VALUES 2
AfterPhase: APPLY_REQUEST_VALUES 2
BeforePhase: PROCESS_VALIDATIONS 3
MyConverter getAsObject: test
AfterPhase: PROCESS_VALIDATIONS 3
BeforePhase: RENDER_RESPONSE 6
MyBean getOutputValue: null
AfterPhase: RENDER_RESPONSE 6
1. Restore view.
The components are restored in the UIViewRoot and set in the eventual component bindings.
2. Apply request values.
Nothing to see here. Behind the scenes the submitted form values are obtained as request parameters and set in the relevant components in the UIViewRoot, for example inputComponent.setSubmittedValue("test").
3. Process validations.
The submitted values are passed through the converter getAsObject() method, where a ConverterException is thrown. The validator and the valueChangeListener are bypassed. Also the inputComponent.setValue(submittedValue) won't take place. The lifecycle will proceed to the Render response phase immediately.
4. Update model values.
This phase is skipped due to the ConverterException.
5. Invoke application.
This phase is skipped due to the ConverterException.
6. Render response.
The values to be shown are retrieved from the value binding getters in the backing bean, expecting the values for which a ConverterException has occurred. Behind the scenes those will be retrieved from the components in the UIViewRoot, e.g. inputComponent.getSubmittedValue().
Back to top
Validation error
Let's see what happens if a validation error will occur. Change the validate() method of MyValidator.java as follows (and remove the immediate="true" from the test.jsp file and revert MyConverter.java back to original):
package mypackage;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
public class MyValidator implements Validator {
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException
{
System.out.println("MyValidator validate: " + value);
throw new ValidatorException(new FacesMessage("Validation failed."));
}
}
The form submit with the value "test" entered should output at least:
BeforePhase: RESTORE_VIEW 1
MyBean setInputComponent: javax.faces.component.html.HtmlInputText@2a9fca57
MyBean setOutputComponent: javax.faces.component.html.HtmlOutputText@13bbca56
AfterPhase: RESTORE_VIEW 1
BeforePhase: APPLY_REQUEST_VALUES 2
AfterPhase: APPLY_REQUEST_VALUES 2
BeforePhase: PROCESS_VALIDATIONS 3
MyConverter getAsObject: test
MyValidator validate: test
AfterPhase: PROCESS_VALIDATIONS 3
BeforePhase: RENDER_RESPONSE 6
MyBean getOutputValue: null
AfterPhase: RENDER_RESPONSE 6
1. Restore view.
The components are restored in the UIViewRoot and set in the eventual component bindings.
2. Apply request values.
Nothing to see here. Behind the scenes the submitted form values are obtained as request parameters and set in the relevant components in the UIViewRoot, for example inputComponent.setSubmittedValue("test").
3. Process validations.
The values are retrieved as objects from the components, passed through the converter getAsObject() method and validated by the validator, where a ValidatorException is thrown. The valueChangeListener is bypassed. Also the inputComponent.setValue(submittedValue) won't take place. The lifecycle will proceed to the Render response phase immediately.
4. Update model values.
This phase is skipped due to the ValidatorException.
5. Invoke application.
This phase is skipped due to the ValidatorException.
6. Render response.
The values to be shown are retrieved from the value binding getters in the backing bean, expect of the values for which a ValidatorException has occurred. Behind the scenes those will be retrieved from the components in the UIViewRoot, e.g. inputComponent.getSubmittedValue().
JSF lifecycle phases - with example
Labels: JSF
Subscribe to:
Post Comments (Atom)
0 comments:
Post a Comment