1 17 package org.alfresco.repo.node.integrity; 18 19 import java.io.Serializable ; 20 import java.util.ArrayList ; 21 import java.util.Collections ; 22 import java.util.HashMap ; 23 import java.util.List ; 24 import java.util.Map ; 25 26 import org.alfresco.error.AlfrescoRuntimeException; 27 import org.alfresco.repo.node.NodeServicePolicies; 28 import org.alfresco.repo.policy.JavaBehaviour; 29 import org.alfresco.repo.policy.PolicyComponent; 30 import org.alfresco.repo.transaction.AlfrescoTransactionSupport; 31 import org.alfresco.service.cmr.dictionary.AspectDefinition; 32 import org.alfresco.service.cmr.dictionary.AssociationDefinition; 33 import org.alfresco.service.cmr.dictionary.ClassDefinition; 34 import org.alfresco.service.cmr.dictionary.DictionaryException; 35 import org.alfresco.service.cmr.dictionary.DictionaryService; 36 import org.alfresco.service.cmr.repository.AssociationRef; 37 import org.alfresco.service.cmr.repository.ChildAssociationRef; 38 import org.alfresco.service.cmr.repository.NodeRef; 39 import org.alfresco.service.cmr.repository.NodeService; 40 import org.alfresco.service.namespace.NamespaceService; 41 import org.alfresco.service.namespace.QName; 42 import org.apache.commons.logging.Log; 43 import org.apache.commons.logging.LogFactory; 44 45 76 public class IntegrityChecker 77 implements NodeServicePolicies.OnCreateNodePolicy, 78 NodeServicePolicies.OnUpdatePropertiesPolicy, 79 NodeServicePolicies.OnDeleteNodePolicy, 80 NodeServicePolicies.OnAddAspectPolicy, 81 NodeServicePolicies.OnRemoveAspectPolicy, 82 NodeServicePolicies.OnCreateChildAssociationPolicy, 83 NodeServicePolicies.OnDeleteChildAssociationPolicy, 84 NodeServicePolicies.OnCreateAssociationPolicy, 85 NodeServicePolicies.OnDeleteAssociationPolicy 86 { 87 private static Log logger = LogFactory.getLog(IntegrityChecker.class); 88 89 90 private static final String KEY_EVENT_SET = "IntegrityChecker.EventSet"; 91 92 private PolicyComponent policyComponent; 93 private DictionaryService dictionaryService; 94 private NodeService nodeService; 95 private boolean enabled; 96 private boolean failOnViolation; 97 private int maxErrorsPerTransaction; 98 private boolean traceOn; 99 100 102 public IntegrityChecker() 103 { 104 this.enabled = true; 105 this.failOnViolation = false; 106 this.maxErrorsPerTransaction = 10; 107 this.traceOn = false; 108 } 109 110 113 public void setPolicyComponent(PolicyComponent policyComponent) 114 { 115 this.policyComponent = policyComponent; 116 } 117 118 121 public void setDictionaryService(DictionaryService dictionaryService) 122 { 123 this.dictionaryService = dictionaryService; 124 } 125 126 129 public void setNodeService(NodeService nodeService) 130 { 131 this.nodeService = nodeService; 132 } 133 134 137 public void setEnabled(boolean enabled) 138 { 139 this.enabled = enabled; 140 } 141 142 146 public void setTraceOn(boolean traceOn) 147 { 148 this.traceOn = traceOn; 149 } 150 151 155 public void setFailOnViolation(boolean failOnViolation) 156 { 157 this.failOnViolation = failOnViolation; 158 } 159 160 164 public void setMaxErrorsPerTransaction(int maxLogNumberPerTransaction) 165 { 166 this.maxErrorsPerTransaction = maxLogNumberPerTransaction; 167 } 168 169 172 public void init() 173 { 174 if (dictionaryService == null) 176 throw new AlfrescoRuntimeException("IntegrityChecker property not set: dictionaryService"); 177 if (nodeService == null) 178 throw new AlfrescoRuntimeException("IntegrityChecker property not set: nodeService"); 179 if (policyComponent == null) 180 throw new AlfrescoRuntimeException("IntegrityChecker property not set: policyComponent"); 181 182 if (enabled) { 184 policyComponent.bindClassBehaviour( 186 QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), 187 this, 188 new JavaBehaviour(this, "onCreateNode")); 189 policyComponent.bindClassBehaviour( 190 QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), 191 this, 192 new JavaBehaviour(this, "onUpdateProperties")); 193 policyComponent.bindClassBehaviour( 194 QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"), 195 this, 196 new JavaBehaviour(this, "onDeleteNode")); 197 policyComponent.bindClassBehaviour( 198 QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"), 199 this, 200 new JavaBehaviour(this, "onAddAspect")); 201 policyComponent.bindClassBehaviour( 202 QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"), 203 this, 204 new JavaBehaviour(this, "onRemoveAspect")); 205 policyComponent.bindAssociationBehaviour( 206 QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"), 207 this, 208 new JavaBehaviour(this, "onCreateChildAssociation")); 209 policyComponent.bindAssociationBehaviour( 210 QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteChildAssociation"), 211 this, 212 new JavaBehaviour(this, "onDeleteChildAssociation")); 213 policyComponent.bindAssociationBehaviour( 214 QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateAssociation"), 215 this, 216 new JavaBehaviour(this, "onCreateAssociation")); 217 policyComponent.bindAssociationBehaviour( 218 QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteAssociation"), 219 this, 220 new JavaBehaviour(this, "onDeleteAssociation")); 221 } 222 } 223 224 229 @SuppressWarnings ("unchecked") 230 private void save(IntegrityEvent event) 231 { 232 if (traceOn) 234 { 235 Throwable t = new Throwable (); 237 t.fillInStackTrace(); 238 StackTraceElement [] trace = t.getStackTrace(); 239 240 event.addTrace(trace); 241 } 243 244 AlfrescoTransactionSupport.bindIntegrityChecker(this); 246 247 Map <IntegrityEvent, IntegrityEvent> events = 249 (Map <IntegrityEvent, IntegrityEvent>) AlfrescoTransactionSupport.getResource(KEY_EVENT_SET); 250 if (events == null) 251 { 252 events = new HashMap <IntegrityEvent, IntegrityEvent>(113, 0.75F); 253 AlfrescoTransactionSupport.bindResource(KEY_EVENT_SET, events); 254 } 255 IntegrityEvent existingEvent = events.get(event); 257 if (existingEvent != null) 258 { 259 if (traceOn) 261 { 262 existingEvent.getTraces().addAll(event.getTraces()); 263 } 264 } 265 else 266 { 267 events.put(event, event); 269 } 270 if (logger.isDebugEnabled()) 271 { 272 logger.debug("" + (existingEvent != null ? "Event already present in" : "Added event to") + " event set: \n" + 273 " event: " + event); 274 } 275 } 276 277 280 public void onCreateNode(ChildAssociationRef childAssocRef) 281 { 282 IntegrityEvent event = null; 283 event = new PropertiesIntegrityEvent( 285 nodeService, 286 dictionaryService, 287 childAssocRef.getChildRef()); 288 save(event); 289 290 event = new AssocTargetRoleIntegrityEvent( 292 nodeService, 293 dictionaryService, 294 childAssocRef.getParentRef(), 295 childAssocRef.getTypeQName(), 296 childAssocRef.getQName()); 297 save(event); 298 299 NodeRef childRef = childAssocRef.getChildRef(); 301 QName childNodeTypeQName = nodeService.getType(childRef); 302 ClassDefinition nodeTypeDef = dictionaryService.getClass(childNodeTypeQName); 303 if (nodeTypeDef == null) 304 { 305 throw new DictionaryException("The node type is not recognized: " + childNodeTypeQName); 306 } 307 Map <QName, AssociationDefinition> childAssocDefs = nodeTypeDef.getAssociations(); 308 309 for (AssociationDefinition assocDef : childAssocDefs.values()) 311 { 312 QName assocTypeQName = assocDef.getName(); 313 event = new AssocTargetMultiplicityIntegrityEvent( 315 nodeService, 316 dictionaryService, 317 childRef, 318 assocTypeQName, 319 false); 320 save(event); 321 } 322 } 323 324 327 public void onUpdateProperties( 328 NodeRef nodeRef, 329 Map <QName, Serializable > before, 330 Map <QName, Serializable > after) 331 { 332 IntegrityEvent event = null; 333 event = new PropertiesIntegrityEvent(nodeService, dictionaryService, nodeRef); 335 save(event); 336 } 337 338 341 public void onDeleteNode(ChildAssociationRef childAssocRef) 342 { 343 } 344 345 348 public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName) 349 { 350 IntegrityEvent event = null; 351 event = new PropertiesIntegrityEvent(nodeService, dictionaryService, nodeRef); 353 save(event); 354 355 AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName); 357 if (aspectDef == null) 358 { 359 throw new DictionaryException("The aspect type is not recognized: " + aspectTypeQName); 360 } 361 Map <QName, AssociationDefinition> assocDefs = aspectDef.getAssociations(); 362 363 for (AssociationDefinition assocDef : assocDefs.values()) 365 { 366 QName assocTypeQName = assocDef.getName(); 367 event = new AssocTargetMultiplicityIntegrityEvent( 369 nodeService, 370 dictionaryService, 371 nodeRef, 372 assocTypeQName, 373 false); 374 save(event); 375 } 376 } 377 378 381 public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName) 382 { 383 } 384 385 public void onCreateChildAssociation(ChildAssociationRef childAssocRef) 386 { 387 IntegrityEvent event = null; 388 event = new AssocSourceTypeIntegrityEvent( 390 nodeService, 391 dictionaryService, 392 childAssocRef.getParentRef(), 393 childAssocRef.getTypeQName()); 394 save(event); 395 event = new AssocTargetTypeIntegrityEvent( 397 nodeService, 398 dictionaryService, 399 childAssocRef.getChildRef(), 400 childAssocRef.getTypeQName()); 401 save(event); 402 event = new AssocSourceMultiplicityIntegrityEvent( 404 nodeService, 405 dictionaryService, 406 childAssocRef.getChildRef(), 407 childAssocRef.getTypeQName(), 408 false); 409 save(event); 410 event = new AssocTargetMultiplicityIntegrityEvent( 412 nodeService, 413 dictionaryService, 414 childAssocRef.getParentRef(), 415 childAssocRef.getTypeQName(), 416 false); 417 save(event); 418 event = new AssocTargetRoleIntegrityEvent( 420 nodeService, 421 dictionaryService, 422 childAssocRef.getParentRef(), 423 childAssocRef.getTypeQName(), 424 childAssocRef.getQName()); 425 save(event); 426 } 427 428 431 public void onDeleteChildAssociation(ChildAssociationRef childAssocRef) 432 { 433 IntegrityEvent event = null; 434 event = new AssocSourceMultiplicityIntegrityEvent( 436 nodeService, 437 dictionaryService, 438 childAssocRef.getChildRef(), 439 childAssocRef.getTypeQName(), 440 true); 441 save(event); 442 event = new AssocTargetMultiplicityIntegrityEvent( 444 nodeService, 445 dictionaryService, 446 childAssocRef.getParentRef(), 447 childAssocRef.getTypeQName(), 448 true); 449 save(event); 450 } 451 452 455 public void onCreateAssociation(AssociationRef nodeAssocRef) 456 { 457 IntegrityEvent event = null; 458 event = new AssocSourceTypeIntegrityEvent( 460 nodeService, 461 dictionaryService, 462 nodeAssocRef.getSourceRef(), 463 nodeAssocRef.getTypeQName()); 464 save(event); 465 event = new AssocTargetTypeIntegrityEvent( 467 nodeService, 468 dictionaryService, 469 nodeAssocRef.getTargetRef(), 470 nodeAssocRef.getTypeQName()); 471 save(event); 472 event = new AssocSourceMultiplicityIntegrityEvent( 474 nodeService, 475 dictionaryService, 476 nodeAssocRef.getTargetRef(), 477 nodeAssocRef.getTypeQName(), 478 false); 479 save(event); 480 event = new AssocTargetMultiplicityIntegrityEvent( 482 nodeService, 483 dictionaryService, 484 nodeAssocRef.getSourceRef(), 485 nodeAssocRef.getTypeQName(), 486 false); 487 save(event); 488 } 489 490 493 public void onDeleteAssociation(AssociationRef nodeAssocRef) 494 { 495 IntegrityEvent event = null; 496 event = new AssocSourceMultiplicityIntegrityEvent( 498 nodeService, 499 dictionaryService, 500 nodeAssocRef.getTargetRef(), 501 nodeAssocRef.getTypeQName(), 502 true); 503 save(event); 504 event = new AssocTargetMultiplicityIntegrityEvent( 506 nodeService, 507 dictionaryService, 508 nodeAssocRef.getSourceRef(), 509 nodeAssocRef.getTypeQName(), 510 true); 511 save(event); 512 } 513 514 521 public void checkIntegrity() throws IntegrityException 522 { 523 if (!enabled) 524 { 525 return; 526 } 527 528 List <IntegrityRecord> failures = processAllEvents(); 530 AlfrescoTransactionSupport.unbindResource(KEY_EVENT_SET); 532 533 if (failures.isEmpty()) 535 { 536 return; 537 } 538 539 int failureCount = failures.size(); 542 StringBuilder sb = new StringBuilder (300 * failureCount); 543 sb.append("Found ").append(failureCount).append(" integrity violations"); 544 if (maxErrorsPerTransaction < failureCount) 545 { 546 sb.append(" - first ").append(maxErrorsPerTransaction); 547 } 548 sb.append(":"); 549 int count = 0; 550 for (IntegrityRecord failure : failures) 551 { 552 count++; 554 if (count > maxErrorsPerTransaction) 555 { 556 break; 557 } 558 sb.append("\n").append(failure); 559 } 560 if (failOnViolation) 561 { 562 logger.error(sb.toString()); 563 throw new IntegrityException(failures); 564 } 565 else 566 { 567 logger.warn(sb.toString()); 568 } 570 } 571 572 582 @SuppressWarnings ("unchecked") 583 private List <IntegrityRecord> processAllEvents() 584 { 585 ArrayList <IntegrityRecord> allIntegrityResults = new ArrayList <IntegrityRecord>(0); 588 Map <IntegrityEvent, IntegrityEvent> events = 591 (Map <IntegrityEvent, IntegrityEvent>) AlfrescoTransactionSupport.getResource(KEY_EVENT_SET); 592 if (events == null) 593 { 594 return allIntegrityResults; 596 } 597 598 List <IntegrityRecord> integrityRecords = new ArrayList <IntegrityRecord>(0); 600 601 for (IntegrityEvent event : events.keySet()) 603 { 604 try 605 { 606 event.checkIntegrity(integrityRecords); 607 } 608 catch (Throwable e) 609 { 610 e.printStackTrace(); 611 IntegrityRecord exceptionRecord = new IntegrityRecord("" + e.getMessage()); 613 exceptionRecord.setTraces(Collections.singletonList(e.getStackTrace())); 614 allIntegrityResults.add(exceptionRecord); 615 continue; 617 } 618 619 if (traceOn) 621 { 622 for (IntegrityRecord integrityRecord : integrityRecords) 624 { 625 integrityRecord.setTraces(event.getTraces()); 626 } 627 } 628 629 allIntegrityResults.addAll(integrityRecords); 631 integrityRecords.clear(); 633 634 if (allIntegrityResults.size() >= maxErrorsPerTransaction) 635 { 636 break; 638 } 639 } 640 return allIntegrityResults; 642 } 643 } 644 | Popular Tags |