convertLoggingResourceableListToString(resourceInfos), new Exception());
return;
}
final String sessionId = session_.getSessionInfo().getSession().getId();
Identity identity = session_.getIdentity();
if (identity==null) {
// no identity available - odd
log_.error("No identity available to UserActivityLogger. Cannot write log entry: "+
crudAction.name()+":"+actionVerb.name()+", "+actionObject+", "+
convertLoggingResourceableListToString(resourceInfos), new Exception());
return;
}
Long identityKey = identity.getKey();
if (actionType!=ActionType.admin) {
final String identityKeyStr = String.valueOf(identityKey);
for (Iterator it = resourceInfos.iterator(); it.hasNext();) {
ILoggingResourceable lr = (ILoggingResourceable) it.next();
if (lr.getResourceableType()==StringResourceableType.targetIdentity) {
if (!lr.getId().equals(identityKeyStr)) {
// complain
log_.error("OLAT-4955: Not storing targetIdentity for non-admin logging actions. A non-admin logging action wanted to store a user other than the one from the session: action="+loggingAction+", fieldId="+loggingAction.getJavaFieldIdForDebug(), new Exception("OLAT-4955 debug stacktrac"));
}
// OLAT-4955: remove targetIdentity
it.remove();
}
}
}
String identityName;
if(isLogAnonymous_ && (actionType != ActionType.admin)) {
identityName = "";
} else {
identityName = identity.getName();
}
// start creating the LoggingObject
final LoggingObject logObj = new LoggingObject(sessionId, identityKey, identityName, crudAction.name().substring(0,1), actionVerb.name(), actionObject);
// do simpleDuration calculation & storing
LoggingObject lastLogObj = (LoggingObject) session_.getEntry(USESS_KEY_USER_ACTIVITY_LOGGING_LAST_LOG);
if (lastLogObj!=null) {
//lastLogObj = (LoggingObject) DBFactory.getInstance().loadObject(lastLogObj);
// DBFactory.getInstance().updateObject(lastLogObj);
// Implementation Note:
// we used to do loadObject(), updateObject() here - which is the preferred best practice hibernate way
// for changing an existing object in the database.
// in the setup @UZH we'll use BLACKHOLE as the storage engine for the o_loggingtable (this is to have
// minimal work load on the Main OLAT DB and not have duplicate data on the Main OLAT DB and the Logging DB).
// Using BLACKHOLE results in the 'lastLogObj' here, not to exist in the database anymore.
// Hence we can't do a loadObject() nor an updateObject(). The latter does not work due to the fact
// that Hibernate checks the number of updated rows and expect that to equal 1 - which is not the case
// when using BLACKHOLE.
// Workaround: - also compare with LoggingObject.hbm.xml docu
//
// We use the sql-update's feature check="none", which disables the above mentioned check.
// Using this in combination with manual SQL code below seems to be the only feasible way
// to have Hibernate not do any row-number-checks.
// Implications of the workaround:
//
// * Manual SQL: It shouldn't be a big deal to have this manual SQL code as it is very standard.
// * CANT USE updateObject(LoggingObject) EVER:
//@TODO We might have to add a check which verifies that no one calls updateObject(LoggingObject)
// if that would be called it would simply fail in the BLACKHOLE@UZH setup
// calculate the duration - take the simple diff of the two creationDate fields
Date currentTime = logObj.getCreationDate();
Date lastTime = lastLogObj.getCreationDate();
long duration;
if (lastTime==null) {
duration = -1;
} else if (currentTime==null) {
duration = System.currentTimeMillis() - lastTime.getTime();
} else {
duration = currentTime.getTime() - lastTime.getTime();
}
DB db = DBFactory.getInstanceForClosing();
if (db!=null && db.isError()) {
// then we would run into an ERROR when we'd do more with this DB
// hence we just issue a log.info here with the details
//@TODO: lower to log_.info once we checked that it doesn't occur very often (best for 6.4)
log_.warn("log: DB is in Error state therefore the UserActivityLoggerImpl cannot update the simpleDuration of log_id "+lastLogObj.getKey()+" with value "+duration+", loggingObject: "+lastLogObj);
} else {
DBQuery update = DBFactory.getInstance().createQuery(
"update org.olat.core.logging.activity.LoggingObject set simpleDuration = :duration where log_id = :logid");
update.setLong("duration", duration);
update.setLong("logid", lastLogObj.getKey());
// we have to do FlushMode.AUTO (which is the default anyway)
update.executeUpdate(FlushMode.AUTO);
}
}
// store the current logging object in the session - for duration calculation at next log
session_.putEntry(USESS_KEY_USER_ACTIVITY_LOGGING_LAST_LOG, logObj);
if (resourceInfos!=null && resourceInfos.size()!=0) {
// this should be the normal case - we do have LoggingResourceables which we can log
// alongside the log message
// check if we have more than 4 - if we do, issue a log and remove the middle ones
if (resourceInfos.size()>4) {
log_.warn("More than 4 resource infos set on a user activity log. Can only have 4. Having: "+resourceInfos.size());
int diff = resourceInfos.size()-4;
for(int i=0; i<diff; i++) {
resourceInfos.remove(3);
}
}
// get the target resourceable
ILoggingResourceable ri = resourceInfos.get(resourceInfos.size()-1);
logObj.setTargetResourceInfo(ri);
// now set parent - if applicable
if (resourceInfos.size()>1) {
ri = resourceInfos.get(resourceInfos.size()-2);
logObj.setParentResourceInfo(ri);
}
// and set the grand parent - if applicable
if (resourceInfos.size()>2) {
ri = resourceInfos.get(resourceInfos.size()-3);
logObj.setGrandParentResourceInfo(ri);
}
// and set the great grand parent - if applicable
if (resourceInfos.size()>3) {
ri = resourceInfos.get(resourceInfos.size()-4);
logObj.setGreatGrandParentResourceInfo(ri);
}
}
// fill the remaining fields
logObj.setBusinessPath(businessPath_);
logObj.setSourceClass(callingClass.getCanonicalName());
logObj.setSimpleDuration(-1);
logObj.setResourceAdminAction(actionType.equals(ActionType.admin)?true:false);
Locale locale = I18nManager.getInstance().getLocaleOrDefault(identity.getUser().getPreferences().getLanguage());
//prepate the user properties, set them at once
List<String> tmpUserProperties = new ArrayList<String>(12);
for(Iterator<String> iterator = userProperties_.iterator(); iterator.hasNext();) {
tmpUserProperties.add(identity.getUser().getPropertyOrIdentityEnvAttribute(iterator.next(), locale));
}
logObj.setUserProperties(tmpUserProperties);
// and store it
DB db = DBFactory.getInstanceForClosing();