2

I was suggested by someone to use #view.attributes map to store view scoped data that I need to survive even after session destroys(with client side state saving). Now that it works perfect for my requirement but I just want to make sure this is not a bad practice or bad thing to do.

I found that this works exactly like viewscope map except for it survives the data even after user session was destroyed.

Community
  • 1
  • 1
Rajat Gupta
  • 25,853
  • 63
  • 179
  • 294

2 Answers2

3

In JSF versions prior to 2.0 it was only possible to store objects in the view only by using UIViewRoot.set/getAttribute.

However in JSF 2.0 a separate view scope was introduced that can be used in EL via #{viewScope} or programatically using UIViewRoow.getViewMap(). Using the view scope is the recommended way to go. It is implemented using a Map that is kept in the UIViewRoot and is serialized the same way as the view attributes in thew view state so it has the same life as the view attributes.

UPDATE

According to Leonardo Uribe from MyFaces team:

In JSF 2.2 it was decided to store view scope beans always in session (take a look at the description of @ViewScoped annotation in the javadoc). But you can just call facesContext.getViewRoot() and use the attribute map. Just remember the values there must be Serializable or implement StateHolder.

So it looks like the portable way is to use the attributes map in UIViewRoot.

Adrian Mitev
  • 4,722
  • 3
  • 31
  • 53
  • but viewscope is stored in session & thus it is lost when user session is destroyed (which has happens very quickly in my app), so how could I make the viewscope data survive even after session destroys ? Should I design a custom implementation for this data ? any ideas? – Rajat Gupta Mar 30 '14 at 10:33
  • The view is serialized in the session if your state saving method is server. If the method is client, the view state is serialized, encoded as base64 and sent to the browser where it is stored in hidden field and sent back to the server when the next request is performed. Thus even if the session expires on the server, the client will provide that view state and it will be successfully restored. – Adrian Mitev Mar 30 '14 at 12:41
  • Looking at the implementation of UIViewRoot, the component attributes are saved with the view map so their lifecycle should be the same. What JSF implementation and version are you using? – Adrian Mitev Mar 30 '14 at 12:49
  • Thanks Adrian, for digging deeper into this. I am using **`Myfaces 2.2.2`**. But I did tried this out practically, whenever I am storing in viewscope map(with client side state saving on) I am unable to restore the viewscope stored data after session is destroyed. For your reference, here is the link for this issue posted in myfaces mailing list & solution proposed by some users(to use `#view.attributes`) https://www.mail-archive.com/users@myfaces.apache.org/msg59924.html – Rajat Gupta Mar 30 '14 at 15:35
  • Are you looking at the mojarra implementation & does it do it differently ? – Rajat Gupta Mar 30 '14 at 15:36
  • I'm looking at mojarra. However the both JSF implementations should support that. – Adrian Mitev Mar 30 '14 at 18:02
  • & what specific version of mojarra ? As per my testing, Myfaces 2.2 does not support this. – Rajat Gupta Mar 30 '14 at 18:04
  • Well, it looks like I was wrong because my statement and the source code I'm looking is for jsf 2.1. Accorging to Leonardo Uribe - In JSF 2.2 it was decided to store view scope beans always in session (take a look at the description of @ViewScoped annotation in the javadoc). I'll update my answer here. – Adrian Mitev Mar 30 '14 at 18:05
  • Yes,, that's was I was referring to in that link. So, what should be my solution now ? Ideas ? – Rajat Gupta Mar 30 '14 at 18:06
  • But as @Michele Mariotti points out this is not at all an efficient/performant solution, so are there any alternative solutions, Or should I go about writing my custom implementation (that would save the viewscoped data as a hidden field in my jsf pages) ? – Rajat Gupta Mar 30 '14 at 18:12
  • I agree that it's less efficient than using the view scope map directly. However I wouldn't worry about any performance here as it would be a problem only if you store many many attributes in the view attributes map. – Adrian Mitev Mar 30 '14 at 18:18
0

using viewScope is recommended. avoid using view root (component in general) attributes for two reasons:

  1. in theory, there exists the possibility that you overwrite a view root component attribute. in practice it is not possible since attribute keys are implemented with an enum (at least in mojarra)

  2. UIComponentBase.getAttributes returns a particular Map implementation: AttributesMap. this implementation, at first, checks if exists a method with the same name of map key, in the component. and if not not, it checks the internal map. if again it is not found, it checks the component ValueExpression map. so it is not efficient at all, and, in very particular case, can lead to infinite recursion.

take a look at AttributesMap.get, in example:

public Object get(Object keyObj) {
    String key = (String) keyObj;
    Object result = null;
    if (key == null) {
        throw new NullPointerException();
    }
    if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(key)) {
        result = component.getStateHelper().get(UIComponent.PropertyKeysPrivate.attributesThatAreSet);
    }
    Map<String,Object> attributes = (Map<String,Object>)
          component.getStateHelper().get(PropertyKeys.attributes);
    if (null == result) {
        PropertyDescriptor pd =
                getPropertyDescriptor(key);
        if (pd != null) {
            try {
                Method readMethod = pd.getReadMethod();
                if (readMethod != null) {
                    result = (readMethod.invoke(component,
                            EMPTY_OBJECT_ARRAY));
                } else {
                    throw new IllegalArgumentException(key);
                }
            } catch (IllegalAccessException e) {
                throw new FacesException(e);
            } catch (InvocationTargetException e) {
                throw new FacesException(e.getTargetException());
            }
        } else if (attributes != null) {
            if (attributes.containsKey(key)) {
                result = attributes.get(key);
            }
        }
    }
    if (null == result) {
        ValueExpression ve = component.getValueExpression(key);
        if (ve != null) {
            try {
                result = ve.getValue(component.getFacesContext().getELContext());
            } catch (ELException e) {
                throw new FacesException(e);
            }
        }
    }

    return result;
}

thinking about your requisite, it makes little sense, at least from a webapp point of view. view state remembers component states: component model values that are changed from inital state and are to be processed next. if these values have not to be processed then there's no need to be rembered for a long time. we can think about them as "transient". conversely, if they have to be processed and remembered for a long time, persistence is the way. indeed i can't think a single case for this kind of data to survive for a longer time than session, and shorter than forever (persistence).

can you tell us a real life example?

the best example that comes in my mind is remember the active index for a tabView or an accordion, but this value can (and should) be persisted, if it is important.

however every problem has a solution, the first thing i can think is that you may implement a custom scope that stores these values in application scope using a specific cookie (client) value as key.

Michele Mariotti
  • 7,372
  • 5
  • 41
  • 73
  • but viewscope is stored in session & thus it is lost when user session is destroyed (which has happens very quickly in my app), so how could I make the viewscope data survive even after session destroys ? Should I design a custom implementation for this data ? any ideas? – Rajat Gupta Mar 30 '14 at 10:35