tags:

views:

44

answers:

2

Assume I have a session scoped bean with object references in it, where the bean extends from an abstract generic base class. Example:

public abstract class AbstractCrudController<K, E> implements Serializable {
  private Class<E> entityClass;
  private E user;

  public E getUser() {
      if (user == null) {
          try {
              user = entityClass.newInstance();
          } catch (Exception ex) {
              JsfUtil.addErrorMessage(ex, JsfUtil.getStringResource("fatalError"));
              return null;
          }
      }
      return user;
  }

  public AbstractCrudController(Class<E> entityClass) {
    this.entityClass = entityClass;
  }
}

@ManagedBean
@SessionScoped
public class UserController extends AbstractCrudController<String, User> implements Serializable {
  public UserController() {
      super(User.class);
  }
}

And I'm accessing the properties of user in JSF via EL, like so:

<h:inputText id="firstName" title="#{str.firstName}"
              value="#{userController.user.firstName}" >
    <f:ajax render="outputText"/>
</h:inputText>

<h:outputText id="outputText" value="Hello World" rendered="#{not empty userController.user.firstName}" />

What I observed is in the debugger is that the user object does not remain alive across the ajax request. I.e. when the outputText is rendered, the user is null. I found out that this is because the user is a generic type. When I make the user instance variable of type User in the base class, everything works. But I have no explanation for this.

Any idea? Thank, Theo

A: 

This is not normal behaviour. I am not sure about that @Named thing and which of the various @SessionScoped annotations you used, but normally, in "plain" JSF 2.0 you'd like to annotate it with javax.faces.bean.ManagedBean in combination with javax.faces.bean.SessionScoped to get it to work as you'd intuitively expect.

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
public class UserController {}

Update as per your updated question. Your backing bean code is all fine. The problem is however in the view side. The rendered attribute will evaluate false in the initial request and hence the HTML output won't end up in the HTML DOM tree in the client side. When Ajax is attempting to replace/refresh the HTML element in question in the HTML DOM tree, it simply cannot locate it and hence you won't see anything to happen.

During the occurrences when it seemed to work, you probably hit Enter which would cause a synchronous request instead of an asynchronous request and thus force JSF to send a brand new full response back, with the element present.

You'd like to avoid using rendered attribute directly in JSF components whose direct HTML output is to be located in the HTML DOM tree during partial updates. If you change the h:outputText as follows, it will work as intented:

<h:panelGroup id="outputText">
    <h:outputText value="Hello World" rendered="#{not empty userController.user.firstName}" />
</h:panelGroup>

Note that User was never been null as you initially mentioned in your question. I am not sure how you came to this conclusion, but this is certainly not the normal behaviour and that was where my initial answer was all about.

BalusC
I get the same behavior when I use the JSF annotations. I think I've found the reason, but I have no explanation for this. My user object resides in an abstract super class and whose type is generic. When I move the user object to the subclass, it works. Do you have any idea why inheritance might affect this?
Theo
I have edited the question to reflect the generic crud controller.
Theo
I didn't press enter or anything like that. I fired the ajax event by changing the value of the input text. Then, I stepped in the debugger through the code and saw that at some point the user object had a different object id and was null. But I have no explanation for this. I also tried it without the rendered attribute. When I put a concrete User type in the base class (instead of the generic type E), the strange behavior does not occur. But how can this be related?
Theo
Your code ran fine here on Mojarra 2.0.3 + Tomcat 6.0.29. Maybe there's more into the code which you omitted in the question by oversimplifying. I just had used a simple page with a single `h:form` with therein the input and output. Your bean code was identical. I only created an `User` class with a `firstName` property.
BalusC
Yeah, you're right. I just created a small sample project with the code, and it worked fine. There's probably something else. I'll try to find out and will post it when I have more information. Thanks for bearing with me.
Theo
Just found out what was wrong. See my posted answer.
Theo
A: 

The problem was in my code. On my JSF page, I additionally registered the following event listener for preRenderView:

<f:metadata>
    <f:event type="preRenderView" listener="#{userController.prepareCreate}" />
</f:metadata>

The method that listens to this event resets the user object, which is why the user object was null afterwards. I did not know that this event is called between ajax requests. Is there any way to prevent this?

Theo
You should rahter have posted this as a new question, your original question is been answered :) Anyway, depending on the functional requirement, you'd like to either attach the `f:event` to a component which isn't involved in partial updates, or to create a `@ViewScoped` bean and annotate `prepareCreate()` method with `@PostConstruct`.
BalusC
Sorry, I'm new to Stack Overflow and I'm just learning the rules :).
Theo