@PersistenceContext
msystems Feb 1, 2008 6:21 PMI 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.