Audit queries

- Any combination of application (e.g. RM, repo, etc), user and time
 - TODO: Extend queries to support finding audit entries by arbitrary audited values
 - TODO: Full map retrieval in single query


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@16086 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2009-09-04 00:34:28 +00:00
parent 4f60e2608d
commit 034027961d
26 changed files with 843 additions and 212 deletions

View File

@@ -85,6 +85,10 @@
<bean name="auditModel.extractor.simpleValue" class="org.alfresco.repo.audit.extractor.SimpleValueDataExtractor"> <bean name="auditModel.extractor.simpleValue" class="org.alfresco.repo.audit.extractor.SimpleValueDataExtractor">
<property name="registry" ref="auditModel.extractorRegistry" /> <property name="registry" ref="auditModel.extractorRegistry" />
</bean> </bean>
<bean name="auditModel.extractor.nodeName" class="org.alfresco.repo.audit.extractor.NodeNameDataExtractor">
<property name="registry" ref="auditModel.extractorRegistry" />
<property name="nodeService" ref="nodeService" />
</bean>
<!-- Data Generators --> <!-- Data Generators -->
<bean id="auditModel.generatorRegistry" class="org.alfresco.util.registry.NamedObjectRegistry"> <bean id="auditModel.generatorRegistry" class="org.alfresco.util.registry.NamedObjectRegistry">
@@ -96,6 +100,11 @@
<bean name="auditModel.generator.user" class="org.alfresco.repo.audit.generator.AuthenticatedUserDataGenerator"> <bean name="auditModel.generator.user" class="org.alfresco.repo.audit.generator.AuthenticatedUserDataGenerator">
<property name="registry" ref="auditModel.generatorRegistry" /> <property name="registry" ref="auditModel.generatorRegistry" />
</bean> </bean>
<bean name="auditModel.generator.personFullName" class="org.alfresco.repo.audit.generator.AuthenticatedPersonDataGenerator">
<property name="registry" ref="auditModel.generatorRegistry" />
<property name="personService" ref="personService" />
<property name="nodeService" ref="nodeService" />
</bean>
<!-- Models --> <!-- Models -->
<bean id="auditModel.modelRegistry" class="org.alfresco.repo.audit.model.AuditModelRegistry"> <bean id="auditModel.modelRegistry" class="org.alfresco.repo.audit.model.AuditModelRegistry">

View File

@@ -78,6 +78,7 @@ CREATE TABLE alf_prop_link
value_prop_id BIGINT NOT NULL, value_prop_id BIGINT NOT NULL,
key_prop_id BIGINT NOT NULL, key_prop_id BIGINT NOT NULL,
INDEX idx_alf_prop_coll_rev (value_prop_id, root_prop_id), INDEX idx_alf_prop_coll_rev (value_prop_id, root_prop_id),
CONSTRAINT fk_alf_prop_link_root FOREIGN KEY (root_prop_id) REFERENCES alf_prop_value (id) ON DELETE CASCADE,
PRIMARY KEY (root_prop_id, current_prop_id, value_prop_id, key_prop_id) PRIMARY KEY (root_prop_id, current_prop_id, value_prop_id, key_prop_id)
) ENGINE=InnoDB; ) ENGINE=InnoDB;

View File

@@ -13,6 +13,8 @@
<typeAlias alias="AuditModel" type="org.alfresco.repo.domain.audit.AuditModelEntity"/> <typeAlias alias="AuditModel" type="org.alfresco.repo.domain.audit.AuditModelEntity"/>
<typeAlias alias="AuditApplication" type="org.alfresco.repo.domain.audit.AuditApplicationEntity"/> <typeAlias alias="AuditApplication" type="org.alfresco.repo.domain.audit.AuditApplicationEntity"/>
<typeAlias alias="AuditEntry" type="org.alfresco.repo.domain.audit.AuditEntryEntity"/> <typeAlias alias="AuditEntry" type="org.alfresco.repo.domain.audit.AuditEntryEntity"/>
<typeAlias alias="AuditQueryParameters" type="org.alfresco.repo.domain.audit.AuditQueryParameters"/>
<typeAlias alias="AuditQueryResult" type="org.alfresco.repo.domain.audit.AuditQueryResult"/>
<!-- --> <!-- -->
<!-- Result Maps --> <!-- Result Maps -->
@@ -35,6 +37,13 @@
<result property="auditTime" column="audit_time" jdbcType="BIGINT" javaType="long"/> <result property="auditTime" column="audit_time" jdbcType="BIGINT" javaType="long"/>
<result property="auditValuesId" column="audit_values_id" jdbcType="BIGINT" javaType="long"/> <result property="auditValuesId" column="audit_values_id" jdbcType="BIGINT" javaType="long"/>
</resultMap> </resultMap>
<resultMap id="result.AuditQuery" class="AuditQueryResult">
<result property="auditEntryId" column="audit_entry_id" jdbcType="BIGINT" javaType="java.lang.Long"/>
<result property="auditAppName" column="audit_app_name" jdbcType="VARCHAR" javaType="string"/>
<result property="auditUser" column="audit_user" jdbcType="VARCHAR" javaType="string"/>
<result property="auditTime" column="audit_time" jdbcType="BIGINT" javaType="long"/>
<result property="auditValuesId" column="audit_values_id" jdbcType="BIGINT" javaType="long"/>
</resultMap>
<!-- --> <!-- -->
<!-- Parameter Maps --> <!-- Parameter Maps -->
@@ -43,6 +52,14 @@
<parameterMap id="parameter.IdMap" class="map"> <parameterMap id="parameter.IdMap" class="map">
<parameter property="id" jdbcType="BIGINT" javaType="java.lang.Long"/> <parameter property="id" jdbcType="BIGINT" javaType="java.lang.Long"/>
</parameterMap> </parameterMap>
<parameterMap id="parameter.AuditQuery" class="AuditQueryParameters">
<parameter property="auditAppNameShort" jdbcType="VARCHAR" javaType="string"/>
<parameter property="auditAppNameCrc" jdbcType="BIGINT" javaType="long"/>
<parameter property="auditUserShort" jdbcType="VARCHAR" javaType="string"/>
<parameter property="auditUserCrc" jdbcType="BIGINT" javaType="long"/>
<parameter property="auditFromTime" jdbcType="BIGINT" javaType="long"/>
<parameter property="auditToTime" jdbcType="BIGINT" javaType="long"/>
</parameterMap>
<!-- --> <!-- -->
<!-- SQL Snippets --> <!-- SQL Snippets -->
@@ -87,4 +104,39 @@
audit_model_id = ? audit_model_id = ?
</select> </select>
<!-- Get audit entries by application name -->
<!-- TODO: Use compound parameters for string searching, possibly namespaced from propval -->
<!-- TODO: Use compound result map for audit values -->
<select id="select.AuditEntriesSimple" parameterClass="AuditQueryParameters" resultMap="result.AuditQuery">
select
entry.id as audit_entry_id,
user_sv.string_value as audit_user,
app_sv.string_value as audit_app_name,
entry.audit_time as audit_time,
entry.audit_values_id as audit_values_id
from
alf_audit_app app
join alf_prop_value app_pv on (app_pv.id = app.app_name_id)
join alf_prop_string_value app_sv on (app_sv.id = app_pv.long_value and app_pv.persisted_type = 3)
join alf_audit_entry entry on (entry.audit_app_id = app.id)
join alf_prop_value user_pv on (user_pv.id = entry.audit_user_id)
join alf_prop_string_value user_sv on (user_sv.id = user_pv.long_value and user_pv.persisted_type = 3)
<dynamic prepend="where">
<isNotNull prepend="and" property="auditAppNameShort">
app_sv.string_end_lower = #auditAppNameShort# and
app_sv.string_crc = #auditAppNameCrc#
</isNotNull>
<isNotNull prepend="and" property="auditUserShort">
user_sv.string_end_lower = #auditUserShort# and
user_sv.string_crc = #auditUserCrc#
</isNotNull>
<isNotNull prepend="and" property="auditFromTime">
<![CDATA[entry.audit_time >= #auditFromTime#]]>
</isNotNull>
<isNotNull prepend="and" property="auditToTime">
<![CDATA[entry.audit_time < #auditToTime#]]>
</isNotNull>
</dynamic>
</select>
</sqlMap> </sqlMap>

View File

@@ -31,6 +31,7 @@ import java.util.Map;
import org.alfresco.repo.audit.model.AuditApplication; import org.alfresco.repo.audit.model.AuditApplication;
import org.alfresco.repo.audit.model._3.AuditPath; import org.alfresco.repo.audit.model._3.AuditPath;
import org.alfresco.service.cmr.audit.AuditInfo; import org.alfresco.service.cmr.audit.AuditInfo;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.aopalliance.intercept.MethodInvocation; import org.aopalliance.intercept.MethodInvocation;
@@ -107,4 +108,18 @@ public interface AuditComponent
* @since 3.2 * @since 3.2
*/ */
Map<String, Serializable> audit(String applicationName, String rootPath, Map<String, Serializable> values); Map<String, Serializable> audit(String applicationName, String rootPath, Map<String, Serializable> values);
/**
* Get the audit entries that match the given criteria.
*
* @param callback the callback that will handle results
* @param applicationName if not <tt>null</tt>, find entries logged against this application
* @param user if not <tt>null</tt>, find entries logged against this user
* @param from the start search time (<tt>null</tt> to start at the beginning)
* @param to the end search time (<tt>null</tt> for no limit)
* @param maxResults the maximum number of results to retrieve (zero or negative to ignore)
*/
void auditQuery(
AuditQueryCallback callback,
String applicationName, String user, Long from, Long to, int maxResults);
} }

View File

@@ -34,6 +34,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.audit.extractor.DataExtractor; import org.alfresco.repo.audit.extractor.DataExtractor;
import org.alfresco.repo.audit.generator.DataGenerator; import org.alfresco.repo.audit.generator.DataGenerator;
import org.alfresco.repo.audit.model.AuditApplication; import org.alfresco.repo.audit.model.AuditApplication;
@@ -49,6 +50,7 @@ import org.alfresco.service.Auditable;
import org.alfresco.service.NotAuditable; import org.alfresco.service.NotAuditable;
import org.alfresco.service.PublicService; import org.alfresco.service.PublicService;
import org.alfresco.service.cmr.audit.AuditInfo; import org.alfresco.service.cmr.audit.AuditInfo;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
@@ -883,30 +885,16 @@ public class AuditComponentImpl implements AuditComponent
final Serializable data; final Serializable data;
try try
{ {
data = extractor.convert(value); data = extractor.extractData(value);
} }
catch (Throwable e) catch (Throwable e)
{ {
Log extractorLogger = LogFactory.getLog(extractor.getClass()); throw new AlfrescoRuntimeException(
if (extractorLogger.isDebugEnabled()) "Failed to extract audit data: \n" +
{ " Path: " + path + "\n" +
extractorLogger.debug( " Raw value: " + value + "\n" +
"Failed to extract audit data: \n" + " Extractor: " + extractor,
" Path: " + path + "\n" + e);
" Raw value: " + value + "\n" +
" Extractor: " + extractor,
e);
}
else
{
extractorLogger.warn(
"Failed to extract audit data (turn on DEBUG for full stack): \n" +
" Path: " + path + "\n" +
" Raw value: " + value + "\n" +
" Extractor: " + extractor + "\n" +
" Error: " + e.getMessage());
}
continue;
} }
// Add it to the map // Add it to the map
newData.put(extractorPath, data); newData.put(extractorPath, data);
@@ -943,24 +931,11 @@ public class AuditComponentImpl implements AuditComponent
} }
catch (Throwable e) catch (Throwable e)
{ {
Log generatorLogger = LogFactory.getLog(generator.getClass()); throw new AlfrescoRuntimeException(
if (generatorLogger.isDebugEnabled()) "Failed to generate audit data: \n" +
{ " Path: " + path + "\n" +
generatorLogger.debug( " Generator: " + generator,
"Failed to generate audit data: \n" + e);
" Path: " + path + "\n" +
" Generator: " + generator,
e);
}
else
{
generatorLogger.warn(
"Failed to generate audit data (turn on DEBUG for full stack): \n" +
" Path: " + path + "\n" +
" Generator: " + generator + "\n" +
" Error: " + e.getMessage());
}
continue;
} }
// Add it to the map // Add it to the map
newData.put(path, data); newData.put(path, data);
@@ -968,4 +943,27 @@ public class AuditComponentImpl implements AuditComponent
// Done // Done
return newData; return newData;
} }
/**
* {@inheritDoc}
*/
public void auditQuery(
AuditQueryCallback callback,
String applicationName,
String user,
Long from,
Long to,
int maxResults)
{
ParameterCheck.mandatory("callback", callback);
// Shortcuts
if (from != null && to != null && from.compareTo(to) > 0)
{
// Time range can't yield results
return;
}
auditDAO.findAuditEntries(callback, applicationName, user, from, to, maxResults);
}
} }

View File

@@ -39,12 +39,16 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry; import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.EqualsHelper; import org.alfresco.util.EqualsHelper;
import org.apache.commons.lang.mutable.MutableInt;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.util.ResourceUtils; import org.springframework.util.ResourceUtils;
@@ -62,6 +66,8 @@ public class AuditComponentTest extends TestCase
private static final String APPLICATION_TEST = "Alfresco Test"; private static final String APPLICATION_TEST = "Alfresco Test";
private static final String APPLICATION_ACTIONS_TEST = "Actions Test"; private static final String APPLICATION_ACTIONS_TEST = "Actions Test";
private static final Log logger = LogFactory.getLog(AuditComponentTest.class);
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private AuditModelRegistry auditModelRegistry; private AuditModelRegistry auditModelRegistry;
@@ -71,6 +77,7 @@ public class AuditComponentTest extends TestCase
private NodeService nodeService; private NodeService nodeService;
private NodeRef nodeRef; private NodeRef nodeRef;
private String user;
@Override @Override
public void setUp() throws Exception public void setUp() throws Exception
@@ -96,7 +103,8 @@ public class AuditComponentTest extends TestCase
nodeRef = AuthenticationUtil.runAs(testRunAs, AuthenticationUtil.getSystemUserName()); nodeRef = AuthenticationUtil.runAs(testRunAs, AuthenticationUtil.getSystemUserName());
// Authenticate // Authenticate
AuthenticationUtil.setFullyAuthenticatedUser("User-" + getName()); user = "User-" + getName();
AuthenticationUtil.setFullyAuthenticatedUser(user);
} }
@Override @Override
@@ -254,9 +262,9 @@ public class AuditComponentTest extends TestCase
} }
/** /**
* Start a session and use it within a single txn * Test auditing of something resembling real-world data
*/ */
public void testSession_Action01() throws Exception public void testAudit_Action01() throws Exception
{ {
Serializable valueA = new Date(); Serializable valueA = new Date();
Serializable valueB = "BBB-value-here"; Serializable valueB = "BBB-value-here";
@@ -283,4 +291,81 @@ public class AuditComponentTest extends TestCase
// Check // Check
checkAuditMaps(result, expected); checkAuditMaps(result, expected);
} }
public void testQuery_Action01() throws Exception
{
final Long beforeTime = new Long(System.currentTimeMillis());
// Make sure that we have something to search for
testAudit_Action01();
final StringBuilder sb = new StringBuilder();
final MutableInt rowCount = new MutableInt();
AuditQueryCallback callback = new AuditQueryCallback()
{
public boolean handleAuditEntry(
Long entryId, String applicationName, String user, long time, Map<String, Serializable> values)
{
assertNotNull(applicationName);
assertNotNull(user);
sb.append("Row: ")
.append(entryId).append(" | ")
.append(applicationName).append(" | ")
.append(user).append(" | ")
.append(new Date(time)).append(" | ")
.append(values).append(" | ")
.append("\n");
;
rowCount.setValue(rowCount.intValue() + 1);
return true;
}
};
sb.delete(0, sb.length());
rowCount.setValue(0);
auditComponent.auditQuery(callback, APPLICATION_ACTIONS_TEST, null, null, null, -1);
assertTrue("Expected some data", rowCount.intValue() > 0);
logger.debug(sb.toString());
int allResults = rowCount.intValue();
// Limit by count
sb.delete(0, sb.length());
rowCount.setValue(0);
auditComponent.auditQuery(callback, APPLICATION_ACTIONS_TEST, null, null, null, 1);
assertEquals("Expected to limit data", 1, rowCount.intValue());
logger.debug(sb.toString());
// Limit by time and query up to and excluding the 'before' time
sb.delete(0, sb.length());
rowCount.setValue(0);
auditComponent.auditQuery(callback, APPLICATION_ACTIONS_TEST, null, null, beforeTime, -1);
logger.debug(sb.toString());
int resultsBefore = rowCount.intValue();
// Limit by time and query from and including the 'before' time
sb.delete(0, sb.length());
rowCount.setValue(0);
auditComponent.auditQuery(callback, APPLICATION_ACTIONS_TEST, null, beforeTime, null, -1);
logger.debug(sb.toString());
int resultsAfter = rowCount.intValue();
assertEquals(
"Time-limited queries did not get all results before and after a time",
allResults, (resultsBefore + resultsAfter));
sb.delete(0, sb.length());
rowCount.setValue(0);
auditComponent.auditQuery(callback, APPLICATION_ACTIONS_TEST, user, null, null, -1);
assertTrue("Expected some data for specific user", rowCount.intValue() > 0);
logger.debug(sb.toString());
sb.delete(0, sb.length());
rowCount.setValue(0);
auditComponent.auditQuery(callback, APPLICATION_ACTIONS_TEST, "Numpty", null, null, -1);
assertTrue("Expected no data for bogus user", rowCount.intValue() == 0);
logger.debug(sb.toString());
}
} }

View File

@@ -1,93 +0,0 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.audit;
import java.io.Serializable;
import java.util.Date;
import java.util.Map;
import org.alfresco.util.ParameterCheck;
/**
* Bean to hold audit entry data. An audit entry represents a single audit call, but the
* data stored may be a large map.
*
* @author Derek Hulley
* @since 3.2
*/
public class AuditEntry
{
private final String user;
private final long time;
private final Long valuesId;
private final Map<String, Serializable> values;
/**
* TODO: Comment
*/
public AuditEntry(String user, long time, Long valuesId, Map<String, Serializable> values)
{
ParameterCheck.mandatoryString("user", user);
ParameterCheck.mandatory("time", time);
this.user = user;
this.time = time;
this.valuesId = valuesId;
this.values = values;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(512);
sb.append("AuditEntry")
.append("[ user=").append(user)
.append(", time=").append(new Date(time))
.append(", valuesId=").append(valuesId)
.append(", values=").append(values)
.append("]");
return sb.toString();
}
public String getUser()
{
return user;
}
public long getTime()
{
return time;
}
public Long getValuesId()
{
return valuesId;
}
public Map<String, Serializable> getValues()
{
return values;
}
}

View File

@@ -35,6 +35,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.ParameterCheck;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
/** /**
@@ -129,9 +130,16 @@ public class AuditServiceImpl implements AuditService
/** /**
* {@inheritDoc} * {@inheritDoc}
* @see AuditComponent#auditQuery(AuditQueryCallback, String, String, Long, Long, int)
* @since 3.2
*/ */
public void auditQuery(AuditQueryCallback callback, String auditPath, String user, long from, long to, int limit) public void auditQuery(
AuditQueryCallback callback,
String applicationName, String user, Long from, Long to, int maxResults)
{ {
throw new UnsupportedOperationException(); ParameterCheck.mandatory("callback", callback);
auditComponent.auditQuery(callback, applicationName, user, from, to, maxResults);
} }
} }

View File

@@ -26,6 +26,8 @@ package org.alfresco.repo.audit.extractor;
import org.alfresco.util.PropertyCheck; import org.alfresco.util.PropertyCheck;
import org.alfresco.util.registry.NamedObjectRegistry; import org.alfresco.util.registry.NamedObjectRegistry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
@@ -37,6 +39,9 @@ import org.springframework.beans.factory.InitializingBean;
*/ */
public abstract class AbstractDataExtractor implements DataExtractor, InitializingBean, BeanNameAware public abstract class AbstractDataExtractor implements DataExtractor, InitializingBean, BeanNameAware
{ {
/** Logger that can be used by subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private String name; private String name;
private NamedObjectRegistry<DataExtractor> registry; private NamedObjectRegistry<DataExtractor> registry;

View File

@@ -32,7 +32,7 @@ import java.io.Serializable;
* components for recording. * components for recording.
* <p/> * <p/>
* The framework will first determine if data passed into the instance is {@link #isSupported(Object) supported} * The framework will first determine if data passed into the instance is {@link #isSupported(Object) supported}
* and will then pass it in for {@link #convert(Object) conversion} to the type that will be * and will then pass it in for {@link #extractData(Serializable) conversion} to the type that will be
* recorded. * recorded.
* *
* @author Derek Hulley * @author Derek Hulley
@@ -51,9 +51,9 @@ public interface DataExtractor
/** /**
* Convert an value passed into the audit components into a value to be recorded. * Convert an value passed into the audit components into a value to be recorded.
* *
* @param value the value to convert * @param value the source data
* @return the (potentially) converted value * @return the extracted data or <tt>null</tt> if the value is not relevant
* @throws Throwable All errors will be handled by the calling framework * @throws Throwable All errors will be handled by the calling framework
*/ */
public Serializable convert(Serializable value) throws Throwable; public Serializable extractData(Serializable value) throws Throwable;
} }

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing
*/
package org.alfresco.repo.audit.extractor;
import java.io.Serializable;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.util.PropertyCheck;
/**
* An extractor that pulls out the {@link ContentModel#PROP_NAME <b>cm:name</b>} property from a node.
*
* @author Derek Hulley
* @since 3.2
*/
public class NodeNameDataExtractor extends AbstractDataExtractor
{
private NodeService nodeService;
/**
* Set the service to get the property from
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
@Override
public void afterPropertiesSet() throws Exception
{
super.afterPropertiesSet();
PropertyCheck.mandatory(this, "nodeService", nodeService);
}
/**
* @return Returns <tt>true</tt> if the data is a {@link NodeRef}
*/
public boolean isSupported(Serializable data)
{
return (data != null && data instanceof NodeRef);
}
/**
* Gets the <b>cm:name</b> property from the node
*/
public Serializable extractData(Serializable in) throws Throwable
{
NodeRef nodeRef = (NodeRef) in;
String nodeName = null;
if (!nodeService.exists(nodeRef))
{
if (logger.isDebugEnabled())
{
logger.debug("Extractor can't pull value from non-existent node: " + nodeRef);
}
}
else
{
nodeName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
}
return nodeName;
}
}

View File

@@ -47,7 +47,7 @@ public class SimpleValueDataExtractor extends AbstractDataExtractor
/** /**
* Just returns the value unchanged * Just returns the value unchanged
*/ */
public Serializable convert(Serializable in) throws Throwable public Serializable extractData(Serializable in) throws Throwable
{ {
return in; return in;
} }

View File

@@ -26,6 +26,8 @@ package org.alfresco.repo.audit.generator;
import org.alfresco.util.PropertyCheck; import org.alfresco.util.PropertyCheck;
import org.alfresco.util.registry.NamedObjectRegistry; import org.alfresco.util.registry.NamedObjectRegistry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
@@ -37,6 +39,9 @@ import org.springframework.beans.factory.InitializingBean;
*/ */
public abstract class AbstractDataGenerator implements DataGenerator, InitializingBean, BeanNameAware public abstract class AbstractDataGenerator implements DataGenerator, InitializingBean, BeanNameAware
{ {
/** Logger that can be used by subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private String name; private String name;
private NamedObjectRegistry<DataGenerator> registry; private NamedObjectRegistry<DataGenerator> registry;

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing
*/
package org.alfresco.repo.audit.generator;
import java.io.Serializable;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.util.PropertyCheck;
/**
* Gives back the full name (person details) of the currently-authenticated user.
*
* @author Derek Hulley
* @since 3.2
*/
public class AuthenticatedPersonDataGenerator extends AbstractDataGenerator
{
private PersonService personService;
private NodeService nodeService;
/**
* Set the service used to discover the user's person node
*/
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
/**
* Set the service to retrieve the user's full name
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
@Override
public void afterPropertiesSet() throws Exception
{
super.afterPropertiesSet();
PropertyCheck.mandatory(this, "personService", personService);
PropertyCheck.mandatory(this, "nodeService", nodeService);
}
/**
* @return Returns the full name of the currently-authenticated user
*/
public Serializable getData() throws Throwable
{
String user = AuthenticationUtil.getFullyAuthenticatedUser();
NodeRef personNodeRef = personService.getPerson(user);
String fullName = null;
if (personNodeRef != null && nodeService.exists(personNodeRef))
{
String firstName = (String)nodeService.getProperty(personNodeRef, ContentModel.PROP_FIRSTNAME);
String lastName = (String)nodeService.getProperty(personNodeRef, ContentModel.PROP_LASTNAME);
fullName = ((firstName != null && firstName.length() > 0) ? firstName : "");
if (lastName != null && lastName.length() > 0)
{
fullName += (fullName.length() > 0 ? " " : "");
fullName += lastName;
}
}
// Done
if (logger.isDebugEnabled())
{
logger.debug("Generated name '" + fullName + "' for user '" + user + "'.");
}
return fullName;
}
}

View File

@@ -41,7 +41,7 @@ public interface DataGenerator
/** /**
* Get the data generated by the instance. * Get the data generated by the instance.
* *
* @return Returns the generated data * @return Returns the generated data or <tt>null</tt> if no data could be generated
* @throws Throwable All exceptions are handled by the framework * @throws Throwable All exceptions are handled by the framework
*/ */
public Serializable getData() throws Throwable; public Serializable getData() throws Throwable;

View File

@@ -52,6 +52,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.TransactionalDao; import org.alfresco.repo.transaction.TransactionalDao;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.audit.AuditInfo; import org.alfresco.service.cmr.audit.AuditInfo;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.ContentWriter;
@@ -140,39 +141,6 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO,
this.localSessionFactory = localSessionFactory; this.localSessionFactory = localSessionFactory;
} }
/**
* Fallout implementation from new audit DAO
*
* @throws UnsupportedOperationException always
* @since 3.2
*/
public Pair<Long, ContentData> getOrCreateAuditModel(URL url)
{
throw new UnsupportedOperationException();
}
/**
* Fallout implementation from new audit DAO
*
* @throws UnsupportedOperationException always
* @since 3.2
*/
public Long getOrCreateAuditApplication(Long modelId, String applicationName)
{
throw new UnsupportedOperationException();
}
/**
* Fallout implementation from new audit DAO
*
* @throws UnsupportedOperationException always
* @since 3.2
*/
public Long createAuditEntry(Long applicationId, long time, String username, Map<String, Serializable> values)
{
throw new UnsupportedOperationException();
}
public void audit(AuditState auditInfo) public void audit(AuditState auditInfo)
{ {
if (auditInfo.getUserIdentifier() == null) if (auditInfo.getUserIdentifier() == null)
@@ -680,5 +648,54 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO,
query.setParameter(QUERY_AUDIT_DATE_PARAM, date); query.setParameter(QUERY_AUDIT_DATE_PARAM, date);
return (AuditDate) query.uniqueResult(); return (AuditDate) query.uniqueResult();
} }
/*
* V3.2 from here on. Put all fixes to the older audit code before this point, please.
*/
} /**
* Fallout implementation from new audit DAO
*
* @throws UnsupportedOperationException always
* @since 3.2
*/
public Pair<Long, ContentData> getOrCreateAuditModel(URL url)
{
throw new UnsupportedOperationException();
}
/**
* Fallout implementation from new audit DAO
*
* @throws UnsupportedOperationException always
* @since 3.2
*/
public Long getOrCreateAuditApplication(Long modelId, String applicationName)
{
throw new UnsupportedOperationException();
}
/**
* Fallout implementation from new audit DAO
*
* @throws UnsupportedOperationException always
* @since 3.2
*/
public Long createAuditEntry(Long applicationId, long time, String username, Map<String, Serializable> values)
{
throw new UnsupportedOperationException();
}
/**
* Fallout implementation from new audit DAO
*
* @throws UnsupportedOperationException always
* @since 3.2
*/
public void findAuditEntries(
AuditQueryCallback callback,
String applicationName, String user, Long from, Long to, int maxResults)
{
throw new UnsupportedOperationException();
}
}

View File

@@ -40,6 +40,7 @@ import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.domain.contentdata.ContentDataDAO; import org.alfresco.repo.domain.contentdata.ContentDataDAO;
import org.alfresco.repo.domain.propval.PropertyValueDAO; import org.alfresco.repo.domain.propval.PropertyValueDAO;
import org.alfresco.service.cmr.audit.AuditInfo; import org.alfresco.service.cmr.audit.AuditInfo;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.ContentWriter;
@@ -61,7 +62,7 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO
private HibernateAuditDAO oldDAO; private HibernateAuditDAO oldDAO;
private ContentService contentService; private ContentService contentService;
private ContentDataDAO contentDataDAO; private ContentDataDAO contentDataDAO;
private PropertyValueDAO propertyValueDAO; protected PropertyValueDAO propertyValueDAO;
public void setOldDAO(HibernateAuditDAO oldDAO) public void setOldDAO(HibernateAuditDAO oldDAO)
{ {
@@ -271,4 +272,59 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO
} }
protected abstract AuditEntryEntity createAuditEntry(Long applicationId, long time, Long usernameId, Long valuesId); protected abstract AuditEntryEntity createAuditEntry(Long applicationId, long time, Long usernameId, Long valuesId);
/*
* Searches
*/
/**
* Class that passes results from a result entity into the client callback
*/
protected class AuditQueryRowHandler
{
private final AuditQueryCallback callback;
private boolean more;
private AuditQueryRowHandler(AuditQueryCallback callback)
{
this.callback = callback;
this.more = true;
}
@SuppressWarnings("unchecked")
public void processResult(AuditQueryResult row)
{
if (!more)
{
// No more results required
return;
}
// Get the value map
// TODO: Should be done with a nested resultmapping
Long auditValuesId = row.getAuditValuesId();
Pair<Long, Serializable> auditValuesPair = propertyValueDAO.getPropertyValueById(auditValuesId);
if (auditValuesPair == null)
{
// Ignore
return;
}
Map<String, Serializable> auditValues = (Map<String, Serializable>) auditValuesPair.getSecond();
more = callback.handleAuditEntry(
row.getAuditEntryId(),
row.getAuditAppName(),
row.getAuditUser(),
row.getAuditTime(),
auditValues);
}
}
public void findAuditEntries(
AuditQueryCallback callback,
String applicationName, String user, Long from, Long to, int maxResults)
{
AuditQueryRowHandler rowHandler = new AuditQueryRowHandler(callback);
findAuditEntries(rowHandler, applicationName, user, from, to, maxResults);
}
protected abstract void findAuditEntries(
AuditQueryRowHandler rowHandler,
String applicationName, String user, Long from, Long to, int maxResults);
} }

View File

@@ -31,6 +31,7 @@ import java.util.Map;
import org.alfresco.repo.audit.AuditState; import org.alfresco.repo.audit.AuditState;
import org.alfresco.service.cmr.audit.AuditInfo; import org.alfresco.service.cmr.audit.AuditInfo;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
@@ -93,4 +94,8 @@ public interface AuditDAO
* @return Returns the unique entry ID * @return Returns the unique entry ID
*/ */
Long createAuditEntry(Long applicationId, long time, String username, Map<String, Serializable> values); Long createAuditEntry(Long applicationId, long time, String username, Map<String, Serializable> values);
void findAuditEntries(
AuditQueryCallback callback,
String applicationName, String user, Long from, Long to, int maxResults);
} }

View File

@@ -0,0 +1,122 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.domain.audit;
import java.util.Date;
import org.alfresco.util.Pair;
/**
* Query parameters for <b>alf_audit_entry</b> table.
*
* @author Derek Hulley
* @since 3.2
*/
public class AuditQueryParameters
{
private Long auditEntryId;
private Pair<String, Long> auditAppNameCrcPair;
private Pair<String, Long> auditUserCrcPair;
private Long auditFromTime;
private Long auditToTime;
public AuditQueryParameters()
{
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(512);
sb.append("AuditEntryParameters")
.append("[ auditEntryId=").append(auditEntryId)
.append(", auditAppNameCrcPair=").append(auditAppNameCrcPair)
.append(", auditUserCrcPair=").append(auditUserCrcPair)
.append(", auditFromTime").append(new Date(auditFromTime))
.append(", auditToTime").append(new Date(auditToTime))
.append("]");
return sb.toString();
}
public Long getAuditEntryId()
{
return auditEntryId;
}
public void setAuditEntryId(Long entryId)
{
this.auditEntryId = entryId;
}
public String getAuditAppNameShort()
{
return auditAppNameCrcPair == null ? null : auditAppNameCrcPair.getFirst();
}
public Long getAuditAppNameCrc()
{
return auditAppNameCrcPair == null ? null : auditAppNameCrcPair.getSecond();
}
public void setAuditAppNameCrcPair(Pair<String, Long> appNameCrcPair)
{
this.auditAppNameCrcPair = appNameCrcPair;
}
public String getAuditUserShort()
{
return auditUserCrcPair == null ? null : auditUserCrcPair.getFirst();
}
public Long getAuditUserCrc()
{
return auditUserCrcPair == null ? null : auditUserCrcPair.getSecond();
}
public void setAuditUserCrcPair(Pair<String, Long> userCrcPair)
{
this.auditUserCrcPair = userCrcPair;
}
public Long getAuditFromTime()
{
return auditFromTime;
}
public void setAuditFromTime(Long from)
{
this.auditFromTime = from;
}
public Long getAuditToTime()
{
return auditToTime;
}
public void setAuditToTime(Long to)
{
this.auditToTime = to;
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.domain.audit;
import java.util.Date;
/**
* Results bean for <b>alf_audit_entry</b> table.
*
* @author Derek Hulley
* @since 3.2
*/
public class AuditQueryResult
{
private Long auditEntryId;
private String auditAppName;
private String auditUser;
private long auditTime;
private Long auditValuesId;
public AuditQueryResult()
{
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(512);
sb.append("AuditEntryResult")
.append("[ auditEntryId=").append(auditEntryId)
.append(", auditAppName=").append(auditAppName)
.append(", auditUser=").append(auditUser)
.append(", auditTime").append(new Date(auditTime))
.append(", auditValuesId=").append(auditValuesId)
.append("]");
return sb.toString();
}
public Long getAuditEntryId()
{
return auditEntryId;
}
public void setAuditEntryId(Long entryId)
{
this.auditEntryId = entryId;
}
public String getAuditAppName()
{
return auditAppName;
}
public void setAuditAppName(String appName)
{
this.auditAppName = appName;
}
public String getAuditUser()
{
return auditUser;
}
public void setAuditUser(String user)
{
this.auditUser = user;
}
public long getAuditTime()
{
return auditTime;
}
public void setAuditTime(long time)
{
this.auditTime = time;
}
public Long getAuditValuesId()
{
return auditValuesId;
}
public void setAuditValuesId(Long auditValuesId)
{
this.auditValuesId = auditValuesId;
}
}

View File

@@ -33,9 +33,13 @@ import org.alfresco.repo.domain.audit.AbstractAuditDAOImpl;
import org.alfresco.repo.domain.audit.AuditApplicationEntity; import org.alfresco.repo.domain.audit.AuditApplicationEntity;
import org.alfresco.repo.domain.audit.AuditEntryEntity; import org.alfresco.repo.domain.audit.AuditEntryEntity;
import org.alfresco.repo.domain.audit.AuditModelEntity; import org.alfresco.repo.domain.audit.AuditModelEntity;
import org.alfresco.repo.domain.audit.AuditQueryParameters;
import org.alfresco.repo.domain.audit.AuditQueryResult;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
import org.springframework.orm.ibatis.SqlMapClientTemplate; import org.springframework.orm.ibatis.SqlMapClientTemplate;
import com.ibatis.sqlmap.client.event.RowHandler;
/** /**
* iBatis-specific implementation of the DAO for <b>alf_audit_XXX</b> tables. * iBatis-specific implementation of the DAO for <b>alf_audit_XXX</b> tables.
* *
@@ -52,6 +56,8 @@ public class AuditDAOImpl extends AbstractAuditDAOImpl
private static final String INSERT_ENTRY = "insert.AuditEntry"; private static final String INSERT_ENTRY = "insert.AuditEntry";
private static final String SELECT_ENTRIES_SIMPLE = "select.AuditEntriesSimple";
private SqlMapClientTemplate template; private SqlMapClientTemplate template;
public void setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate) public void setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate)
@@ -142,4 +148,50 @@ public class AuditDAOImpl extends AbstractAuditDAOImpl
entity.setId(id); entity.setId(id);
return entity; return entity;
} }
@SuppressWarnings("unchecked")
@Override
protected void findAuditEntries(
final AuditQueryRowHandler rowHandler,
String appName,
String user,
Long from,
Long to,
int maxResults)
{
AuditQueryParameters params = new AuditQueryParameters();
if (appName != null)
{
Pair<String, Long> appNameCrcPair = propertyValueDAO.getPropertyStringCaseSensitiveSearchParameters(appName);
params.setAuditAppNameCrcPair(appNameCrcPair);
}
if (user != null)
{
Pair<String, Long> userCrcPair = propertyValueDAO.getPropertyStringCaseSensitiveSearchParameters(user);
params.setAuditUserCrcPair(userCrcPair);
}
params.setAuditFromTime(from);
params.setAuditToTime(to);
if (maxResults <= 0)
{
RowHandler rowHandlerInternal = new RowHandler()
{
public void handleRow(Object valueObject)
{
AuditQueryResult row = (AuditQueryResult) valueObject;
rowHandler.processResult(row);
}
};
template.queryWithRowHandler(SELECT_ENTRIES_SIMPLE, params, rowHandlerInternal);
}
else
{
List<AuditQueryResult> rows = template.queryForList(SELECT_ENTRIES_SIMPLE, params, 0, maxResults);
for (AuditQueryResult row : rows)
{
rowHandler.processResult(row);
}
}
}
} }

View File

@@ -408,6 +408,11 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
// 'alf_prop_string_value' accessors // 'alf_prop_string_value' accessors
//================================ //================================
public Pair<String, Long> getPropertyStringCaseSensitiveSearchParameters(String value)
{
return CrcHelper.getStringCrcPair(value, 16, false, true);
}
public Pair<Long, String> getPropertyStringValueById(Long id) public Pair<Long, String> getPropertyStringValueById(Long id)
{ {
if (id == null) if (id == null)
@@ -449,7 +454,7 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
{ {
public Pair<String, Long> getValueKey(String value) public Pair<String, Long> getValueKey(String value)
{ {
return CrcHelper.getStringCrcPair(value, 128, true, true); return getPropertyStringCaseSensitiveSearchParameters(value);
} }
public Pair<Long, String> createValue(String value) public Pair<Long, String> createValue(String value)

View File

@@ -31,6 +31,8 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.domain.propval.PropertyValueEntity.PersistedType; import org.alfresco.repo.domain.propval.PropertyValueEntity.PersistedType;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Period; import org.alfresco.service.cmr.repository.Period;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
@@ -60,6 +62,8 @@ public class DefaultPropertyTypeConverter implements PropertyTypeConverter
mapClass.put(NodeRef.class, PersistedType.STRING); mapClass.put(NodeRef.class, PersistedType.STRING);
mapClass.put(Period.class, PersistedType.STRING); mapClass.put(Period.class, PersistedType.STRING);
mapClass.put(Locale.class, PersistedType.STRING); mapClass.put(Locale.class, PersistedType.STRING);
mapClass.put(AssociationRef.class, PersistedType.STRING);
mapClass.put(ChildAssociationRef.class, PersistedType.STRING);
defaultPersistedTypesByClass = Collections.unmodifiableMap(mapClass); defaultPersistedTypesByClass = Collections.unmodifiableMap(mapClass);
} }

View File

@@ -27,6 +27,7 @@ package org.alfresco.repo.domain.propval;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
import org.alfresco.repo.domain.CrcHelper;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
/** /**
@@ -84,6 +85,11 @@ public interface PropertyValueDAO
//================================ //================================
// 'alf_prop_string_value' accessors // 'alf_prop_string_value' accessors
//================================ //================================
/**
* Utility method to get query parameters for case-sensitive string searching
* @see CrcHelper
*/
Pair<String, Long> getPropertyStringCaseSensitiveSearchParameters(String value);
/** /**
* <b>alf_prop_string_value</b> accessor * <b>alf_prop_string_value</b> accessor
* *

View File

@@ -114,50 +114,34 @@ public interface AuditService
public static interface AuditQueryCallback public static interface AuditQueryCallback
{ {
/** /**
* Check if summary or full data fetching is required. Depending on the return value, * Handle a row of audit entry data.
* the underlying query may be completely different; it is not possible to change the
* return value and expect the callback to be used differently during row handling.
*
* @return Return <tt>true</tt> if summary data is required only i.e
* the full map of audit values for the entries will not be
* retrieved.
*/
boolean isSummaryOnly();
/**
* Handle a summary row of audit entry data. The ID of the full values map is provided.
*
* @param applicationName the name of the application
* @param user the user that logged the entry
* @param time the time of the entry
* @param valuesId the ID of the values map as created
* @return Return <tt>true</tt> to continue processing rows or <tt>false</tt> to stop
*/
boolean handleAuditEntrySummary(String applicationName, String user, long time, Long valuesId);
/**
* Handle a full row of audit entry data.
* *
* @param entryId the unique audit entry ID
* @param applicationName the name of the application * @param applicationName the name of the application
* @param user the user that logged the entry * @param user the user that logged the entry
* @param time the time of the entry * @param time the time of the entry
* @param values the values map as created * @param values the values map as created
* @return Return <tt>true</tt> to continue processing rows or <tt>false</tt> to stop * @return Return <tt>true</tt> to continue processing rows or <tt>false</tt> to stop
*/ */
boolean handleAuditEntryFull(String applicationName, String user, long time, Map<String, Serializable> values); boolean handleAuditEntry(
Long entryId,
String applicationName,
String user,
long time,
Map<String, Serializable> values);
} }
/** /**
* Get the audit entries that match the given criteria. * Get the audit entries that match the given criteria.
* *
* @param callback the callback that will handle results * @param callback the callback that will handle results
* @param auditPath if not <tt>null</tt>, at least one value in the entry must start with this path * @param applicationName if not <tt>null</tt>, find entries logged against this application
* @param user if not <tt>null</tt>, the entry must be logged against this user * @param user if not <tt>null</tt>, find entries logged against this user
* @param from the start search time (use 0L) to cover all times * @param from the start search time (<tt>null</tt> to start at the beginning)
* @param to the end search time (use Long.MAX_VALUE) to cover all times * @param to the end search time (<tt>null</tt> for no limit)
* @param limit the maximum number of results to retrieve * @param maxResults the maximum number of results to retrieve (zero or negative to ignore)
*/ */
void auditQuery( void auditQuery(
AuditQueryCallback callback, AuditQueryCallback callback,
String auditPath, String user, long from, long to, int limit); String applicationName, String user, Long from, Long to, int maxResults);
} }

View File

@@ -31,13 +31,13 @@
<AuditPath key="4.2"> <AuditPath key="4.2">
<RecordValue key="value.1" dataExtractor="simpleValue"/> <RecordValue key="value.1" dataExtractor="simpleValue"/>
<RecordValue key="value.2" dataExtractor="simpleValue"/> <RecordValue key="value.2" dataExtractor="simpleValue"/>
<GenerateValue key="value.3" dataGenerator="systemTime" scope="AUDIT"/> <GenerateValue key="value.3" dataGenerator="systemTime"/>
</AuditPath> </AuditPath>
</AuditPath> </AuditPath>
<AuditPath key="3.2"> <AuditPath key="3.2">
<AuditPath key="4.1"> <AuditPath key="4.1">
<RecordValue key="value.1" dataExtractor="simpleValue"/> <RecordValue key="value.1" dataExtractor="simpleValue"/>
<GenerateValue key="time" dataGenerator="systemTime" scope="AUDIT"/> <GenerateValue key="time" dataGenerator="systemTime"/>
</AuditPath> </AuditPath>
<AuditPath key="4.2"> <AuditPath key="4.2">
<RecordValue key="value.1" dataExtractor="simpleValue"/> <RecordValue key="value.1" dataExtractor="simpleValue"/>