1

Implementation: org.glassfish 2.2.12

I have the following session-scoped validator:

@ManagedBean
@SessionScoped
public class CreateGroupNameValidator extends LengthValidator implements Serializable{ 

    @ManagedProperty(value="#{myDao}")
    private MyDao myDao;
    //Validate methods
}

In spite of being session-scoped and Serializable, the validator fails to restore the value of the property myDao when postback comes. I used debugger and figuredOut that the state is saved by the class StateHolderSaver which has the following consturctor:

public StateHolderSaver(FacesContext context, Object toSave) {
    className = toSave.getClass().getName();

    if (toSave instanceof StateHolder) {
        // do not save an attached object that is marked transient.
        if (!((StateHolder) toSave).isTransient()) {
            Serializable [] tuple = new Serializable[StateHolderTupleIndices.LastMember.ordinal()];

            tuple[StateHolderTupleIndices.StateHolderSaverInstance.ordinal()] =
                  (Serializable) ((StateHolder) toSave).saveState(context);
            if (toSave instanceof UIComponent) {
                tuple[StateHolderTupleIndices.ComponentAddedDynamically.ordinal()] = ((UIComponent)toSave).getAttributes().containsKey(DYNAMIC_COMPONENT) ? Boolean.TRUE : Boolean.FALSE;
            }
            savedState = tuple;
        } else {
            className = null;
        }
    } else if (toSave instanceof Serializable) {
        savedState = (Serializable) toSave;
        className = null;
    }
}

So, since LenghtValidator implements javax.faces.component.StateHolder it didn't save my initial Dao value. Is it a normal behavior?

Cœur
  • 37,241
  • 25
  • 195
  • 267
St.Antario
  • 26,175
  • 41
  • 130
  • 318
  • 2
    What is `MyDao`? The name implies that it is a service class or an EJB which cannot be injected using `@ManagedProperty`, if such is a case (and will need annotations like `@EJB`, `@Inject` or `@Autowired`). True validators (or converters) can mostly have either of two scopes only, request scope (`@FacesValidator`) or application scope. The rest of the scopes do not make much sense. – Tiny Nov 03 '15 at 16:56
  • @Tiny It's a a spring bean. – St.Antario Nov 04 '15 at 06:25

1 Answers1

3

This is indeed specified behavior. See also a.o. Validator javadoc:

...

Validator implementations must have a zero-arguments public constructor. In addition, if the Validator class wishes to have configuration property values saved and restored with the view, the implementation must also implement StateHolder.

...

Converters and validators can be saved in JSF state so that the JSF implementation can ensure that they have after restoring the view exactly the intented property values as they were during rendering the view of the previous request (such as minimum and maximum in case of LengthValidator, might it happen that they refer a dynamic EL expression).

Although I must admit that they did during designing the JSF 1.0 specification (on which the converter/validator is still based) not really thought about the possibility to inject business service instances in a JSF converter/validator. You of course don't want to save it in the JSF view state. In case of managed properties (and thus not EJB/CDI proxies), it would only blow up the JSF view state (and thus also the HTTP session in case of server side state saving).

If you don't need your validator being JSF state aware, use composition instead of inheritance.

public class CreateGroupNameValidator { 

    private LengthValidator lengthValidator;

    public CreateGroupNameValidator() {
        lengthValidator = new LengthValidator();
    }

    // ...
}

Nonetheless, putting a validator in the session scope is kind of fishy. This implies that the validator's behavior is specific to the current HTTP session. I can't think of sensible real world use cases for this as they are inherently view scoped (not the validator instances but the validator properties). Usually the session scoped data (such as logged-in user) is instead injected/resolved on a thread local basis. You'd better make it request scoped if it's stateful (i.e. validator properties may vary on a per request/view basis), or application scoped if it's stateless (i.e. validator properties are the same throughout application's lifetime).

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • _can't think of sensible real world use cases for this as they are inherently view scoped_ Indeed, ApplicationScoped would be better. – St.Antario Nov 04 '15 at 06:38
  • _You of course don't want to save it in the JSF view state._ Actually, it wouldn't be safe from the security standpoint. But if the object doesn't implement `StateHolder`, but `Serializable`, when is it saved if not in `javax.faces.viewState`? It's hard to say anything just by `savedState = (Serializable) toSave; className = null;` Or it's an implementaiton dependent thing and we actually don't care about it? – St.Antario Nov 04 '15 at 06:50
  • If it implements `Serializable`, then it's also saved in state. JSF specification says that objects attached to component instances (converters, validators and listeners) are also saved in state. – BalusC Nov 04 '15 at 08:10
  • But then we expose our EJBs to clients in case of client-side view state. Is it safe? DataBase realted EJBs usually contain `DataSource` implementation which in turn holds DB-url and user/passowrd `Properties` – St.Antario Nov 04 '15 at 08:14
  • 1
    EJBs (and CDI beans) are injected as [proxies](http://stackoverflow.com/questions/25514361/when-using-ejb-does-each-managed-bean-get-its-own-ejb-instance/). – BalusC Nov 04 '15 at 08:25