Working with Auditing-related Objects
The following code examples demonstrate audit-related operations. For an overview of auditing function, see Auditing.
Adding Audit Definitions to a Class
Auditing is configured
on a per-class basis, in which a class is represented by a SubscribableClassDefinition object. An event
to be audited for a class is represented by an EventClassDefinition object. The audit configuration
information is held in an AuditDefinition object, and contains an EventClassDefinition object.
There is one AuditDefinition object for each event
to be audited in a class. A SubscribableClassDefinition object
can contain one or more AuditDefinition objects.
The following steps describe how to configure a class for auditing:
- Get a
SubscribableClassDefinitionobject or subobject, such as theDocumentclass. - Get an
EventClassDefinitionobject, such as theCreationEvent. - Create an
AuditDefinitionobject. - Create an
AuditDefinitionListobject. - Set the
AuditDefinitionobject properties, such as EventClass, which takes theEventClassDefinitionobject. - Add the
AuditDefinitionobject to theAuditDefinitionListobject. - Repeat steps 2 - 6 for each event to be audited for a class.
- Set the
SubscribableClassDefinitionobject's AuditDefinitions property to theAuditDefinitionListobject.
In the following Java™ and
C# examples, two events are configured for auditing on the FloodClaim class,
a subclass of Document. For each event, an AuditDefinition object
is created. For the system UpdateEvent, the FilterExpression property of the AuditDefinition object
is set, which is applied to an instance of the FloodClaim class.
The expression tests the value of a FloodClaim custom
property. When a FloodClaim instance is updated,
the event is audited only if the expression evaluates to true.
The custom event was previously created and is specified by a GUID. For more information about custom events, see Creating and Raising a Custom Event.
Java Example
// Make sure auditing is enabled for the object store.
if (store.get_AuditLevel()==AuditLevel.NONE)
{
store.set_AuditLevel(AuditLevel.ENABLED);
store.save(RefreshMode.REFRESH);
}
// Fetch class definition for FloodClaim.
DocumentClassDefinition dcd = Factory.DocumentClassDefinition.fetchInstance(
store, new Id("{00A3E7B8-3473-4136-B44C-5A05874E718C}"), null);
// Get AuditDefinitionList from DocumentClassDefinition object.
AuditDefinitionList adl = dcd.get_AuditDefinitions();
// Create AuditDefinition object to be set with a system event.
AuditDefinition ad = Factory.AuditDefinition.createInstance(store);
// Get EventClassDefinition for system event.
EventClassDefinition ecd = Factory.EventClassDefinition.getInstance(
store, GuidConstants.Class_UpdateEvent);
// Set properties on AuditDefinition object for system event.
ad.set_EventClass(ecd);
ad.set_AuditSuccess(new Boolean("TRUE"));
ad.set_AuditFailure(new Boolean("TRUE"));
ad.set_IncludeSubclassesRequested(new Boolean("FALSE"));
ad.set_ObjectStateRecordingLevel(ObjectStateRecordingLevel.NONE);
ad.set_FilterExpression("auditLocationCode IN ('NOL', 'SEA', 'NVL')");
ad.set_DisplayName("Flood claim processing");
// Add first audit definition to list for system event.
adl.add(ad);
// Create AuditDefinition object to be set with a custom event.
AuditDefinition ad2 = Factory.AuditDefinition.createInstance(store);
// Get EventClassDefinition for custom event.
EventClassDefinition ecd2 = Factory.EventClassDefinition.getInstance (
store, new Id("{9906D34A-DBE2-415E-B807-6B4CCA65B893}"));
// Set properties on AuditDefinition object for custom event.
ad2.set_EventClass(ecd2);
ad2.set_AuditSuccess(new Boolean("TRUE"));
ad2.set_AuditFailure(new Boolean("TRUE"));
ad2.set_IncludeSubclassesRequested(new Boolean("TRUE"));
ad2.set_ObjectStateRecordingLevel(ObjectStateRecordingLevel.ORIGINAL_AND_MODIFIED_OBJECTS);
ad2.set_DisplayName("Fraudulent claim review");
// Add second audit definition to list for custom event.
adl.add(ad2);
// Set AuditDefinitions property on DocumentClassDefinition object.
dcd.set_AuditDefinitions(adl);
// Save DocumentClassDefinition object.
dcd.save(RefreshMode.REFRESH);
C# Example
// Make sure auditing is enabled for the object store.
if (store.AuditLevel == AuditLevel.NONE)
{
store.AuditLevel = AuditLevel.ENABLED;
store.Save(RefreshMode.REFRESH);
}
// Fetch class definition for FloodClaim.
IDocumentClassDefinition dcd = Factory.DocumentClassDefinition.FetchInstance(
store, new Id("{00A3E7B8-3473-4136-B44C-5A05874E718C}"), null);
// Get AuditDefinitionList from DocumentClassDefinition object.
IAuditDefinitionList adl = dcd.AuditDefinitions;
// Create audit definition to be set with a system event.
IAuditDefinition ad = Factory.AuditDefinition.CreateInstance(store);
// Get EventClassDefinition for system event.
IEventClassDefinition ecd = Factory.EventClassDefinition.GetInstance (
store, GuidConstants.Class_UpdateEvent);
// Set properties on audit definition object for system event.
ad.EventClass = ecd;
ad.AuditSuccess = true;
ad.AuditFailure = true;
ad.IncludeSubclassesRequested = false;
ad.ObjectStateRecordingLevel=ObjectStateRecordingLevel.NONE;
ad.FilterExpression = "auditLocationCode IN ('NOL', 'SEA', 'NVL')";
ad.DisplayName = "Flood claim processing";
// Add first audit definition to list for system event.
adl.Add(ad);
// Create AuditDefinition object to be set with a custom event.
IAuditDefinition ad2 = Factory.AuditDefinition.CreateInstance(store);
// Get EventClassDefinition for custom event.
IEventClassDefinition ecd2 = Factory.EventClassDefinition.GetInstance (
store, new Id("{9906D34A-DBE2-415E-B807-6B4CCA65B893}"));
// Set properties on AuditDefinition object for custom event.
ad2.EventClass = ecd2;
ad2.AuditSuccess = true;
ad2.AuditFailure = true;
ad2.IncludeSubclassesRequested = true;
ad2.ObjectStateRecordingLevel=ObjectStateRecordingLevel.ORIGINAL_AND_MODIFIED_OBJECTS;
ad2.DisplayName = "Fraudulent claim review";
// Add second audit definition to list for custom event.
adl.Add(ad2);
// Set AuditDefinitions property on DocumentClassDefinition object.
dcd.AuditDefinitions = adl;
// Save DocumentClassDefinition object.
dcd.Save(RefreshMode.REFRESH);
Configuring Property Auditing
You can audit individual source object properties, allowing audit processing clients to retrieve granular information, as opposed to retrieving source objects, from audited events. To configure the auditing of properties for a particular event, you need to configure an event class and a source class, as described in the next section.
Configuring an Event Class
The setup for auditing properties includes configuration
of an event class of type ObjectChangeEvent. Using
a PropertyTemplate object, you create a custom PropertyDefinition object
and add it to the event class. For each PropertyDefinition object
added, the Content Engine extends
the Event table with a corresponding property column, where an audited
source object value is stored.
The following Java and C# examples show how to configure an
event class. Property definitions for two custom properties and one
system property are added to the UpdateEvent class. Note that
unlike custom properties, there are no ready-made property templates
for system properties. Therefore, to add a PropertyDefinition object
for a system property to an event class, you must create a PropertyTemplate object.
Java Example
// Get object change event class.
EventClassDefinition ecd = Factory.EventClassDefinition.fetchInstance (
store, GuidConstants.Class_ObjectChangeEvent, null);
// Get PropertyDefinitions property from the property cache.
PropertyDefinitionList pdList = ecd.get_PropertyDefinitions();
// Get property template for custom property auditLocationCode.
PropertyTemplateString ptString = Factory.PropertyTemplateString.getInstance(
store, new Id("{6427183B-6AD9-4C38-8825-ECE7F4D36766}") );
// Create property definition from property template.
PropertyDefinitionString pdString = (PropertyDefinitionString)ptString.createClassProperty();
// Add auditLocationCode to class definition for update event.
pdList.add(pdString);
// Get property template for custom property auditStatus.
ptString = Factory.PropertyTemplateString.getInstance (
store, new Id("{8815C41B-4316-482F-82E5-0D2F6540EE1A}") );
// Create property definition from property template.
pdString = (PropertyDefinitionString)ptString.createClassProperty();
// Add auditStatus to class definition for update event.
pdList.add(pdString);
// Create property template for system property to be audited, LastModifier.
ptString = Factory.PropertyTemplateString.createInstance(store);
ptString.set_Cardinality (Cardinality.SINGLE);
LocalizedString locStr = Factory.LocalizedString.createInstance();
locStr.set_LocalizedText ("auditLastModifier");
locStr.set_LocaleName (store.get_LocaleName());
ptString.set_DisplayNames (Factory.LocalizedString.createList());
ptString.get_DisplayNames().add(locStr);
// Save new property template.
ptString.save(RefreshMode.REFRESH);
// Create property definition from property template.
pdString = (PropertyDefinitionString)ptString.createClassProperty();
// Add auditLastModifier to class definition for update event.
pdList.add(pdString);
// Save update event class.
ecd.save(RefreshMode.REFRESH);
C# Example
// Get update event class.
IEventClassDefinition ecd = Factory.EventClassDefinition.FetchInstance (
store, GuidConstants.Class_UpdateEvent, null);
// Get PropertyDefinitions property from the property cache.
IPropertyDefinitionList pdList = ecd.PropertyDefinitions;
// Get property template for custom property auditLocationCode.
IPropertyTemplateString ptString = Factory.PropertyTemplateString.GetInstance(
store, new Id("{6427183B-6AD9-4C38-8825-ECE7F4D36766}"));
// Create property definition from property template.
IPropertyDefinitionString pdString = (IPropertyDefinitionString)ptString.CreateClassProperty();
// Add auditLocationCode to class definition for update event.
pdList.Add(pdString);
// Get property template for custom property auditStatus.
ptString = Factory.PropertyTemplateString.GetInstance(
store, new Id("{8815C41B-4316-482F-82E5-0D2F6540EE1A}"));
// Create property definition from property template.
pdString = (IPropertyDefinitionString)ptString.CreateClassProperty();
// Add auditStatus to class definition for update event.
pdList.Add(pdString);
// Create property template for system property to be audited, LastModifier.
ptString = Factory.PropertyTemplateString.CreateInstance(store);
ptString.Cardinality=Cardinality.SINGLE;
ILocalizedString locStr = Factory.LocalizedString.CreateInstance();
locStr.LocalizedText="auditLastModifier";
locStr.LocaleName = store.LocaleName;
ptString.DisplayNames = Factory.LocalizedString.CreateList();
ptString.DisplayNames.Add(locStr);
// Save new property template.
ptString.Save(RefreshMode.REFRESH);
// Create property definition from property template.
pdString = (IPropertyDefinitionString)ptString.CreateClassProperty();
// Add auditLastModifier to class definition for update event.
pdList.Add(pdString);
// Save update event class.
ecd.Save(RefreshMode.REFRESH);
Configuring a Source Class
In addition to configuring an event class to set up property
auditing, you must also configure the source class that has the properties
you want to audit. The properties to be audited must correspond to
the PropertyDefinition objects added to the event
class.
The Java and
C# examples for configuring a source class assume that the class has
an audit definition that is configured for UpdateEvent objects.
Property definitions for two custom properties and one system property
are configured for auditing on the class. That is, for each PropertyDefinition object,
its AuditAs property is set to the PropertyTemplate object
that was used to create the corresponding PropertyDefinition object
on the event class.
Java Example
// Get Document subclass, FloodClaim.
DocumentClassDefinition dcd = Factory.DocumentClassDefinition.fetchInstance (
store, new Id("{00A3E7B8-3473-4136-B44C-5A05874E718C}"),null);
// Get PropertyDefinitions property from the property cache.
PropertyDefinitionList pdList = dcd.get_PropertyDefinitions();
// Get property template for custom property auditLocationCode.
PropertyTemplateString auditLocationCode = Factory.PropertyTemplateString.getInstance (
store, new Id("{6427183B-6AD9-4C38-8825-ECE7F4D36766}") );
// Get property template for custom property auditStatus.
PropertyTemplateString auditStatus = Factory.PropertyTemplateString.getInstance (
store, new Id("{8815C41B-4316-482F-82E5-0D2F6540EE1A}") );
//Get property template for system property LastModifier.
PropertyTemplateString auditLastModifier = Factory.PropertyTemplateString.getInstance (
store, new Id("{F9BA4605-3B96-4CD0-B7B2-8DF64DBE175B}") );
// Iterate list to find property definitions targeted for auditing.
// Set AuditAs properties to property templates.
Iterator iter = pdList.iterator();
PropertyDefinition pd = null;
while (iter.hasNext())
{
pd = (PropertyDefinition) iter.next();
// Get SymbolicName property from the property cache.
String pdSymbolicName;
pdSymbolicName = pd.get_SymbolicName();
if (pdSymbolicName.equalsIgnoreCase("auditLocationCode"))
{
System.out.println(pdSymbolicName + " found");
pd.set_AuditAs(auditLocationCode);
}
if (pdSymbolicName.equalsIgnoreCase("auditStatus"))
{
System.out.println(pdSymbolicName + " found");
pd.set_AuditAs(auditStatus);
}
if (pdSymbolicName.equalsIgnoreCase("LastModifier"))
{
System.out.println("pdSymbolicName + " found");
pd.set_AuditAs(auditLastModifier);
}
}
// Save FloodClaim class.
dcd.save(RefreshMode.REFRESH);
C# Example
// Get Document subclass, FloodClaim.
IDocumentClassDefinition dcd = Factory.DocumentClassDefinition.FetchInstance (
store, new Id("{00A3E7B8-3473-4136-B44C-5A05874E718C}"),null);
// Get PropertyDefinitions property from the property cache.
IPropertyDefinitionList pdList = dcd.PropertyDefinitions;
// Get property template for custom property auditLocationCode.
IPropertyTemplateString auditLocationCode = Factory.PropertyTemplateString.GetInstance(
store, new Id("{6427183B-6AD9-4C38-8825-ECE7F4D36766}") );
// Get property template for custom property auditStatus.
IPropertyTemplateString auditStatus = Factory.PropertyTemplateString.GetInstance(
store, new Id("{8815C41B-4316-482F-82E5-0D2F6540EE1A}") );
//Get property template for system property LastModifier.
IPropertyTemplateString auditLastModifier = Factory.PropertyTemplateString.GetInstance(
store, new Id("{F9BA4605-3B96-4CD0-B7B2-8DF64DBE175B}") );
// Iterate list to find property definitions targeted for auditing.
// Set AuditAs properties to property templates.
foreach (IPropertyDefinition pd in pdList)
{
String pdSymbolicName;
pdSymbolicName = pd.SymbolicName;
if (pdSymbolicName.Equals("auditLocationCode"))
{
System.Console.WriteLine("Property definition selected: " + pdSymbolicName);
pd.AuditAs=auditLocationCode;
}
if (pdSymbolicName.Equals("auditStatus"))
{
System.Console.WriteLine("Property definition selected: " + pdSymbolicName);
pd.AuditAs=auditStatus;
}
if (pdSymbolicName.Equals("LastModifier"))
{
System.Console.WriteLine("Property definition selected: " + pdSymbolicName);
pd.AuditAs=auditLastModifier;
}
}
// Save FloodClaim class.
dcd.Save(RefreshMode.REFRESH);
Configuring Audit Disposition
To configure the audit disposition background thread, you need to set an auditing configuration on one or more objects of the server hierarchy, and set one or more audit disposition policies on an object store.
Setting Auditing Configuration
The following Java and
C# examples show how to set a CmAuditingConfiguration to configure the audit
disposition subsystem. The code sets the properties on the default
instance of CmAuditingConfiguration, which was generated
during domain creation. Note that the default instance is initially
disabled.
Java Example
CmAuditingConfiguration auditConfig = null;
// Create PropertyFilter for domain.
PropertyFilter pf = new PropertyFilter();
pf.addIncludeProperty(new FilterElement(null, null, null, "SubsystemConfigurations", null));
// Get default domain.
Domain domain = Factory.Domain.fetchInstance(ceConn, null, pf);
// Get domain's subsystem configurations,
// and iterate to get the default auditing configuration.
SubsystemConfigurationList sclist = domain.get_SubsystemConfigurations();
Iterator it = sclist.iterator();
while (it.hasNext())
{
SubsystemConfiguration subsystemConfig = (SubsystemConfiguration) it.next();
if (subsystemConfig.getClassName().equals("CmAuditingConfiguration") )
{
auditConfig = (CmAuditingConfiguration) subsystemConfig;
break;
}
}
// Create CmTimeslotList object.
CmTimeslotList tsList = Factory.CmTimeslot.createList();
// Create 1 of 2 CmTimeslot objects and configure it
// so that audit disposition runs every Wednesday for
// 4 hours, starting at 2 a.m.
CmTimeslot ts1 = Factory.CmTimeslot.createInstance();
ts1.set_Weekday(Weekday.WEDNESDAY);
ts1.set_Duration(Integer.valueOf(240)); // 4 hours
ts1.set_StartMinutesPastMidnight(Integer.valueOf(120));
// Create 2 of 2 CmTimeslot objects and configure it
// so that audit disposition runs every weekend for
// 48 hours, starting at midnight Saturday.
CmTimeslot ts2 = Factory.CmTimeslot.createInstance();
ts2.set_Weekday(Weekday.SATURDAY);
ts2.set_Duration(Integer.valueOf(2880));// 48 hours
ts2.set_StartMinutesPastMidnight(Integer.valueOf(0));
// Add CmTimeslot objects to the list.
tsList.add(ts1);
tsList.add(ts2);
// Set timeslots on CmAuditingConfiguration and enable the object.
auditConfig.set_AuditDispositionTimeslots(tsList);
auditConfig.set_AuditDispositionEnabled(Boolean.TRUE);
// Add updated CmAuditingConfiguration object to list,
// and set list on domain.
sclist.add(auditConfig);
domain.set_SubsystemConfigurations(sclist);
// Save domain.
domain.save(RefreshMode.REFRESH);
C# Example
ICmAuditingConfiguration auditConfig = null;
// Create PropertyFilter for domain.
PropertyFilter pf = new PropertyFilter();
pf.AddIncludeProperty(new FilterElement(null, null, null, "SubsystemConfigurations", null));
// Get default domain.
IDomain domain = Factory.Domain.FetchInstance(ceConn, null, pf);
// Get domain's subsystem configurations,
// and iterate to get the default auditing configuration.
ISubsystemConfigurationList sclist = domain.SubsystemConfigurations;
foreach (ISubsystemConfiguration subsystemConfig in sclist)
{
if (subsystemConfig.GetClassName().Equals("CmAuditingConfiguration") )
{
auditConfig = (ICmAuditingConfiguration) subsystemConfig;
break;
}
}
// Create ICmTimeslotList object.
ICmTimeslotList tsList = Factory.CmTimeslot.CreateList();
// Create 1 of 2 CmTimeslot objects and configure it
// so that audit disposition runs every Wednesday for
// 4 hours, starting at 2 a.m.
ICmTimeslot ts1 = Factory.CmTimeslot.CreateInstance();
ts1.Weekday=Weekday.WEDNESDAY;
ts1.Duration=240; // 4 hours
ts1.StartMinutesPastMidnight = 120;
// Create 2 of 2 CmTimeslot objects and configure it
// so that audit disposition runs every weekend for
// 48 hours, starting at midnight Saturday.
ICmTimeslot ts2 = Factory.CmTimeslot.CreateInstance();
ts2.Weekday=Weekday.SATURDAY;
ts2.Duration = 2880;// 48 hours
ts2.StartMinutesPastMidnight = 0;
// Add CmTimeslot objects to the list.
tsList.Add(ts1);
tsList.Add(ts2);
// Set timeslots on ICmAuditingConfiguration and enable the object.
auditConfig.AuditDispositionTimeslots=tsList;
auditConfig.AuditDispositionEnabled=true;
// Add updated CmAuditingConfiguration object to list,
// and set list on domain.
sclist.Add(auditConfig);
domain.SubsystemConfigurations=sclist;
// Save domain.
domain.Save(RefreshMode.REFRESH);
Setting Object Store Policy
The following Java and C# examples show how to set a CmAuditDispositionPolicy object on an object store. The object's disposition rule specifies the selection criteria to be applied against the object store's audited event records. When activated, the disposition thread will delete update events after three months.
Java Example
// Create policy object.
CmAuditDispositionPolicy auditPolicy = Factory.CmAuditDispositionPolicy.createInstance(
store, ClassNames.CM_AUDIT_DISPOSITION_POLICY);
// Set display name and enable policy.
auditPolicy.set_DisplayName("DeleteUpdateEventsAfter3Months");
auditPolicy.set_IsEnabled(Boolean.TRUE);
// Set disposition rule.
String rule = "IsClass(source, UpdateEvent) AND DateCreated < NOW() - TimeSpan(90,'days')";
auditPolicy.set_DispositionRule(rule);
// Save policy object.
auditPolicy.save(RefreshMode.REFRESH);
C# Example
// Create policy object.
ICmAuditDispositionPolicy auditPolicy = Factory.CmAuditDispositionPolicy.CreateInstance(
store, ClassNames.CM_AUDIT_DISPOSITION_POLICY);
// Set display name and enable policy.
auditPolicy.DisplayName = "DeleteUpdateEventsAfter3Months";
auditPolicy.IsEnabled = true;
// Set disposition rule.
String rule = "IsClass(source, UpdateEvent) AND DateCreated < NOW() - TimeSpan(90,'days')";
auditPolicy.DispositionRule = rule;
// Save policy object.
auditPolicy.Save(RefreshMode.REFRESH);
Retrieving Audit Definitions from a Class
You can retrieve
the audit definitions that are defined for a class by getting the AuditDefinitions property
on an object that is derived from the SubscribableClassDefinition object. The returned
value is a list of AuditDefinition objects. By iterating the list
for AuditDefinition objects, you can find the audited
events for which the class is configured.
One use case
for retrieving audit definitions is to verify that a particular audit
event is configured for a class. In the following Java and C# code snippets, the FloodClaim subclass,
as represented by a DocumentClassDefinition object,
is tested for a custom event, which is specified by a GUID. If the FloodClaim class
is configured to be audited for the custom event, the code outputs
configuration information about the audited custom event.
Java Example
// Fetch class definition for FloodClaim.
DocumentClassDefinition dcd = Factory.DocumentClassDefinition.fetchInstance (
store, new Id("{00A3E7B8-3473-4136-B44C-5A05874E718C}"), null);
// Get list of AuditDefinition objects configured for the FloodClaim class,
// iterate through it, and test each AuditDefinition object for
// the custom event.
AuditDefinitionList adl=dcd.get_AuditDefinitions();
Iterator adlIter = adl.iterator();
while (adlIter.hasNext())
{
AuditDefinition ad = (AuditDefinition) adlIter.next();
EventClassDefinition ecd = ad.get_EventClass();
if (ecd.get_Id().equals(new Id("{9906D34A-DBE2-415E-B807-6B4CCA65B893}")))
{
System.out.println (
"Audited class is " + dcd.get_DisplayName() + "\n" +
"Audited custom event is " + ecd.get_DisplayName() + "\n" +
"Audit successes? " + ad.get_AuditSuccess() + "\n" +
"Audit failures? " + ad.get_AuditFailure() + "\n" +
"Audit subclasses? " + ad.get_IncludeSubclassesRequested() + "\n" +
"Recording level? " + ad.get_ObjectStateRecordingLevel()
);
}
}
C# Example
// Retrieve class definition for FloodClaim.
IDocumentClassDefinition dcd = Factory.DocumentClassDefinition.FetchInstance (
store, new Id("{00A3E7B8-3473-4136-B44C-5A05874E718C}"), null);
// Get list of AuditDefinition objects configured for the FloodClaim class,
// iterate through it, and test each AuditDefinition object for the custom event.
IAuditDefinitionList adl = dcd.AuditDefinitions;
foreach (IAuditDefinition ad in adl)
{
IEventClassDefinition ecd = ad.EventClass;
if (ecd.Id.Equals(new Id("{9906D34A-DBE2-415E-B807-6B4CCA65B893}")))
{
System.Console.WriteLine
(
"Audited class is " + dcd.Name + "\n" +
"Audited custom event is " + ecd.DisplayName + "\n" +
"Audited successes? " + ad.AuditSuccess + "\n" +
"Audited failures? " + ad.AuditFailure + "\n" +
"Audit subsclasses? " + ad.IncludeSubclassesRequested + "\n" +
"Recording level? " + ad.ObjectStateRecordingLevel
);
}
}
Retrieving Audit History from an Object
You can retrieve
audit history for instances of classes
with the AuditedEvents property. (You can also query the audit
event log for historical or analytical information.) The following Java and C# code snippets retrieve
a Document object's AuditedEvents property, and then
print property values for each Event object in the EventSet collection.
Note that the Document objects are instances
of the FloodClaim class, on which individual properties
were configured for auditing the update event, extending the Event
table with the source object properties. Therefore, as shown in the
examples, the source object properties can be retrieved from the returned
audited events.
Java Example
// Get Document object from which audit history will be retrieved.
Document doc=Factory.Document.fetchInstance(store, new Id("{7AFAC25E-D27B-49A8-B31C-E555EC87608A}"),null);
// Get audited events that have been fired on Document object.
EventSet es = doc.get_AuditedEvents();
// If EventSet collection not empty, iterate it for events that
// have been fired on Document object.
if (!es.isEmpty())
{
Iterator esIter = es.iterator();
while (esIter.hasNext())
{
Event event = (Event) esIter.next();
System.out.println(
"Event is " + event.getClassName() + "\n" +
"Event ID is " + event.get_Id().toString()+ "\n" +
"Event initiator is " + event.get_InitiatingUser() + "\n" +
"Event date is " + event.get_DateCreated().toString() + "\n" +
"Source object properties extended on event:"+ "\n" +
" auditLocationCode: " + event.getProperties().getStringValue("auditLocationCode") + "\n" +
" auditStatus: " + event.getProperties().getStringValue("auditStatus") + "\n" +
" auditLastModifier: " + event.getProperties().getStringValue("auditLastModifier")+ "\n" +
"==================================="
);
}
}
else System.out.println("No audited events have been fired for this object");
C# Example
// Get Document object from which audit history will be retrieved.
IDocument doc = Factory.Document.FetchInstance (
store, new Id("{7AFAC25E-D27B-49A8-B31C-E555EC87608A}"), null);
//Get audited events that have been fired on Document object.
IEventSet es = doc.AuditedEvents;
// If EventSet collection is not empty, iterate it for events that have been fired on Document object.
if (!es.IsEmpty())
{
foreach (IEvent docEvent in es)
{
System.Console.WriteLine
(
"Event is " + docEvent.GetClassName() + "\n" +
"Event ID is " + docEvent.Id.ToString() + "\n" +
"Event initiator is " + docEvent.InitiatingUser + "\n" +
"Event date is " + docEvent.DateCreated.ToString() + "\n" +
"Source object properties extended on event:" + "\n" +
" auditLocationCode: " + docEvent.Properties.GetStringValue("auditLocationCode") + "\n" +
" auditStatus: " + docEvent.Properties.GetStringValue("auditStatus") + "\n" +
" auditLastModifier: " + docEvent.Properties.GetStringValue("auditLastModifier")+ "\n" +
"==================================="
);
}
}
else
{
System.Console.WriteLine("No audited events have been fired for this object");
}
Querying the Audit Event Log
You can query the audit event log, which is the Event table in an object store's database. The easiest way to query the log is with Administration Console for Content Platform Engine, from which you can create SQL searches or start search templates. However, you can also use the Content Java and .NET APIs to work with the audit log.
The APIs include query
classes that are designed for ad hoc searches, which allow you to
pass a SQL statement to the Content Engine. As
shown in the following Java and
C# examples, a SearchSQL object is constructed with
a SQL statement string that searches for events with a source object
property equal to a specified value. To run the search, the SearchSQL object
is passed in the fetchRows call on the SearchScope object.
Then, the property values are printed for each audited event that
satisfies the condition of the SQL statement.
For overview information, see Queries. For information on which properties are searchable, selectable, and orderable, refer to the metadata for each property in Event Properties. For code examples that show how to retrieve source objects from queried events, see Retrieving Source Objects.
Java Example
// Build the SQL select statement, and include source object properties that are configured for auditing.
String sqlString = "SELECT " + PropertyNames.DATE_LAST_MODIFIED + "," +
"auditLastModifier" + "," + "auditLocationCode" + "," + "auditStatus";
// Construct SearchSQL object to find events extended with audited source object properties.
SearchSQL sql = new SearchSQL(sqlString + " FROM Event" +
" WHERE " + "auditStatus IN ('Opened', 'Closed')" +
" ORDER BY " + "auditLocationCode" + "," + PropertyNames.DATE_LAST_MODIFIED);
// Execute the search operation.
SearchScope ss = new SearchScope(store);
RepositoryRowSet rrc = ss.fetchRows(sql, null, null, null);
// Iterate and print search results.
Iterator it = rrc.iterator();
while (it.hasNext())
{
RepositoryRow rr = (RepositoryRow) it.next();
System.out.println
(
"Location Code: " + rr.getProperties().getStringValue("auditLocationCode") + "\n" +
"Claim Status: " + rr.getProperties().getStringValue("auditStatus") + "\n" +
"Date: " + rr.getProperties().getDateTimeValue(PropertyNames.DATE_LAST_MODIFIED)+ "\n" +
"Adjuster: " + rr.getProperties().getStringValue("auditLastModifier") + "\n" +
"==================================="
);
}
C# Example
// Build the SQL select statement, and include source object properties that are configured for auditing.
string sqlString = "SELECT " + PropertyNames.DATE_LAST_MODIFIED + "," +
"auditLastModifier" + "," + "auditLocationCode" + "," + "auditStatus";
// Construct SearchSQL object to find events extended with audited source object properties.
SearchSQL sql = new SearchSQL(sqlString + " FROM Event" +
" WHERE " + "auditStatus IN ('Opened', 'Closed')" +
" ORDER BY " + "auditLocationCode" + "," + PropertyNames.DATE_LAST_MODIFIED);
// Execute the search operation.
SearchScope ss = new SearchScope(store);
IRepositoryRowSet rrc = ss.FetchRows(sql, null, null, null);
// Iterate and print search results.
foreach (IRepositoryRow rr in rrc)
{
System.Console.WriteLine
(
"Location Code: " + rr.Properties.GetStringValue("auditLocationCode") + "\n" +
"Claim Status: " + rr.Properties.GetStringValue("auditStatus") + "\n" +
"Date: " + rr.Properties.GetDateTimeValue(PropertyNames.DATE_LAST_MODIFIED) + "\n" +
"Adjuster: " + rr.Properties.GetStringValue("auditLastModifier") + "\n"
);
}
Using Audit Bookmarks
The following Java and C# examples show how to set a CmAuditProcessingBookmark object, an action that an audit processing client performs when audit disposition is enabled on the Content Engine. If the FloodClaims bookmark exists, indicating that the client previously processed the audit log (Event table), then the client begins processing the log at the point where it was last read, which is determined by the bookmark's LastProcessed property. When the client completes processing for the current session, it updates the LastProcessed property and saves the "FloodClaims" bookmark.
Java Example
CmAuditProcessingBookmark bookmark = null;
Double lastProcessedRecord = new Double(0);
Id bookmarkId = null;
Boolean bookmarkExist = Boolean.FALSE;
// Get existing bookmarks from object store,
// and iterate in search of "FloodClaims" bookmark.
CmAuditProcessingBookmarkSet bookmarks = store.get_AuditProcessingBookmarks();
Iterator it = bookmarks.iterator();
while (it.hasNext())
{
bookmark = (CmAuditProcessingBookmark) it.next();
System.out.println("Bookmark name is " + bookmark.get_DisplayName() );
if (bookmark.get_DisplayName().equals("FloodClaims") )
{
bookmarkExist = Boolean.TRUE;
// If "FloodClaims" bookmark exists, get the audit sequence number of the last processed record;
lastProcessedRecord = bookmark.get_LastProcessed();
break;
}
}
// If "FloodClaims" bookmark does not exist, create it.
if (bookmarkExist == Boolean.FALSE)
{
bookmark = Factory.CmAuditProcessingBookmark.createInstance(store,
ClassNames.CM_AUDIT_PROCESSING_BOOKMARK);
bookmark.set_DisplayName("FloodClaims");
}
// Build the SQL select statement, and include source object properties that are configured for auditing.
String sqlString = "SELECT " + PropertyNames.DATE_LAST_MODIFIED + "," +
"auditLastModifier" + "," + "auditLocationCode" + "," + "auditStatus";
// Construct SearchSQL object to find events extended with audited source object properties.
// Exclude events that were previously processed.
SearchSQL sql = new SearchSQL(sqlString + " FROM Event" +
" WHERE " + PropertyNames.CM_AUDIT_SEQUENCE + ">" + lastProcessedRecord +
" AND " + "auditStatus IN ('Opened', 'Closed')" +
" ORDER BY " + "auditLocationCode" + "," + PropertyNames.DATE_LAST_MODIFIED);
// Execute the search operation.
SearchScope ss = new SearchScope(store);
RepositoryRowSet rrc = ss.fetchRows(sql, null, null, null);
// Iterate and print search results.
Iterator it = rrc.iterator();
while (it.hasNext())
{
RepositoryRow rr = (RepositoryRow) it.next();
System.out.println
(
"Location Code: " + rr.getProperties().getStringValue("auditLocationCode") + "\n" +
"Claim Status: " + rr.getProperties().getStringValue("auditStatus") + "\n" +
"Date: " + rr.getProperties().getDateTimeValue(PropertyNames.DATE_LAST_MODIFIED)+ "\n" +
"Adjuster: " + rr.getProperties().getStringValue("auditLastModifier") + "\n" +
"==================================="
);
// Update lastProcessedRecord.
lastProcessedRecord = rr.getProperties().getFloat64Value(PropertyNames.CM_AUDIT_SEQUENCE);
}
// Update bookmark with audit sequence number of the last processed record.
bookmark.set_LastProcessed(lastProcessedRecord);
// Save bookmark.
bookmark.save(RefreshMode.REFRESH);
C# Example
ICmAuditProcessingBookmark bookmark;
Double lastProcessedRecord = 0;
Id bookmarkId = null;
Boolean bookmarkExist = false;
// Get existing bookmarks from object store,
// and iterate in search of "FloodClaims" bookmark.
ICmAuditProcessingBookmarkSet bookmarks = store.AuditProcessingBookmarks;
foreach (ICmAuditProcessingBookmark bookmarkItem in bookmarks)
{
System.Console.WriteLine("Bookmark name is " + bookmarkItem.DisplayName);
if (bookmarkItem.DisplayName.Equals("FloodClaims"))
{
bookmarkExist = true;
bookmark = bookmarkItem;
// If "FloodClaims" bookmark exists, get the audit sequence number of the last processed record;
lastProcessedRecord = (Double) bookmark.LastProcessed;
break;
}
}
// If "FloodClaims" bookmark does not exist, create it.
if (bookmarkExist == false)
{
bookmark = Factory.CmAuditProcessingBookmark.CreateInstance(store,
ClassNames.CM_AUDIT_PROCESSING_BOOKMARK);
bookmark.DisplayName = "FloodClaims";
}
// Build the SQL select statement, and include source object properties that are configured for auditing.
String sqlString = "SELECT " + PropertyNames.DATE_LAST_MODIFIED + "," +
"auditLastModifier" + "," + "auditLocationCode" + "," + "auditStatus";
// Construct SearchSQL object to find events extended with audited source object properties.
// Exclude events that were previously processed.
SearchSQL sql = new SearchSQL(sqlString + " FROM Event" +
" WHERE " + PropertyNames.CM_AUDIT_SEQUENCE + ">" + lastProcessedRecord +
" AND " + "auditStatus IN ('Opened', 'Closed')" +
" ORDER BY " + "auditLocationCode" + "," + PropertyNames.DATE_LAST_MODIFIED);
// Execute the search operation.
SearchScope ss = new SearchScope(store);
IRepositoryRowSet rrc = ss.FetchRows(sql, null, null, null);
// Iterate and print search results.
foreach (IRepositoryRow rr in rrc)
{
System.Console.WriteLine
(
"Location Code: " + rr.Properties.GetStringValue("auditLocationCode") + "\n" +
"Claim Status: " + rr.Properties.GetStringValue("auditStatus") + "\n" +
"Date: " + rr.Properties.GetDateTimeValue(PropertyNames.DATE_LAST_MODIFIED) + "\n" +
"Adjuster: " + rr.Properties.GetStringValue("auditLastModifier") + "\n" +
"==================================="
);
// Update lastProcessedRecord.
lastProcessedRecord = (Double) rr.Properties.GetFloat64Value(PropertyNames.CM_AUDIT_SEQUENCE);
}
// Update bookmark with audit sequence number of the last processed record.
bookmark.LastProcessed = lastProcessedRecord;
// Save bookmark.
bookmark.Save(RefreshMode.REFRESH);
Retrieving Source Objects
You can configure an AuditDefinition to record the source object of an event (the object on which the event is fired). Then, when the event is triggered, the source object is persisted to the audit event log (the Event table in the database), along with other event information.
To control the extra database space that is required to store
source objects in audited events, you can configure the recording
level of the source object with the AuditDefinition.ObjectStateRecordingLevel property.
The recording levels are ORIGINAL_AND_MODIFIED_OBJECTS (both
the original, pre-event object and the modified, post-event object
are recorded), MODIFIED_OBJECT (only the modified,
post-event object is recorded), and NONE (no source
objects are recorded). If NONE is specified, then
the event's ModifiedProperties property is also be empty because this
value is derived from the event's SourceObject property.
The following Java and C#
examples show how to retrieve source objects from the audit event
log. The SQL statement specifies that check-in events of a certain
age be retrieved from the log, and includes properties for the source
modified object and the source original object. After the search runs,
the code iterates the results. For each audited Event object,
the code prints information about the event and the event's source
objects, if any. The code checks for events in which only the modified
source object was recorded or in which no source objects were recorded.
WHERE clause to limit
the result set.Java Example
// Build the SQL select statement.
String sqlString = "SELECT " + PropertyNames.ID +"," + PropertyNames.DATE_CREATED + "," +
PropertyNames.EVENT_STATUS + "," + PropertyNames.ORIGINAL_OBJECT + "," +
PropertyNames.SOURCE_OBJECT;
// Construct SearchSQL object to be used in search operation.
SearchSQL sql = new SearchSQL(sqlString + " FROM " + GuidConstants.Class_CheckinEvent +
" WHERE " + PropertyNames.DATE_CREATED + "> 20100401T080000Z" +
" ORDER BY " + PropertyNames.DATE_CREATED);
// Execute the search operation.
SearchScope ss = new SearchScope(store);
RepositoryRowSet rrc = ss.fetchRows(sql, null, null, null);
// Iterate and print results.
Iterator it = rrc.iterator();
while (it.hasNext())
{
RepositoryRow rr = (RepositoryRow) it.next();
// Get recorded Event object and print information about it
Id eventId = rr.getProperties().getIdValue(PropertyNames.ID);
Event eventObject = Factory.Event.fetchInstance(store, eventId, null);
String eventStatus = rr.getProperties().getInteger32Value(
PropertyNames.EVENT_STATUS).equals(new Integer(0)) ?
"Succeeded" : "Failed";
System.out.println("Event Properties:\n" +
" Event ID: " + eventId.toString()+ "\n" +
" Event Name: " + eventObject.getClassName()+ "\n" +
" Date Created: " + rr.getProperties().getDateTimeValue(PropertyNames.DATE_CREATED)+ "\n" +
" Event Status: " + eventStatus
);
// Get source modified object and print name of object and date last modified.
// If the modified object was not recorded, null returned.
IndependentObject modifiedObject = (IndependentObject)rr.getProperties().getObjectValue(
PropertyNames.SOURCE_OBJECT);
if (modifiedObject!=null)
{
try {
System.out.println("Source modified object: " +
"\n Name: " + modifiedObject.getProperties().getStringValue(PropertyNames.NAME) +
"\n Date object modified: " + modifiedObject.getProperties().getDateTimeValue(
PropertyNames.DATE_LAST_MODIFIED) );
}
catch (EngineRuntimeException e)
{
System.out.println("Source modified object:\n " + e.getMessage() );
}
}
else
{
// If the modified object was not recorded then neither was the original object.
System.out.println("There is no source modified or source original object recorded for this
audited event." + "\n=================================\n");
continue;
}
// Get source original object and print name of object and date last modified.
// If original object was not recorded, null returned.
IndependentObject originalObject = (IndependentObject) rr.getProperties().getObjectValue(
PropertyNames.ORIGINAL_OBJECT);
if (originalObject!=null)
{
try {
System.out.println("Source original object: " +
"\n Name: " + originalObject.getProperties().getStringValue(PropertyNames.NAME) +
"\n Date object modified: " + originalObject.getProperties().getDateTimeValue(
PropertyNames.DATE_LAST_MODIFIED) );
}
catch (EngineRuntimeException e)
{
System.out.println("Source original object: \n " + e.getMessage() );
}
System.out.println("\n=================================\n");
}
else
{
System.out.println("There is no source original object recorded for this audited event." +
"\n=================================\n");
}
}
C# Example
// Build the SQL select statement.
String sqlString = "SELECT " + PropertyNames.ID + "," + PropertyNames.DATE_CREATED + "," +
PropertyNames.EVENT_STATUS + "," + PropertyNames.ORIGINAL_OBJECT + "," +
PropertyNames.SOURCE_OBJECT;
// Construct SearchSQL object to be used in search operation.
SearchSQL sql = new SearchSQL(sqlString + " FROM " + GuidConstants.Class_CheckinEvent +
" WHERE " + PropertyNames.DATE_CREATED + "> 20100401T080000Z" +
" ORDER BY " + PropertyNames.DATE_CREATED);
// Execute the search operation.
SearchScope ss = new SearchScope(store);
IRepositoryRowSet rrc = ss.FetchRows(sql, null, null, null);
// Iterate and print results.
foreach (IRepositoryRow rr in rrc)
{
IIndependentObject modifiedObject;
IEvent eventObject;
// Get recorded Event object and print information about it.
Id eventId = rr.Properties.GetIdValue(PropertyNames.ID);
eventObject = Factory.Event.FetchInstance(store, eventId, null);
String eventStatus = rr.Properties.GetInteger32Value(PropertyNames.EVENT_STATUS) == 0 ?
"Succeeded" : "Failed";
System.Console.WriteLine("Event Properties:\n" +
" Event ID: " + eventId.ToString() + "\n" +
" Event Name: " + eventObject.GetClassName() + "\n" +
" Date Created: " + rr.Properties.GetDateTimeValue(PropertyNames.DATE_CREATED)+ "\n" +
" Event Status: " + eventStatus
);
// Get source modified object and print name of object and date last modified.
// If the modified object was not recorded, null returned.
modifiedObject = (IIndependentObject)rr.Properties.GetObjectValue(PropertyNames.SOURCE_OBJECT);
if (modifiedObject!=null)
{
try {
System.Console.WriteLine("Source modified object: " +
"\n Name: " + modifiedObject.Properties.GetStringValue(PropertyNames.NAME) +
"\n Date object modified: " + modifiedObject.Properties.GetDateTimeValue(
PropertyNames.DATE_LAST_MODIFIED) );
}
catch (EngineRuntimeException e)
{
System.Console.WriteLine("Source modified object:\n " + e.Message );
}
}
else
{
// If the modified object was not recorded then neither was the original object.
System.Console.WriteLine("There is no source modified or source original object recorded for
this audited event." + "\n=================================\n");
continue;
}
// Get source original object and print name of object and date last modified.
// If original object was not recorded, null returned.
IIndependentObject originalObject = (IIndependentObject)rr.Properties.GetObjectValue(
PropertyNames.ORIGINAL_OBJECT);
if (originalObject!=null)
{
try {
System.Console.WriteLine("Source original object: " +
"\n Name: " + originalObject.Properties.GetStringValue(PropertyNames.NAME) +
"\n Date object modified: " + originalObject.Properties.GetDateTimeValue(
PropertyNames.DATE_LAST_MODIFIED) );
}
catch (EngineRuntimeException e)
{
System.Console.WriteLine("Source original object: \n " + e.Message );
}
System.Console.WriteLine("\n=================================\n");
}
else
{
System.Console.WriteLine("There is no source original object recorded for this audited
event." + "\n=================================\n");
}
}