Instance variables in converters

If you are making a simple attempt to declare an instance variable in a converter, you will notice that you can't store the variable state over time. This may look like a strange behavior, but the truth is that the getAsObject and getAsString are called on different instances. This is the simple explanation of why the instance variable doesn't have persistence over these methods calls.

We can fix this using UIComponent set/getAttribute or using a session variable instead. In this recipe, we will use a session variable to simulate an instance variable of a converter. For this, let's suppose that we have two numbers, one inserted by the user and one is selected by the user from a selectOneMenu component. The inserted value is multiplied with the selected value, inside of a custom converter, in the getAsObject method. In the backing bean we keep the multiplied result. Before the result is rendered, its value is divided

by the same value in the getAsString method. If everything works fine, then we will not notice these operations over the inserted value.

Getting ready

We developed this recipe with NetBeans 6.8, JSF 2.0, and GlassFish v3. The JSF 2.0 classes were obtained from the NetBeans JSF 2.0 bundled library.

How to do it...

Storing the selected value in the session is a simple task. First, the backing bean associated to this value is marked with the annotation @SessionScoped, indicating that the instance of this bean should be stored in session. Second, we pass the selected value in the traditional way (this code is from the multiply.xhtml page of the application), as shown next:

<h:form id="MultiplyForm">
<h:outputText value="Select the multiply factor:" />
<h:selectOneMenu id="factorID"
value="#{factorBean.selectedFactor}">
<f:selectItems value="#{factorBean.factors}"/>
</h:selectOneMenu>
<h:commandButton id="submit" action= "number?faces-redirect=true" value="Submit"/>
</h:form>

The selectedFactor property belongs to the next backing bean:

package multiply;
import java.util.LinkedList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.model.SelectItem;
@ManagedBean
@SessionScoped
public class FactorBean {
private List<SelectItem> factors = new LinkedList<SelectItem>();
private double selectedFactor;
public FactorBean(){
factors.add(new SelectItem("1.0", "1.0"));
factors.add(new SelectItem("2.0", "2.0"));
factors.add(new SelectItem("3.0", "3.0"));
factors.add(new SelectItem("4.0", "4.0"));
factors.add(new SelectItem("5.0", "5.0"));
}
public List<SelectItem> getFactors() {
return factors;
}
public void setFactors(List<SelectItem> factors) {
this.factors = factors;
}
public double getSelectedFactor() {
return this.selectedFactor;
}
public void setSelectedFactor(double selectedFactor) {
this.selectedFactor = selectedFactor;
}
}

Now, the multiplication factor is on session and we can request the user to insert a value to be multiplied by this factor (number.xhtml), as shown next:

<h:form id="NumberForm">
<h:outputText value="Insert the value to be multiplied:"/>
<h:inputText id="valueID" required="true"
value="#{multiplyBean.value}"
converter="multiplyConverter" />
<h:message showSummary="true" showDetail="false" for="valueID"
style="color: red; text-decoration:overline"/>
<br />
<h:commandButton id="submit" action="number?faces-
redirect=true" value="Submit"/>
</h:form>

The value is stored in the MultiplyBean, as shown next:

package multiply;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean
@SessionScoped
public class MultiplyBean {
private double value = 0.0d;
public double getValue() {
return this.value;
}
public void setValue(double value) {
this.value = value;
}
}

As you can see the operations are taking place in a converter. Now, the converter has access to the multiplication factor in a very easy approach, as shown next:

public String getAsString(FacesContext arg0, UIComponent arg1,
Object arg2) {
if (arg0 == null) {
throw new NullPointerException("context");
}
if (arg1 == null) {
throw new NullPointerException("component");
}
FacesContext ctx = FacesContext.getCurrentInstance();
ValueExpression vex =
ctx.getApplication().getExpressionFactory().
createValueExpression(ctx.getELContext(),
"#{factorBean}", FactorBean.class);
FactorBean c = (FactorBean) vex.getValue(ctx.getELContext());
try {
Double dividedVal = (Double) arg2 / c.getSelectedFactor();
return dividedVal.toString();
} catch (Exception e) {
FacesMessage message = new
FacesMessage(FacesMessage.SEVERITY_ERROR,
"Error!", "Cannot accomplish this operation (DIVIDE) !");
throw new ConverterException(message);
}
}
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
if (arg0 == null) {
throw new NullPointerException("context");
}
if (arg1 == null) {
throw new NullPointerException("component");
}
FacesContext ctx = FacesContext.getCurrentInstance();
ValueExpression vex =
ctx.getApplication().getExpressionFactory().
createValueExpression(ctx.getELContext(),
"#{factorBean}", FactorBean.class);
FactorBean c = (FactorBean) vex.getValue(ctx.getELContext());
try {
Double val = new Double(arg2);
Double multiplyVal = val * c.getSelectedFactor();
return multiplyVal;
} catch (NumberFormatException e) {
FacesMessage message = new
FacesMessage(FacesMessage.SEVERITY_ERROR,
"Error!","Connot accomplish this operation (MULTIPLY)!");
throw new ConverterException(message);
}
}

How it works...

First, we store in the session the value that we need to have access to in the converter's methods. Second, we call this session value from the getAsString and getAsObject methods. Using this technique we have replaced the instance variable of the converter with a session variable.

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and is named: Instance_variables_in_converters.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset