You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@myfaces.apache.org by "ir.ing.Jan Dockx" <ja...@mac.com> on 2005/07/13 00:49:30 UTC
Exceptions from backing properties
In our code, the business objects properties setters do validation and
throw exceptions (SemanticException). We don't want to repeat this
validation. Our intention is to have the UIInput call the setter. If
the setter throws an exception, it should be caught, and it's localized
message should become a FacesMessage.
Now, the MyFaces UIInput code for updateModel is this:
public void updateModel(FacesContext context)
{
if (!isValid()) return;
if (!isLocalValueSet()) return;
ValueBinding vb = getValueBinding("value");
if (vb == null) return;
try
{
vb.setValue(context, getLocalValue());
setValue(null);
setLocalValueSet(false);
}
catch (RuntimeException e)
{
//Object[] args = {getId()};
context.getExternalContext().log(e.getMessage(), e);
_MessageUtils.addErrorMessage(context,
this,CONVERSION_MESSAGE_ID,new Object[]{getId()});
setValid(false);
}
}
As you see, a RuntimeException is caught. Now, the default value
binding does catch the SemanticException, and wraps it in a
EvaluationException, which is a RuntimeException, so this is caught
here.
We can create a new ValueBinding implementation, and configure JSF to
use it via faces-config. We did, and what our implementation does, is
catch the SemanticException, and wrap it in a special
EvaluationException, whose getMessage(), getLocalMessage() and toString
returns the wrapped SemanticException's getLocalizedMessage(). This
works in the Sun RI, but in MyFaces, as you can see, the message get's
written to the log, and the message that is displayed is a application
default CONVERSION_MESSAGE (which isn't really applicable either, BTW).
Point 1 I want to make is that JSF is really underspecified here. As
far as I can see, the spec does not say clearly what to do with
exceptions from the setters. Hence the different implementations. If
there are any spec leads listening: consider introducing a configurable
component for this, so people can plug in their own exception handling.
Point 2 is that we do not see any solution anymore. Another thing we
tried is to create yet another ValueBinding implementation, that
catches the SemanticException, and that adds its localized message to
the FacesMessages there already, and calls component.setValid(false).
Problem is, that after the above code, we loose all references to the
component. The "vb.setValue(context, getLocalValue())" doesn't pass in
the component. So in the value binding, we cannot set the component's
valid property, and we cannot even use the component id to link the
FacesMessage to the component.
We believe we're stuck, apart from forking the entire MyFaces project,
and changing the code above.
If nobody sees a real solution for this, we suggest that the standard
changes at some time, and that MyFaces takes the lead. We see 3 needs:
1) The JSF spec should define clearly what should happen to model
exceptions.
2) This definition should say that is is configurable, and what the
default is.
3) The default should be to show the exceptions localized message.
and/or
4) The ValueBinding.setValue(FacesContext, Object) method should get an
extra parameter, the component: ValueBinding.setValue(FacesContext,
UIComponent, Object).
The following code should make clear how this could be made
configurable. We suppose the standard says that the
ValueBinding.setValue(...) method needs
to wrap a model exception in a ModelException > FacesException >
RuntimeException, and that there is a configurable model exception
handler.
public interface ModelExceptionHandler {
handleException(FacesContext context, UIComponent component, Throwable
t) throws FacesException;
}
public void updateModel(FacesContext context)
{
if (!isValid()) return;
if (!isLocalValueSet()) return;
ValueBinding vb = getValueBinding("value");
if (vb == null) return;
try {
try {
vb.setValue(context, getLocalValue());
setValue(null);
setLocalValueSet(false);
}
catch (ModelException mExc) {
modelExceptionHandler meh =
context.getModelExceptionHandler();
meh.handleException(context, this, mExc.getCause());
}
}
catch (RuntimeException e)
{
//Object[] args = {getId()};
context.getExternalContext().log(e.getMessage(), e);
_MessageUtils.addErrorMessage(context,
this,CONVERSION_MESSAGE_ID,new Object[]{getId()});
setValid(false);
}
}