I've written an interceptor that populates Audit information on objects of a particular type (SpeakeasyAuditedObject). Among this audit information is the current User. Acegi gives me a username, and the Interceptor looks up the User.
The problem is that apparently the process of looking up the user by its name is causing the session to be flushed, which causes the saved object to actually be written, which causes a null-constraint error on the not-yet-saved "createdBy" field. Which, of course, is the field I was trying to put the user IN.
How can I look up a new record without causing this to happen?
Right now, I'm only trying to get it working for "save" actions, "update" actions will, of course, be my next task.
Here's the Interceptor code:
Code:
public class AuditInterceptor extends EmptyInterceptor {
private SpeakeasyDomainDao<User> userDao;
static final String DATE_CREATED_PROPERTY = "dateCreated";
static final String CREATED_BY_PROPERTY = "createdBy";
static final String DATE_MODIFIED_PROPERTY = "dateModified";
static final String MODIFIED_BY_PROPERTY = "modifiedBy";
/**
* This class is Serializable so that it may be persisted when sessions move
* or are 'paused' for server restarts.
*/
private static final long serialVersionUID = 1L;
/**
* Modifies the state to add the values for the four friendly fields
*/
@Override
public boolean onSave(Object entity, Serializable id, Object[] state,
String[] propertyNames, Type[] types) {
boolean isChanged = false;
if (entity instanceof SpeakeasyAuditedObject) {
User currentUser = getCurrentUser();
Date now = new Date();
isChanged = true;
for (int i = 0; i < propertyNames.length; i++) {
String propertyName = propertyNames[i];
if (propertyName.equals(DATE_CREATED_PROPERTY)) {
state[i] = now;
} else if (propertyName.equals(CREATED_BY_PROPERTY)) {
state[i] = currentUser;
} else if (propertyName.equals(DATE_MODIFIED_PROPERTY)) {
state[i] = now;
} else if (propertyName.equals(MODIFIED_BY_PROPERTY)) {
state[i] = currentUser;
}
}
}
return isChanged;
}
/**
* Gets the current user's id from the Acegi secureContext
*
* @return current user's userId
*/
private User getCurrentUser() {
SecureContext secureContext = SecureContextUtils.getSecureContext();
assert secureContext != null : "no secure context available";
Authentication auth = secureContext.getAuthentication();
assert auth != null : "no authentication available";
Object principal = auth.getPrincipal();
assert principal != null : "no principal available";
String userName = null;
if (principal instanceof UserDetails) {
UserDetails userDetails = (UserDetails) principal;
userName = userDetails.getUsername();
} else {
userName = principal.toString();
}
// isn't this is going to be very slow?
assert userName != null : "No username available";
User exampleUser = new User();
exampleUser.setName(userName);
User user = userDao.get(exampleUser);
assert user != null : "Unable to determine current user";
return user;
}
public SpeakeasyDomainDao<User> getUserDao() {
return userDao;
}
/**
* Sets the userDao to use to find the User object for the current username.
* This attribute is required.
*/
public void setUserDao(SpeakeasyDomainDao<User> userDao) {
this.userDao = userDao;
}
}
Here's the code for the "SpeakeasyDomainDao.get"
Code:
public abstract class AbstractDao<T> extends HibernateDaoSupport {
(snip)
public T get(T example) {
return (T) getSession().createCriteria(example.getClass()).add(
Example.create(example)).uniqueResult();
}
}