5 Replies Latest reply on Feb 26, 2010 1:53 AM by janhh

    Change entity on session bean timeout

    janhh
      Hello,

      I got a weird seam problem.

      The user selects an entity, which then is loaded into a session bean where it remains while the user edits it, until the user logs out or selects another entity. To prevent the entity being selected by another user, it's got a boolean property 'selected', which is set to true when the entity is selected, and false when editing is finished.

      The session bean has session scope.

      Now there is the possibility of the user simply walking away.. in this case, there is the need to 'free' a selected entity on the user's session timeout. Nothing very special, but somehow it simply doesn't work. Code is straight, two versions, neither of it works, with different error messages (the session bean is a statefule EJB):

      a)

      @In
      private EntityManager entityManager

      @Destroy
      public void cleanup()
      {
         myEntity.setSelected(false);
         entityManager.merge(myEntity);
      }

      b)

      @PersistenceContext
      private EntityManager entityManager

      @PreDestroy
      public void cleanup()
      {
         myEntity.setSelected(false);
         entityManager.merge(myEntity);
      }

      Errors a:

      06:32:17,078 ERROR [AssertionFailure] an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
      org.hibernate.AssertionFailure: cannot copy a reference to an object with a null id
         at org.hibernate.type.EntityType.replace(EntityType.java:255)

      Errors b:

      06:38:30,718 WARN  [Component] Exception calling stateful session bean default @Remove method: startProject
      java.lang.RuntimeException: javax.persistence.TransactionRequiredException: EntityManager must be access within a transaction
         at org.jboss.ejb3.interceptor.LifecycleInterceptorHandler.preDestroy(LifecycleInterceptorHandler.java:135

      Could it be an issue with the versions I am using (a little older, Seam 2.0.2 / JBoss AS 4.2.2)? Or am I getting something completely wrong?

      Thanks a lot
      Jan
        • 1. Re: Change entity on session bean timeout
          kragoth

          Why not just use an Application Scoped bean that registers which user has selected which entity (and in which session if you support multiple windows).


          Then cleaning up is easy and you are not polluting your domain entities with application logic.


          Obviously all the methods in the Application scoped bean should be synchronized to avoid two people getting access to the same entity.


          This is how I have solved the same basic problem in my app.

          • 2. Re: Change entity on session bean timeout
            janhh

            how does the application scoped bean know when a user has a session timeout?

            • 3. Re: Change entity on session bean timeout
              kragoth

              Well, I guess I did leave out the step of how to work that out :)


              All you need to do is create a session listener.
              I just looked at my code, I didn't use an application scoped bean.. well, I did but then decided to just make it a class with static variables/methods.


              So this is my session listener.


              public class GekkoSessionListener implements HttpSessionListener {
                  
                  private static final Logger log = 
                      LogManager.getLogger(GekkoSessionListener.class);
              
                  @Override
                  public void sessionCreated(HttpSessionEvent e) {
                      log.trace("Session: " + e.getSession().getId() + " created");
                  }
              
                  @Override
                  public void sessionDestroyed(HttpSessionEvent e) {
                      log.trace("Session: " + e.getSession().getId() + " destroyed");
                      UserSessionCertificateStore.removeHttpSession(e.getSession());
                  }
              
              }
              



              Obviously the line


              UserSessionCertificateStore.removeHttpSession(e.getSession());
              



              is the important one. That is where I remove the session from my map of who is editing which entity (Certificate in my case).


              Then all you need to do is create the class that will hold the value of which user has which entity.


              So something like (THIS IS JUST MADE UP AND PROBABLY DOESN'T WORK, BUT IT IS THE GENERAL IDEA)


              public final class UserEntityStore {
              
                  private static final Map<User, Entity> userEntityMap = new Map...;
                  private static final Map<HttpSession, User> userSessionMap = new Map...;
              
                  //Call this method on a successful login
                  public synchronized static void setupSession(User user, HttpSession session)  {
                      userSessionMap.put(session, user);
                  }
              
                  public synchronized static void setEntityForUser(Entity e, User u) {
                      if (isEntityInUseByOtherUser(e, u)) {
                          //Raise error about not being able to edit this entity because another user has it.
                          return;
                      }
              
                      userEntityMap.put(u, e);
                  }
              
                  public boolean isEntityInUseByOtherUser(Entity e, User u) {
                      //Search the map for the entity and return true if found and in use by a different user
                      //else return false;
                  }
              
                  public void sessionTimeout(HttpSession session) {
                      User u = userSessionMap.get(session);
                      userEntityMap.remove(u);
                      userSessionMap.remove(session);
                  }
              }
              




              I hope this helps. :)

              • 4. Re: Change entity on session bean timeout
                kragoth

                Grr, all those methods should be synchronized OK :)


                Well, actually. I'm not really up to speed on the 'correct' usage of synchronized but that's the general idea. Only 1 thread at a time should execute those lines that access those maps.

                • 5. Re: Change entity on session bean timeout
                  janhh

                  Thanks but what you do seems somehow weird to me. It might work, but I guess it is not the default way how this should be done. Tracking which user is editing which entity with a map just to do some cleanup on session timeout cannot be The Right Way, I guess.