IBM FileNet P8, Version 5.2.1            

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:

  1. Get a SubscribableClassDefinition object or subobject, such as the Document class.
  2. Get an EventClassDefinition object, such as the CreationEvent.
  3. Create an AuditDefinition object.
  4. Create an AuditDefinitionList object.
  5. Set the AuditDefinition object properties, such as EventClass, which takes the EventClassDefinition object.
  6. Add the AuditDefinition object to the AuditDefinitionList object.
  7. Repeat steps 2 - 6 for each event to be audited for a class.
  8. Set the SubscribableClassDefinition object's AuditDefinitions property to the AuditDefinitionList object.

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.

Note: If audit disposition is enabled on the Content Engine, audit processing clients must use audit bookmarks to ensure complete and accurate processing of the Event table. For an example, see Using Audit Bookmarks.

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.

Note: Because there can be substantial numbers of records in the Event table, it is advised that an SQL statement that queries this table include a 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");
    }
}


Last updated: October 2015
audit_procedures.htm

© Copyright IBM Corporation 2015.