Using Session Scoped Entities Locally in Seam

One of the problems faced by Seam users is the disconnect between the session scoped entity instances and using them in more local scopes. A good example is a session scoped User instance that is loaded when the user logs in and is outjected into the session scope. This user instance is available for the duration of the users session.

This user entity is great for displaying who the logged in user is and other information. The problem comes when you need to use that User entity in conjunction with other entities that are managed by the locally scoped entity manager. If an entity has a property that references the current user (such as a createdBy property), then it might seem obvious to assign the User variable value to that value. However, the User is unmanaged by the entity manager for that conversation and will raise errors when you try and save the entity referencing the unmanaged User.

We could make the instance managed before we save it or assign it to the property, but this is a session scoped instance, and it could be shared among multiple conversations. We may want to compare the instances at different times in the same conversation which will break if it has been altered by another conversation. Also, we don’t want to have to write code to handle each property assignment or saving each entity type.

The answer is to have a locally scoped instance of the user that is generated from the session scoped instance. We create a new conversationally scoped bean which will hold the factory for the scoped user instance. The factory method takes the session scoped User instance and merges it with the current entity
manager to get a locally usable instance.

@Name("scopedUserBean")
@Scope(ScopeType.CONVERSATION)
public class ScopedUserBean {

  @In
  private User user;  //injection point for session scoped user

  @In
  private EntityManager entityManager;  

  @Factory("scopedUser")
  public User getScopedUser() {
    return (User) entityManager.merge(user);
  }

}

Now, when we want to reference the user in locally scoped fashion, we can do so using the scopedUser variable. Note also that we can compare the instances either in the server side bean, or even in the JSF page.

@Name("caseEditor")
@Scope(ScopeType.CONVERSATION)
public class CaseEditorBean{

  @In
  private User scopedUser;  

  @In
  private EntityManager entityManager;

  @In
  private Case case;  

  //we can compare scoped user to
  public void closeCase() {
    if (case.createdBy == scopedUser) {
      //this is the users case, they can close it
    } else {
      //this is not your case, you can't close it!
    }
  }

  public String saveCase() {
    case.createdBy = scopedUser;
    entityManager.persist(case);
  }

We can compare the instance on the case with the instance from scopedUser because they are the same instance in the same conversation, and even perform comparisons in EL expressions.

Comments are closed.