mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-02 17:35:18 +00:00
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@3617 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
659 lines
24 KiB
Java
659 lines
24 KiB
Java
/*
|
|
* Copyright (C) 2005 Alfresco, Inc.
|
|
*
|
|
* Licensed under the Mozilla Public License version 1.1
|
|
* with a permitted attribution clause. You may obtain a
|
|
* copy of the License at
|
|
*
|
|
* http://www.alfresco.org/legal/license.txt
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
|
* either express or implied. See the License for the specific
|
|
* language governing permissions and limitations under the
|
|
* License.
|
|
*/
|
|
package org.alfresco.repo.node.integrity;
|
|
|
|
import java.io.Serializable;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import org.alfresco.repo.node.NodeServicePolicies;
|
|
import org.alfresco.repo.policy.JavaBehaviour;
|
|
import org.alfresco.repo.policy.PolicyComponent;
|
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
|
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
|
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
|
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
|
import org.alfresco.service.cmr.dictionary.DictionaryException;
|
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
|
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.NodeService;
|
|
import org.alfresco.service.namespace.NamespaceService;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.util.PropertyCheck;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
|
/**
|
|
* Component that generates and processes integrity events, enforcing the dictionary
|
|
* model's node structure.
|
|
* <p>
|
|
* In order to fulfill the contract of the interface, this class registers to receive notifications
|
|
* pertinent to changes in the node structure. These are then store away in the persistent
|
|
* store until the request to
|
|
* {@link org.alfresco.repo.integrity.IntegrityService#checkIntegrity(String) check integrity} is
|
|
* made.
|
|
* <p>
|
|
* In order to ensure registration of these events, the {@link #init()} method must be called.
|
|
* <p>
|
|
* By default, this service is enabled, but can be disabled using {@link #setEnabled(boolean)}.<br>
|
|
* Tracing of the event stacks is, for performance reasons, disabled by default but can be enabled
|
|
* using {@link #setTraceOn(boolean)}.<br>
|
|
* When enabled, the integrity check can either fail with a <tt>RuntimeException</tt> or not. In either
|
|
* case, the integrity violations are logged as warnings or errors. This behaviour is controleed using
|
|
* {@link #setFailOnViolation(boolean)} and is off by default. In other words, if not set, this service
|
|
* will only log warnings about integrity violations.
|
|
* <p>
|
|
* Some integrity checks are not performed here as they are dealt with directly during the modification
|
|
* operation in the {@link org.alfresco.service.cmr.repository.NodeService node service}.
|
|
*
|
|
* @see #setPolicyComponent(PolicyComponent)
|
|
* @see #setDictionaryService(DictionaryService)
|
|
* @see #setIntegrityDaoService(IntegrityDaoService)
|
|
* @see #setMaxErrorsPerTransaction(int)
|
|
* @see #setFlushSize(int)
|
|
*
|
|
* @author Derek Hulley
|
|
*/
|
|
public class IntegrityChecker
|
|
implements NodeServicePolicies.OnCreateNodePolicy,
|
|
NodeServicePolicies.OnUpdatePropertiesPolicy,
|
|
NodeServicePolicies.OnDeleteNodePolicy,
|
|
NodeServicePolicies.OnAddAspectPolicy,
|
|
NodeServicePolicies.OnRemoveAspectPolicy,
|
|
NodeServicePolicies.OnCreateChildAssociationPolicy,
|
|
NodeServicePolicies.OnDeleteChildAssociationPolicy,
|
|
NodeServicePolicies.OnCreateAssociationPolicy,
|
|
NodeServicePolicies.OnDeleteAssociationPolicy
|
|
{
|
|
private static Log logger = LogFactory.getLog(IntegrityChecker.class);
|
|
|
|
/** key against which the set of events is stored in the current transaction */
|
|
private static final String KEY_EVENT_SET = "IntegrityChecker.EventSet";
|
|
|
|
private PolicyComponent policyComponent;
|
|
private DictionaryService dictionaryService;
|
|
private NodeService nodeService;
|
|
private boolean enabled;
|
|
private boolean failOnViolation;
|
|
private int maxErrorsPerTransaction;
|
|
private boolean traceOn;
|
|
|
|
/**
|
|
*/
|
|
public IntegrityChecker()
|
|
{
|
|
this.enabled = true;
|
|
this.failOnViolation = false;
|
|
this.maxErrorsPerTransaction = 10;
|
|
this.traceOn = false;
|
|
}
|
|
|
|
/**
|
|
* @param policyComponent the component to register behaviour with
|
|
*/
|
|
public void setPolicyComponent(PolicyComponent policyComponent)
|
|
{
|
|
this.policyComponent = policyComponent;
|
|
}
|
|
|
|
/**
|
|
* @param dictionaryService the dictionary against which to confirm model details
|
|
*/
|
|
public void setDictionaryService(DictionaryService dictionaryService)
|
|
{
|
|
this.dictionaryService = dictionaryService;
|
|
}
|
|
|
|
/**
|
|
* @param nodeService the node service to use for browsing node structures
|
|
*/
|
|
public void setNodeService(NodeService nodeService)
|
|
{
|
|
this.nodeService = nodeService;
|
|
}
|
|
|
|
/**
|
|
* @param enabled set to false to disable integrity checking completely
|
|
*/
|
|
public void setEnabled(boolean enabled)
|
|
{
|
|
this.enabled = enabled;
|
|
}
|
|
|
|
/**
|
|
* @param traceOn set to <code>true</code> to enable stack traces recording
|
|
* of events
|
|
*/
|
|
public void setTraceOn(boolean traceOn)
|
|
{
|
|
this.traceOn = traceOn;
|
|
}
|
|
|
|
/**
|
|
* @param failOnViolation set to <code>true</code> to force failure by
|
|
* <tt>RuntimeException</tt> when a violation occurs.
|
|
*/
|
|
public void setFailOnViolation(boolean failOnViolation)
|
|
{
|
|
this.failOnViolation = failOnViolation;
|
|
}
|
|
|
|
/**
|
|
* @param maxLogNumberPerTransaction upper limit on how many violations are
|
|
* logged when multiple violations have been found.
|
|
*/
|
|
public void setMaxErrorsPerTransaction(int maxLogNumberPerTransaction)
|
|
{
|
|
this.maxErrorsPerTransaction = maxLogNumberPerTransaction;
|
|
}
|
|
|
|
/**
|
|
* Registers the system-level policy behaviours
|
|
*/
|
|
public void init()
|
|
{
|
|
// check that required properties have been set
|
|
PropertyCheck.mandatory("IntegrityChecker", "dictionaryService", dictionaryService);
|
|
PropertyCheck.mandatory("IntegrityChecker", "nodeService", nodeService);
|
|
PropertyCheck.mandatory("IntegrityChecker", "policyComponent", policyComponent);
|
|
|
|
if (enabled) // only register behaviour if integrity checking is on
|
|
{
|
|
// register behaviour
|
|
policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"),
|
|
this,
|
|
new JavaBehaviour(this, "onCreateNode"));
|
|
policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
|
|
this,
|
|
new JavaBehaviour(this, "onUpdateProperties"));
|
|
policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"),
|
|
this,
|
|
new JavaBehaviour(this, "onDeleteNode"));
|
|
policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
|
|
this,
|
|
new JavaBehaviour(this, "onAddAspect"));
|
|
policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"),
|
|
this,
|
|
new JavaBehaviour(this, "onRemoveAspect"));
|
|
policyComponent.bindAssociationBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"),
|
|
this,
|
|
new JavaBehaviour(this, "onCreateChildAssociation"));
|
|
policyComponent.bindAssociationBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteChildAssociation"),
|
|
this,
|
|
new JavaBehaviour(this, "onDeleteChildAssociation"));
|
|
policyComponent.bindAssociationBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateAssociation"),
|
|
this,
|
|
new JavaBehaviour(this, "onCreateAssociation"));
|
|
policyComponent.bindAssociationBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteAssociation"),
|
|
this,
|
|
new JavaBehaviour(this, "onDeleteAssociation"));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensures that this service is registered with the transaction and saves the event
|
|
*
|
|
* @param event
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
private void save(IntegrityEvent event)
|
|
{
|
|
// optionally set trace
|
|
if (traceOn)
|
|
{
|
|
// get a stack trace
|
|
Throwable t = new Throwable();
|
|
t.fillInStackTrace();
|
|
StackTraceElement[] trace = t.getStackTrace();
|
|
|
|
event.addTrace(trace);
|
|
// done
|
|
}
|
|
|
|
// register this service
|
|
AlfrescoTransactionSupport.bindIntegrityChecker(this);
|
|
|
|
// get the event list
|
|
Map<IntegrityEvent, IntegrityEvent> events =
|
|
(Map<IntegrityEvent, IntegrityEvent>) AlfrescoTransactionSupport.getResource(KEY_EVENT_SET);
|
|
if (events == null)
|
|
{
|
|
events = new HashMap<IntegrityEvent, IntegrityEvent>(113, 0.75F);
|
|
AlfrescoTransactionSupport.bindResource(KEY_EVENT_SET, events);
|
|
}
|
|
// check if the event is present
|
|
IntegrityEvent existingEvent = events.get(event);
|
|
if (existingEvent != null)
|
|
{
|
|
// the event (or its equivalent is already present - transfer the trace
|
|
if (traceOn)
|
|
{
|
|
existingEvent.getTraces().addAll(event.getTraces());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the event doesn't already exist
|
|
events.put(event, event);
|
|
}
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("" + (existingEvent != null ? "Event already present in" : "Added event to") + " event set: \n" +
|
|
" event: " + event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see PropertiesIntegrityEvent
|
|
* @see AssocTargetRoleIntegrityEvent
|
|
* @see AssocTargetMultiplicityIntegrityEvent
|
|
*/
|
|
public void onCreateNode(ChildAssociationRef childAssocRef)
|
|
{
|
|
NodeRef childRef = childAssocRef.getChildRef();
|
|
IntegrityEvent event = null;
|
|
// check properties on child node
|
|
event = new PropertiesIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
childRef);
|
|
save(event);
|
|
|
|
// check that the multiplicity and other properties of the new association are allowed
|
|
onCreateChildAssociation(childAssocRef);
|
|
|
|
// check mandatory aspects
|
|
event = new AspectsIntegrityEvent(nodeService, dictionaryService, childRef);
|
|
save(event);
|
|
|
|
// check for associations defined on the new node (child)
|
|
QName childNodeTypeQName = nodeService.getType(childRef);
|
|
ClassDefinition nodeTypeDef = dictionaryService.getClass(childNodeTypeQName);
|
|
if (nodeTypeDef == null)
|
|
{
|
|
throw new DictionaryException("The node type is not recognized: " + childNodeTypeQName);
|
|
}
|
|
Map<QName, AssociationDefinition> childAssocDefs = nodeTypeDef.getAssociations();
|
|
|
|
// check the multiplicity of each association with the node acting as a source
|
|
for (AssociationDefinition assocDef : childAssocDefs.values())
|
|
{
|
|
QName assocTypeQName = assocDef.getName();
|
|
// check target multiplicity
|
|
event = new AssocTargetMultiplicityIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
childRef,
|
|
assocTypeQName,
|
|
false);
|
|
save(event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see PropertiesIntegrityEvent
|
|
*/
|
|
public void onUpdateProperties(
|
|
NodeRef nodeRef,
|
|
Map<QName, Serializable> before,
|
|
Map<QName, Serializable> after)
|
|
{
|
|
IntegrityEvent event = null;
|
|
// check properties on node
|
|
event = new PropertiesIntegrityEvent(nodeService, dictionaryService, nodeRef);
|
|
save(event);
|
|
}
|
|
|
|
/**
|
|
* No checking performed: The association changes will be handled
|
|
*/
|
|
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isArchivedNode)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @see PropertiesIntegrityEvent
|
|
* @see AssocTargetMultiplicityIntegrityEvent
|
|
*/
|
|
public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName)
|
|
{
|
|
IntegrityEvent event = null;
|
|
// check properties on node
|
|
event = new PropertiesIntegrityEvent(nodeService, dictionaryService, nodeRef);
|
|
save(event);
|
|
|
|
// check for associations defined on the aspect
|
|
AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName);
|
|
if (aspectDef == null)
|
|
{
|
|
throw new DictionaryException("The aspect type is not recognized: " + aspectTypeQName);
|
|
}
|
|
Map<QName, AssociationDefinition> assocDefs = aspectDef.getAssociations();
|
|
|
|
// check the multiplicity of each association with the node acting as a source
|
|
for (AssociationDefinition assocDef : assocDefs.values())
|
|
{
|
|
QName assocTypeQName = assocDef.getName();
|
|
// check target multiplicity
|
|
event = new AssocTargetMultiplicityIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
nodeRef,
|
|
assocTypeQName,
|
|
false);
|
|
save(event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see AspectsIntegrityEvent
|
|
*/
|
|
public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
|
|
{
|
|
IntegrityEvent event = null;
|
|
// check mandatory aspects
|
|
event = new AspectsIntegrityEvent(nodeService, dictionaryService, nodeRef);
|
|
save(event);
|
|
|
|
}
|
|
|
|
/**
|
|
* @see AssocSourceTypeIntegrityEvent
|
|
* @see AssocTargetTypeIntegrityEvent
|
|
* @see AssocSourceMultiplicityIntegrityEvent
|
|
* @see AssocTargetMultiplicityIntegrityEvent
|
|
* @see AssocTargetRoleIntegrityEvent
|
|
*/
|
|
public void onCreateChildAssociation(ChildAssociationRef childAssocRef)
|
|
{
|
|
IntegrityEvent event = null;
|
|
// check source type
|
|
event = new AssocSourceTypeIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
childAssocRef.getParentRef(),
|
|
childAssocRef.getTypeQName());
|
|
save(event);
|
|
// check target type
|
|
event = new AssocTargetTypeIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
childAssocRef.getChildRef(),
|
|
childAssocRef.getTypeQName());
|
|
save(event);
|
|
// check source multiplicity
|
|
event = new AssocSourceMultiplicityIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
childAssocRef.getChildRef(),
|
|
childAssocRef.getTypeQName(),
|
|
false);
|
|
save(event);
|
|
// check target multiplicity
|
|
event = new AssocTargetMultiplicityIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
childAssocRef.getParentRef(),
|
|
childAssocRef.getTypeQName(),
|
|
false);
|
|
save(event);
|
|
// check target role
|
|
event = new AssocTargetRoleIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
childAssocRef.getParentRef(),
|
|
childAssocRef.getTypeQName(),
|
|
childAssocRef.getQName());
|
|
save(event);
|
|
}
|
|
|
|
/**
|
|
* @see AssocSourceMultiplicityIntegrityEvent
|
|
* @see AssocTargetMultiplicityIntegrityEvent
|
|
*/
|
|
public void onDeleteChildAssociation(ChildAssociationRef childAssocRef)
|
|
{
|
|
IntegrityEvent event = null;
|
|
// check source multiplicity
|
|
event = new AssocSourceMultiplicityIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
childAssocRef.getChildRef(),
|
|
childAssocRef.getTypeQName(),
|
|
true);
|
|
save(event);
|
|
// check target multiplicity
|
|
event = new AssocTargetMultiplicityIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
childAssocRef.getParentRef(),
|
|
childAssocRef.getTypeQName(),
|
|
true);
|
|
save(event);
|
|
}
|
|
|
|
/**
|
|
* @see AssocSourceTypeIntegrityEvent
|
|
* @see AssocTargetTypeIntegrityEvent
|
|
* @see AssocSourceMultiplicityIntegrityEvent
|
|
* @see AssocTargetMultiplicityIntegrityEvent
|
|
*/
|
|
public void onCreateAssociation(AssociationRef nodeAssocRef)
|
|
{
|
|
IntegrityEvent event = null;
|
|
// check source type
|
|
event = new AssocSourceTypeIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
nodeAssocRef.getSourceRef(),
|
|
nodeAssocRef.getTypeQName());
|
|
save(event);
|
|
// check target type
|
|
event = new AssocTargetTypeIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
nodeAssocRef.getTargetRef(),
|
|
nodeAssocRef.getTypeQName());
|
|
save(event);
|
|
// check source multiplicity
|
|
event = new AssocSourceMultiplicityIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
nodeAssocRef.getTargetRef(),
|
|
nodeAssocRef.getTypeQName(),
|
|
false);
|
|
save(event);
|
|
// check target multiplicity
|
|
event = new AssocTargetMultiplicityIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
nodeAssocRef.getSourceRef(),
|
|
nodeAssocRef.getTypeQName(),
|
|
false);
|
|
save(event);
|
|
}
|
|
|
|
/**
|
|
* @see AssocSourceMultiplicityIntegrityEvent
|
|
* @see AssocTargetMultiplicityIntegrityEvent
|
|
*/
|
|
public void onDeleteAssociation(AssociationRef nodeAssocRef)
|
|
{
|
|
IntegrityEvent event = null;
|
|
// check source multiplicity
|
|
event = new AssocSourceMultiplicityIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
nodeAssocRef.getTargetRef(),
|
|
nodeAssocRef.getTypeQName(),
|
|
true);
|
|
save(event);
|
|
// check target multiplicity
|
|
event = new AssocTargetMultiplicityIntegrityEvent(
|
|
nodeService,
|
|
dictionaryService,
|
|
nodeAssocRef.getSourceRef(),
|
|
nodeAssocRef.getTypeQName(),
|
|
true);
|
|
save(event);
|
|
}
|
|
|
|
/**
|
|
* Runs several types of checks, querying specifically for events that
|
|
* will necessitate each type of test.
|
|
* <p>
|
|
* The interface contracts also requires that all events for the transaction
|
|
* get cleaned up.
|
|
*/
|
|
public void checkIntegrity() throws IntegrityException
|
|
{
|
|
if (!enabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// process events and check for failures
|
|
List<IntegrityRecord> failures = processAllEvents();
|
|
// clear out all events
|
|
AlfrescoTransactionSupport.unbindResource(KEY_EVENT_SET);
|
|
|
|
// drop out quickly if there are no failures
|
|
if (failures.isEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// handle errors according to instance flags
|
|
// firstly, log all failures
|
|
int failureCount = failures.size();
|
|
StringBuilder sb = new StringBuilder(300 * failureCount);
|
|
sb.append("Found ").append(failureCount).append(" integrity violations");
|
|
if (maxErrorsPerTransaction < failureCount)
|
|
{
|
|
sb.append(" - first ").append(maxErrorsPerTransaction);
|
|
}
|
|
sb.append(":");
|
|
int count = 0;
|
|
for (IntegrityRecord failure : failures)
|
|
{
|
|
// break if we exceed the maximum number of log entries
|
|
count++;
|
|
if (count > maxErrorsPerTransaction)
|
|
{
|
|
break;
|
|
}
|
|
sb.append("\n").append(failure);
|
|
}
|
|
if (failOnViolation)
|
|
{
|
|
logger.error(sb.toString());
|
|
throw new IntegrityException(failures);
|
|
}
|
|
else
|
|
{
|
|
logger.warn(sb.toString());
|
|
// no exception
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loops through all the integrity events and checks integrity.
|
|
* <p>
|
|
* The events are stored in a set, so there are no duplicates. Since each
|
|
* event performs a particular type of check, this ensures that we don't
|
|
* duplicate checks.
|
|
*
|
|
* @return Returns a list of integrity violations, up to the
|
|
* {@link #maxErrorsPerTransaction the maximum defined}
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
private List<IntegrityRecord> processAllEvents()
|
|
{
|
|
// the results
|
|
ArrayList<IntegrityRecord> allIntegrityResults = new ArrayList<IntegrityRecord>(0); // generally empty
|
|
|
|
// get all the events for the transaction (or unit of work)
|
|
// duplicates have been elimiated
|
|
Map<IntegrityEvent, IntegrityEvent> events =
|
|
(Map<IntegrityEvent, IntegrityEvent>) AlfrescoTransactionSupport.getResource(KEY_EVENT_SET);
|
|
if (events == null)
|
|
{
|
|
// no events were registered - nothing of significance happened
|
|
return allIntegrityResults;
|
|
}
|
|
|
|
// failure results for the event
|
|
List<IntegrityRecord> integrityRecords = new ArrayList<IntegrityRecord>(0);
|
|
|
|
// cycle through the events, performing checking integrity
|
|
for (IntegrityEvent event : events.keySet())
|
|
{
|
|
try
|
|
{
|
|
event.checkIntegrity(integrityRecords);
|
|
}
|
|
catch (Throwable e)
|
|
{
|
|
e.printStackTrace();
|
|
// log it as an error and move to next event
|
|
IntegrityRecord exceptionRecord = new IntegrityRecord("" + e.getMessage());
|
|
exceptionRecord.setTraces(Collections.singletonList(e.getStackTrace()));
|
|
allIntegrityResults.add(exceptionRecord);
|
|
// move on
|
|
continue;
|
|
}
|
|
|
|
// keep track of results needing trace added
|
|
if (traceOn)
|
|
{
|
|
// record the current event trace if present
|
|
for (IntegrityRecord integrityRecord : integrityRecords)
|
|
{
|
|
integrityRecord.setTraces(event.getTraces());
|
|
}
|
|
}
|
|
|
|
// copy all the event results to the final results
|
|
allIntegrityResults.addAll(integrityRecords);
|
|
// clear the event results
|
|
integrityRecords.clear();
|
|
|
|
if (allIntegrityResults.size() >= maxErrorsPerTransaction)
|
|
{
|
|
// only so many errors wanted at a time
|
|
break;
|
|
}
|
|
}
|
|
// done
|
|
return allIntegrityResults;
|
|
}
|
|
}
|