You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@myfaces.apache.org by Mike Kienenberger <mk...@gmail.com> on 2012/09/26 02:00:41 UTC

JSF 2.1 State saving of UIOutput values

I've never paid a lot of detail to the implementation of state saving
up to this point as it never concerned me beyond implementing it in my
own components.

Now that I've upgraded from JSF 1.2 to 2.1, I have hit various state
saving exceptions.  Some were due to t:saveState, which I have since
gotten rid of (most of this project is session scoped, and t:saveState
was a left-over from the original request-scoped project).   Some were
due to trying to create deferred El evaluation in validators, and that
code was likewise replaced with more appropriate code.

However, my latest issue is with a UIInput with a Money converter.
We have a special java class to represent Money amounts.

<h:inputText value="#{page.amount}">
	<my:convertMoney/>
	<my:validatePositiveMoney/>
	<a4j:ajax execute="@this" event="blur"
listener="#{page.paymentAmountChanged}" render="totalsPanelId"/>
</h:inputText>

This has worked for the most part, except when validation of an
unrelated input field on the same page fails.   Then attempting to
continue after that causes the following exception for clazz =
Money.class.  Note that Money isn't serializable.

                throw new RuntimeException("Could not restore
StateHolder of type " + clazz.getName()
                        + " (missing no-args constructor?)", e);
            }

So I traced through the code, and determined that this is because we
are attempting to restore the "value" of the inputText, which is is of
type Money.

I read section 7.7 for StateManager in the JSF 2.1 spec.

	Validators, Converters, FacesListeners, and other objects attached to
a component. The manner in
	which these attached objects are saved is up to the component
implementation. For attached objects that may have
	state, the StateHolder interface (see Section 3.2.4 “StateHolder”) is
provided to allow these objects to preserve
	their own attributes and properties. If an attached object does not
implement StateHolder, but does implement
	Serializable, it is saved using standard serialization. Attached
objects that do not implement either
	StateHolder or Serializable must have a public, zero-arg constructor,
and will be restored only to their
	initial, default object state.

Since this is implementation-specific, we have latitude here.  Why do
we not use the specified converter to create a string if the value
object implements neither StateHolder nor Serializable?   We know the
converter is going to produce reversible reference to the value object
-- it is essentially going to serialize it into a String for us, which
is likely to be far more useful than the initial, default object
state.

It seems to me that we should be doing this for all value objects with
converters.

I know we have to follow the spec otherwise I'd go a step further and
say we should throw an exception rather than create an initial,
default object state, since I'd rather know as soon as possible when a
situation like this turns up rather than trying to debug some
uniitialized object at some point down the road.  Maybe that's
something we can add a oam behavior parameter for.

Re: JSF 2.1 State saving of UIOutput values

Posted by Mike Kienenberger <mk...@gmail.com>.
I think it should be implemented in the core, not in individual components.

If someone is using a non-String value for a UIInput, it is a good bet
that there is a converter available to convert that value from
non-String to String and back.
This is why I think we should add an additional step of trying to use
an existing converter if one exists before defaulting to the no-arg
constructor value.
t:inputCalendar is a composite component, so I am not 100% sure if the
same solution will solve that issue, but the same general approach
makes sense.
Even if it doesn't solve the bridging problem, I am guessing that
there is still a serialization problem for those components.

I think the change should eventually be made in the spec, but we can
enable it with a custom parameter at first.

On Thu, Oct 4, 2012 at 11:43 PM, Leonardo Uribe <lu...@gmail.com> wrote:
> Hi
>
> I feel like a 'deja vu' with this problem. In tomahawk 1.1.10, it was
> introduced
> an interface to deal with a similar problem for t:inputCalendar and t:inputDate
> called DateBusinessConverter. The code looks like this:
>
> /**
>  * Provide a bridge between the java.util.Date instance used by a component
>  * that receive date/time values and the "business" value used to represent
>  * the value.
>  *
>  * @since 1.1.10
>  * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
>  * @version $Revision: 691856 $ $Date: 2008-09-03 21:40:30 -0500 (mié,
> 03 sep 2008) $
>  */
> public interface DateBusinessConverter
> {
>     /**
>      * Convert the java.util.Date instance calculated from submittedValue,
>      * so the resulting object will be used later as the converted value
>      * and validation.
>      *
>      * @param context
>      * @param component
>      * @param value
>      * @return
>      */
>     public Object getBusinessValue(FacesContext context,
>                        UIComponent component,
>                        java.util.Date value);
>
>     /**
>      * Used to retrieve the value stored in the business bean and convert
>      * it in a representation that the component (t:inputCalendar and
>      * t:inputDate for example)using this class can manipulate.
>      *
>      * @param context
>      * @param component
>      * @param value
>      * @return
>      */
>     public java.util.Date getDateValue(FacesContext context,
>                        UIComponent component,
>                        Object value);
> }
>
> In summary, this interface provides a "bridge" between the value to be
> stored in the component (java.util.Date) and the value used in the bean.
> In this case t:inputCalendar understands only java.util.Date instances
> through CalendarDateTimeConverter, but with this we can have any
> non serializable and non stateholder instance on the beans.
>
> I know the intention of DateBusinessConverter is different to what
> is mentioned here, but the concept behind it is still valid. The idea was
> add some application scope object in tomahawk to deal with known
> date types (joda Datetime and others), but that would require an
> alternate faces-context.xml (tomahawk-context.xml).
>
> Something we can do is use the existing <faces-config-extension> to
> do something similar to what is proposed here and update the
> standard components to deal with this. Recently, I added a custom
> config param there, see:
>
> https://issues.apache.org/jira/browse/MYFACES-3612
>
> Is it worth to do it? maybe. This looks like something that you can't
> really fix correctly without put it into myfaces core, but maybe something
> like this should be solved in the spec directly, because otherwise,
> component libraries outside myfaces cannot use it without break
> compatibility with the spec.
>
> regards,
>
> Leonardo Uribe
>
> 2012/9/25 Mike Kienenberger <mk...@gmail.com>:
>> I've never paid a lot of detail to the implementation of state saving
>> up to this point as it never concerned me beyond implementing it in my
>> own components.
>>
>> Now that I've upgraded from JSF 1.2 to 2.1, I have hit various state
>> saving exceptions.  Some were due to t:saveState, which I have since
>> gotten rid of (most of this project is session scoped, and t:saveState
>> was a left-over from the original request-scoped project).   Some were
>> due to trying to create deferred El evaluation in validators, and that
>> code was likewise replaced with more appropriate code.
>>
>> However, my latest issue is with a UIInput with a Money converter.
>> We have a special java class to represent Money amounts.
>>
>> <h:inputText value="#{page.amount}">
>>         <my:convertMoney/>
>>         <my:validatePositiveMoney/>
>>         <a4j:ajax execute="@this" event="blur"
>> listener="#{page.paymentAmountChanged}" render="totalsPanelId"/>
>> </h:inputText>
>>
>> This has worked for the most part, except when validation of an
>> unrelated input field on the same page fails.   Then attempting to
>> continue after that causes the following exception for clazz =
>> Money.class.  Note that Money isn't serializable.
>>
>>                 throw new RuntimeException("Could not restore
>> StateHolder of type " + clazz.getName()
>>                         + " (missing no-args constructor?)", e);
>>             }
>>
>> So I traced through the code, and determined that this is because we
>> are attempting to restore the "value" of the inputText, which is is of
>> type Money.
>>
>> I read section 7.7 for StateManager in the JSF 2.1 spec.
>>
>>         Validators, Converters, FacesListeners, and other objects attached to
>> a component. The manner in
>>         which these attached objects are saved is up to the component
>> implementation. For attached objects that may have
>>         state, the StateHolder interface (see Section 3.2.4 “StateHolder”) is
>> provided to allow these objects to preserve
>>         their own attributes and properties. If an attached object does not
>> implement StateHolder, but does implement
>>         Serializable, it is saved using standard serialization. Attached
>> objects that do not implement either
>>         StateHolder or Serializable must have a public, zero-arg constructor,
>> and will be restored only to their
>>         initial, default object state.
>>
>> Since this is implementation-specific, we have latitude here.  Why do
>> we not use the specified converter to create a string if the value
>> object implements neither StateHolder nor Serializable?   We know the
>> converter is going to produce reversible reference to the value object
>> -- it is essentially going to serialize it into a String for us, which
>> is likely to be far more useful than the initial, default object
>> state.
>>
>> It seems to me that we should be doing this for all value objects with
>> converters.
>>
>> I know we have to follow the spec otherwise I'd go a step further and
>> say we should throw an exception rather than create an initial,
>> default object state, since I'd rather know as soon as possible when a
>> situation like this turns up rather than trying to debug some
>> uniitialized object at some point down the road.  Maybe that's
>> something we can add a oam behavior parameter for.

Re: JSF 2.1 State saving of UIOutput values

Posted by Leonardo Uribe <lu...@gmail.com>.
Hi

I feel like a 'deja vu' with this problem. In tomahawk 1.1.10, it was
introduced
an interface to deal with a similar problem for t:inputCalendar and t:inputDate
called DateBusinessConverter. The code looks like this:

/**
 * Provide a bridge between the java.util.Date instance used by a component
 * that receive date/time values and the "business" value used to represent
 * the value.
 *
 * @since 1.1.10
 * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
 * @version $Revision: 691856 $ $Date: 2008-09-03 21:40:30 -0500 (mié,
03 sep 2008) $
 */
public interface DateBusinessConverter
{
    /**
     * Convert the java.util.Date instance calculated from submittedValue,
     * so the resulting object will be used later as the converted value
     * and validation.
     *
     * @param context
     * @param component
     * @param value
     * @return
     */
    public Object getBusinessValue(FacesContext context,
                       UIComponent component,
                       java.util.Date value);

    /**
     * Used to retrieve the value stored in the business bean and convert
     * it in a representation that the component (t:inputCalendar and
     * t:inputDate for example)using this class can manipulate.
     *
     * @param context
     * @param component
     * @param value
     * @return
     */
    public java.util.Date getDateValue(FacesContext context,
                       UIComponent component,
                       Object value);
}

In summary, this interface provides a "bridge" between the value to be
stored in the component (java.util.Date) and the value used in the bean.
In this case t:inputCalendar understands only java.util.Date instances
through CalendarDateTimeConverter, but with this we can have any
non serializable and non stateholder instance on the beans.

I know the intention of DateBusinessConverter is different to what
is mentioned here, but the concept behind it is still valid. The idea was
add some application scope object in tomahawk to deal with known
date types (joda Datetime and others), but that would require an
alternate faces-context.xml (tomahawk-context.xml).

Something we can do is use the existing <faces-config-extension> to
do something similar to what is proposed here and update the
standard components to deal with this. Recently, I added a custom
config param there, see:

https://issues.apache.org/jira/browse/MYFACES-3612

Is it worth to do it? maybe. This looks like something that you can't
really fix correctly without put it into myfaces core, but maybe something
like this should be solved in the spec directly, because otherwise,
component libraries outside myfaces cannot use it without break
compatibility with the spec.

regards,

Leonardo Uribe

2012/9/25 Mike Kienenberger <mk...@gmail.com>:
> I've never paid a lot of detail to the implementation of state saving
> up to this point as it never concerned me beyond implementing it in my
> own components.
>
> Now that I've upgraded from JSF 1.2 to 2.1, I have hit various state
> saving exceptions.  Some were due to t:saveState, which I have since
> gotten rid of (most of this project is session scoped, and t:saveState
> was a left-over from the original request-scoped project).   Some were
> due to trying to create deferred El evaluation in validators, and that
> code was likewise replaced with more appropriate code.
>
> However, my latest issue is with a UIInput with a Money converter.
> We have a special java class to represent Money amounts.
>
> <h:inputText value="#{page.amount}">
>         <my:convertMoney/>
>         <my:validatePositiveMoney/>
>         <a4j:ajax execute="@this" event="blur"
> listener="#{page.paymentAmountChanged}" render="totalsPanelId"/>
> </h:inputText>
>
> This has worked for the most part, except when validation of an
> unrelated input field on the same page fails.   Then attempting to
> continue after that causes the following exception for clazz =
> Money.class.  Note that Money isn't serializable.
>
>                 throw new RuntimeException("Could not restore
> StateHolder of type " + clazz.getName()
>                         + " (missing no-args constructor?)", e);
>             }
>
> So I traced through the code, and determined that this is because we
> are attempting to restore the "value" of the inputText, which is is of
> type Money.
>
> I read section 7.7 for StateManager in the JSF 2.1 spec.
>
>         Validators, Converters, FacesListeners, and other objects attached to
> a component. The manner in
>         which these attached objects are saved is up to the component
> implementation. For attached objects that may have
>         state, the StateHolder interface (see Section 3.2.4 “StateHolder”) is
> provided to allow these objects to preserve
>         their own attributes and properties. If an attached object does not
> implement StateHolder, but does implement
>         Serializable, it is saved using standard serialization. Attached
> objects that do not implement either
>         StateHolder or Serializable must have a public, zero-arg constructor,
> and will be restored only to their
>         initial, default object state.
>
> Since this is implementation-specific, we have latitude here.  Why do
> we not use the specified converter to create a string if the value
> object implements neither StateHolder nor Serializable?   We know the
> converter is going to produce reversible reference to the value object
> -- it is essentially going to serialize it into a String for us, which
> is likely to be far more useful than the initial, default object
> state.
>
> It seems to me that we should be doing this for all value objects with
> converters.
>
> I know we have to follow the spec otherwise I'd go a step further and
> say we should throw an exception rather than create an initial,
> default object state, since I'd rather know as soon as possible when a
> situation like this turns up rather than trying to debug some
> uniitialized object at some point down the road.  Maybe that's
> something we can add a oam behavior parameter for.