@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.