1 Reply Latest reply on Feb 2, 2008 10:58 AM by msystems

    @PersistenceContext

    msystems

      I have a hard time to understand why a seam-managed persistence context (SMPC) @PersistenceContext behaves differently from a non-managed persistence context @PersistenceContext.

      I know I have to use @In to use the SMPC entitymanger - but it looks like Seam also changes the behaviour for @PersistenceContext when SMPC is enabled.

      Example - non-managed (expected behaviour):

      @Stateful
      @Name("authenticatorService")
      @Scope(ScopeType.SESSION)
      public class AuthenticatorBean implements AuthenticatorLocal {
       private static final String CONTEXT_PARAMETER_MAX_ATTEMPTS = "sine.login.max.attempts";
       private static final String CONTEXT_PARAMETER_TIME_LOCK = "sine.login.time.lock";
      
       @Logger
       private Log log;
      
       @PersistenceContext
       private EntityManager em;
      
       @In
       FacesMessages facesMessages;
      
       @EJB
       private UserLocal userService;
      
       @In(create = false, required = false)
       @Out(required = false)
       private User userDomain;
      
       @Out(required = false)
       private User loggedInUserDomain;
      
       private boolean authenticateError;
       private HashMap<String, Integer>loginAttempts = new HashMap<String, Integer>();
      
       public boolean authenticate() {
       authenticateError = false;
      
       userDomain = userService.getUserByUsername(Identity.instance().getUsername());
       if (userDomain != null) {
       if (userDomain.isTimeLocked()) {
       if (System.currentTimeMillis() - userDomain.getTimeLock().getTime()
       > (Long.parseLong(Util.getContextParameter(CONTEXT_PARAMETER_TIME_LOCK)) * 60000)) {
       loginAttempts.put(userDomain.getUsername(), 0);
       userDomain.setTimeLock(null);
       } else {
       facesMessages.addToControlFromResourceBundle("username", FacesMessage.SEVERITY_ERROR,
       "loginTimeLocked");
      
       authenticateError = true;
       return false;
       }
       }
      
       if (userDomain.getPassword()
       .equals(Password.encodePasswordMD5(Identity.instance().getPassword(), userDomain.getId()))) {
       if (!userDomain.isLocked()) {
       for (Role role : userDomain.getRoles()) {
       Identity.instance().addRole(role.getName());
       }
      
       loginAttempts.put(userDomain.getUsername(), 0);
       loggedInUserDomain = userDomain; // Store userDomain in Session
      
       return true;
       }
       }
       } else {
       log.info("Login error - user don't exist: {0}", Identity.instance().getUsername());
       }
      
       facesMessages.addToControlFromResourceBundle("username", FacesMessage.SEVERITY_ERROR, "loginError");
      
       authenticateError = true;
       return false;
       }
      
       @Observer(value = Identity.EVENT_LOGIN_FAILED, create = false)
       public void failedLoginEvent() {
       if ((userDomain != null) && !userDomain.isTimeLocked()) {
       int attempts = loginAttempts.containsKey(userDomain.getUsername())
       ? loginAttempts.get(userDomain.getUsername()) : 0;
      
       if (++attempts >= Integer.parseInt(Util.getContextParameter(CONTEXT_PARAMETER_MAX_ATTEMPTS))) {
       userDomain.setTimeLock(new Date());
       em.merge(userDomain); // I need to merge because entity is detached *************************** ATTENTION ***********
       em.flush();
       log.info("Time lock on user: {0}", userDomain.getUsername());
       } else {
       loginAttempts.put(userDomain.getUsername(), attempts);
       }
       }
       }
      
       public boolean isAuthenticateError() {
       return authenticateError;
       }
      
       @Remove // Seam needs this one for stateful beans
       public void destroy() {
       }
      }
      


      Above example works fine and the database is updated correctly.

      Example - SMPC (I don't need the merge anymore - looks like @PersistenceContext is extended, but why?):

      @Stateful
      @Name("authenticatorService")
      @Scope(ScopeType.SESSION)
      public class AuthenticatorBean implements AuthenticatorLocal {
       private static final String CONTEXT_PARAMETER_MAX_ATTEMPTS = "sine.login.max.attempts";
       private static final String CONTEXT_PARAMETER_TIME_LOCK = "sine.login.time.lock";
      
       @Logger
       private Log log;
      
       @PersistenceContext
       private EntityManager em;
      
       @In
       FacesMessages facesMessages;
      
       @EJB
       private UserLocal userService;
      
       @In(create = false, required = false)
       @Out(required = false)
       private User userDomain;
      
       @Out(required = false)
       private User loggedInUserDomain;
      
       private boolean authenticateError;
       private HashMap<String, Integer>loginAttempts = new HashMap<String, Integer>();
      
       public boolean authenticate() {
       authenticateError = false;
      
       userDomain = userService.getUserByUsername(Identity.instance().getUsername());
       if (userDomain != null) {
       if (userDomain.isTimeLocked()) {
       if (System.currentTimeMillis() - userDomain.getTimeLock().getTime()
       > (Long.parseLong(Util.getContextParameter(CONTEXT_PARAMETER_TIME_LOCK)) * 60000)) {
       loginAttempts.put(userDomain.getUsername(), 0);
       userDomain.setTimeLock(null);
       } else {
       facesMessages.addToControlFromResourceBundle("username", FacesMessage.SEVERITY_ERROR,
       "loginTimeLocked");
      
       authenticateError = true;
       return false;
       }
       }
      
       if (userDomain.getPassword()
       .equals(Password.encodePasswordMD5(Identity.instance().getPassword(), userDomain.getId()))) {
       if (!userDomain.isLocked()) {
       for (Role role : userDomain.getRoles()) {
       Identity.instance().addRole(role.getName());
       }
      
       loginAttempts.put(userDomain.getUsername(), 0);
       loggedInUserDomain = userDomain; // Store userDomain in Session
      
       return true;
       }
       }
       } else {
       log.info("Login error - user don't exist: {0}", Identity.instance().getUsername());
       }
      
       facesMessages.addToControlFromResourceBundle("username", FacesMessage.SEVERITY_ERROR, "loginError");
      
       authenticateError = true;
       return false;
       }
      
       @Observer(value = Identity.EVENT_LOGIN_FAILED, create = false)
       public void failedLoginEvent() {
       if ((userDomain != null) && !userDomain.isTimeLocked()) {
       int attempts = loginAttempts.containsKey(userDomain.getUsername())
       ? loginAttempts.get(userDomain.getUsername()) : 0;
      
       if (++attempts >= Integer.parseInt(Util.getContextParameter(CONTEXT_PARAMETER_MAX_ATTEMPTS))) {
       userDomain.setTimeLock(new Date());
       em.flush();
       log.info("Time lock on user: {0}", userDomain.getUsername());
       } else {
       loginAttempts.put(userDomain.getUsername(), attempts);
       }
       }
       }
      
       public boolean isAuthenticateError() {
       return authenticateError;
       }
      
       @Remove // Seam needs this one for stateful beans
       public void destroy() {
       }
      }
      


      Above example works fine and the database is updated correctly, but I don't need to merge anymore - looks like @PersistenceContext is extended when running under SMPC.

      Could someone explain why @PersistenceContext behaves differently under a SMPC?

      Thanks.