1 56 package org.opencrx.kernel.layer.persistence; 57 58 import java.math.BigDecimal ; 59 import java.util.ArrayList ; 60 import java.util.Arrays ; 61 import java.util.Date ; 62 import java.util.HashMap ; 63 import java.util.HashSet ; 64 import java.util.Iterator ; 65 import java.util.List ; 66 import java.util.Map ; 67 import java.util.Set ; 68 69 import org.opencrx.kernel.generic.SecurityKeys; 70 import org.openmdx.base.exception.ServiceException; 71 import org.openmdx.compatibility.base.application.configuration.Configuration; 72 import org.openmdx.compatibility.base.collection.SparseList; 73 import org.openmdx.compatibility.base.dataprovider.cci.AttributeSelectors; 74 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderObject; 75 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderObject_1_0; 76 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderOperations; 77 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderReply; 78 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderReplyContexts; 79 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderRequest; 80 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderRequestContexts; 81 import org.openmdx.compatibility.base.dataprovider.cci.Directions; 82 import org.openmdx.compatibility.base.dataprovider.cci.ServiceHeader; 83 import org.openmdx.compatibility.base.dataprovider.cci.SystemAttributes; 84 import org.openmdx.compatibility.base.dataprovider.spi.Layer_1_0; 85 import org.openmdx.compatibility.base.naming.Path; 86 import org.openmdx.compatibility.base.query.FilterOperators; 87 import org.openmdx.compatibility.base.query.FilterProperty; 88 import org.openmdx.compatibility.base.query.Quantors; 89 90 93 public class Audit_1 94 extends ObjectMapping_1 { 95 96 public void activate( 98 short id, 99 Configuration configuration, 100 Layer_1_0 delegation 101 ) throws Exception , ServiceException { 102 super.activate(id, configuration, delegation); 103 } 104 105 public void prolog( 107 ServiceHeader header, 108 DataproviderRequest[] requests 109 ) throws ServiceException { 110 super.prolog( 111 header, 112 requests 113 ); 114 String unitOfWorkId = requests.length == 0 115 ? null 116 : requests[0].context(DataproviderRequestContexts.UNIT_OF_WORK_ID).size() == 0 117 ? null 118 : (String )requests[0].context(DataproviderRequestContexts.UNIT_OF_WORK_ID).get(0); 119 this.unitOfWorkId = unitOfWorkId != null 121 ? unitOfWorkId 122 : this.unitOfWorkId != null 123 ? this.unitOfWorkId 124 : super.uidAsString(); 125 } 126 127 private boolean isAuditee( 129 DataproviderObject_1_0 object 130 ) throws ServiceException { 131 String objectClass = (String )object.values(SystemAttributes.OBJECT_CLASS).get(0); 132 return this.model.isSubtypeOf( 133 objectClass, 134 "org:opencrx:kernel:base:Auditee" 135 ); 136 } 137 138 private boolean isAuditSegment( 140 ServiceHeader header, 141 Path path 142 ) throws ServiceException { 143 Path p = path.getPrefix(5); 144 Boolean isAuditSegment = (Boolean )this.auditSegments.get(p); 145 if(isAuditSegment == null) { 146 DataproviderObject segment = super.get( 147 header, 148 new DataproviderRequest( 149 new DataproviderObject(p), 150 DataproviderOperations.OBJECT_RETRIEVAL, 151 AttributeSelectors.ALL_ATTRIBUTES, 152 null 153 ) 154 ).getObject(); 155 this.auditSegments.put( 156 p, 157 isAuditSegment = new Boolean (this.isAuditee(segment)) 158 ); 159 } 160 return isAuditSegment.booleanValue(); 161 } 162 163 private DataproviderObject createResult( 165 DataproviderRequest request, 166 String structName 167 ) { 168 DataproviderObject result = new DataproviderObject( 169 request.path().getDescendant( 170 new String []{ "reply", super.uidAsString()} 171 ) 172 ); 173 result.clearValues(SystemAttributes.OBJECT_CLASS).add( 174 structName 175 ); 176 return result; 177 } 178 179 183 private Set getChangedAttributes( 184 DataproviderObject_1_0 o1, 185 DataproviderObject_1_0 o2 186 ) { 187 if(o2 == null) { 188 return new HashSet (); 189 } 190 191 for( 193 Iterator i = o1.attributeNames().iterator(); 194 i.hasNext(); 195 ) { 196 o2.values((String )i.next()); 197 } 198 199 Set changedAttributes = new HashSet (); 201 for( 202 Iterator i = o2.attributeNames().iterator(); 203 i.hasNext(); 204 ) { 205 String attributeName = (String )i.next(); 206 SparseList v1 = o1.values(attributeName); 207 SparseList v2 = o2.values(attributeName); 208 if( 209 !SystemAttributes.OBJECT_INSTANCE_OF.equals(attributeName) && 210 !"identity".equals(attributeName) 211 ) { 212 boolean isEqual = v1.size() == v2.size(); 213 if(isEqual) { 214 for(int j = 0; j < v1.size(); j++) { 215 if(v1.get(j) instanceof BigDecimal ) { 216 if(v2.get(j) instanceof BigDecimal ) { 217 isEqual = ((BigDecimal )v1.get(j)).compareTo(v2.get(j)) == 0; 218 } 219 else { 220 isEqual = false; 221 } 222 } 223 else if(v1.get(j) == null) { 224 isEqual = v2.get(j) == null; 225 } 226 else { 227 isEqual = v1.get(j).equals(v2.get(j)); 228 } 229 if(!isEqual) break; 230 } 231 } 232 if(!isEqual) { 233 changedAttributes.add(attributeName); 234 } 235 } 236 } 237 return changedAttributes; 238 } 239 240 public String getBeforeImageAsString( 242 DataproviderObject_1_0 beforeImage 243 ) { 244 String beforeImageAsString = ""; 245 for( 246 Iterator i = beforeImage.attributeNames().iterator(); 247 i.hasNext(); 248 ) { 249 String attributeName = (String )i.next(); 250 beforeImageAsString += attributeName + ":\n"; 251 int jj = 0; 252 for( 253 Iterator j = beforeImage.values(attributeName).iterator(); 254 j.hasNext(); 255 jj++ 256 ) { 257 beforeImageAsString += "" + jj + ": " + j.next() + "\n"; 258 } 259 } 260 return beforeImageAsString; 261 } 262 263 public DataproviderReply get( 265 ServiceHeader header, 266 DataproviderRequest request 267 ) throws ServiceException { 268 Path reference = request.path().getParent(); 269 if("audit".equals(reference.getBase())) { 270 DataproviderObject auditEntry = super.get( 271 header, 272 new DataproviderRequest( 273 new DataproviderObject( 274 request.path().getPrefix(5).getDescendant(new String []{"audit", request.path().getBase()}) 275 ), 276 DataproviderOperations.OBJECT_RETRIEVAL, 277 AttributeSelectors.ALL_ATTRIBUTES, 278 null 279 ) 280 ).getObject(); 281 DataproviderObject mappedAuditEntry = new DataproviderObject( 282 request.path() 283 ); 284 mappedAuditEntry.addClones( 285 auditEntry, 286 true 287 ); 288 return new DataproviderReply( 289 mappedAuditEntry 290 ); 291 } 292 else { 293 return super.get( 294 header, 295 request 296 ); 297 } 298 } 299 300 public DataproviderReply find( 302 ServiceHeader header, 303 DataproviderRequest request 304 ) throws ServiceException { 305 if( 308 (request.path().size() > 6) && 309 "audit".equals(request.path().getBase()) 310 ) { 311 List filterProperties = request.attributeFilter() == null 314 ? new ArrayList () 315 : new ArrayList (Arrays.asList(request.attributeFilter())); 316 filterProperties.add( 317 new FilterProperty( 318 Quantors.THERE_EXISTS, 319 "auditee", 320 FilterOperators.IS_IN, 321 new String []{request.path().getParent().toXri()} 322 ) 323 ); 324 DataproviderObject[] auditEntries = super.find( 325 header, 326 new DataproviderRequest( 327 new DataproviderObject( 328 request.path().getPrefix(5).getChild("audit") 329 ), 330 DataproviderOperations.ITERATION_START, 331 (FilterProperty[])filterProperties.toArray(new FilterProperty[filterProperties.size()]), 332 0, 333 Integer.MAX_VALUE, 334 Directions.ASCENDING, 335 AttributeSelectors.ALL_ATTRIBUTES, 336 request.attributeSpecifier() 337 ) 338 ).getObjects(); 339 340 List mappedAuditEntries = new ArrayList (); 343 for( 344 int i = 0; 345 i < auditEntries.length; 346 i++ 347 ) { 348 DataproviderObject mappedAuditEntry = new DataproviderObject( 349 request.path().getChild(auditEntries[i].path().getBase()) 350 ); 351 mappedAuditEntry.addClones( 352 auditEntries[i], 353 true 354 ); 355 mappedAuditEntries.add( 356 mappedAuditEntry 357 ); 358 } 359 360 DataproviderReply reply = new DataproviderReply( 362 mappedAuditEntries 363 ); 364 reply.context( 365 DataproviderReplyContexts.HAS_MORE 366 ).set(0, Boolean.FALSE); 367 reply.context( 368 DataproviderReplyContexts.TOTAL 369 ).set( 370 0, 371 new Integer (mappedAuditEntries.size()) 372 ); 373 reply.context(DataproviderReplyContexts.ATTRIBUTE_SELECTOR).set( 374 0, 375 new Short (request.attributeSelector()) 376 ); 377 return reply; 378 } 379 else { 380 return super.find( 381 header, 382 request 383 ); 384 } 385 } 386 387 public DataproviderReply replace( 389 ServiceHeader header, 390 DataproviderRequest request 391 ) throws ServiceException { 392 393 if(request.path().size() > 5) { 396 if(this.isAuditSegment(header, request.path())) { 397 398 DataproviderObject_1_0 existing = super.get( 400 header, 401 new DataproviderRequest( 402 request.object(), 403 DataproviderOperations.OBJECT_RETRIEVAL, 404 AttributeSelectors.ALL_ATTRIBUTES, 405 null 406 ) 407 ).getObject(); 408 409 if(this.isAuditee(existing)) { 411 DataproviderObject auditEntry = new DataproviderObject( 412 request.path().getPrefix(5).getDescendant(new String []{"audit", super.uidAsString()}) 413 ); 414 auditEntry.values(SystemAttributes.OBJECT_CLASS).add( 415 "org:opencrx:kernel:base:ObjectModificationAuditEntry" 416 ); 417 auditEntry.values("auditee").add( 418 request.path().toXri() 419 ); 420 if(this.unitOfWorkId != null) { 421 auditEntry.values("unitOfWork").add( 422 this.unitOfWorkId 423 ); 424 } 425 DataproviderObject beforeImage = new DataproviderObject(existing); 430 beforeImage.attributeNames().retainAll( 431 request.object().attributeNames() 432 ); 433 Set modifiedFeatures = this.getChangedAttributes( 434 beforeImage, 435 request.object() 436 ); 437 if( 440 (modifiedFeatures.size() > 1) || 441 !modifiedFeatures.contains(SystemAttributes.MODIFIED_AT) 442 ) { 443 beforeImage.attributeNames().retainAll( 444 modifiedFeatures 445 ); 446 auditEntry.values("beforeImage").add( 447 this.getBeforeImageAsString(beforeImage) 448 ); 449 String modifiedFeaturesAsString = modifiedFeatures.toString(); 450 auditEntry.values("modifiedFeatures").add( 451 modifiedFeaturesAsString.length() > 300 452 ? modifiedFeaturesAsString.substring(0, 280) + "..." 453 : modifiedFeaturesAsString 454 ); 455 auditEntry.values(SystemAttributes.MODIFIED_AT).addAll( 456 request.object().values(SystemAttributes.MODIFIED_AT) 457 ); 458 auditEntry.values(SystemAttributes.MODIFIED_BY).addAll( 459 request.object().values(SystemAttributes.MODIFIED_BY) 460 ); 461 auditEntry.values(SystemAttributes.CREATED_AT).addAll( 462 request.object().values(SystemAttributes.MODIFIED_AT) 463 ); 464 auditEntry.values(SystemAttributes.CREATED_BY).addAll( 465 request.object().values(SystemAttributes.MODIFIED_BY) 466 ); 467 auditEntry.values("owner").addAll( 468 existing.values("owner") 469 ); 470 auditEntry.values("accessLevelBrowse").addAll( 471 existing.values("accessLevelBrowse") 472 ); 473 auditEntry.values("accessLevelUpdate").add( 474 new Short (SecurityKeys.ACCESS_LEVEL_NA) 475 ); 476 auditEntry.values("accessLevelDelete").add( 477 new Short (SecurityKeys.ACCESS_LEVEL_NA) 478 ); 479 480 try { 482 super.create( 483 header, 484 new DataproviderRequest( 485 auditEntry, 486 DataproviderOperations.OBJECT_CREATION, 487 AttributeSelectors.NO_ATTRIBUTES, 488 null 489 ) 490 ); 491 } 492 catch(ServiceException e) { 493 e.log(); 494 } 495 } 496 } 497 } 498 499 return super.replace( 501 header, 502 request 503 ); 504 } 505 else { 506 return new DataproviderReply( 507 request.object() 508 ); 509 } 510 } 511 512 public DataproviderReply create( 514 ServiceHeader header, 515 DataproviderRequest request 516 ) throws ServiceException { 517 518 if( 521 (request.path().size() > 5) && 522 this.isAuditSegment(header, request.path()) && 523 this.isAuditee(request.object()) 524 ) { 525 526 DataproviderObject auditEntry = new DataproviderObject( 528 request.path().getPrefix(5).getDescendant(new String []{"audit", super.uidAsString()}) 529 ); 530 auditEntry.values(SystemAttributes.OBJECT_CLASS).add( 531 "org:opencrx:kernel:base:ObjectCreationAuditEntry" 532 ); 533 auditEntry.values("auditee").add( 534 request.path().toXri() 535 ); 536 if(this.unitOfWorkId != null) { 537 auditEntry.values("unitOfWork").add( 538 this.unitOfWorkId 539 ); 540 } 541 auditEntry.values(SystemAttributes.MODIFIED_AT).addAll( 542 request.object().values(SystemAttributes.MODIFIED_AT) 543 ); 544 auditEntry.values(SystemAttributes.MODIFIED_BY).addAll( 545 request.object().values(SystemAttributes.MODIFIED_BY) 546 ); 547 auditEntry.values(SystemAttributes.CREATED_AT).addAll( 548 request.object().values(SystemAttributes.MODIFIED_AT) 549 ); 550 auditEntry.values(SystemAttributes.CREATED_BY).addAll( 551 request.object().values(SystemAttributes.MODIFIED_BY) 552 ); 553 auditEntry.values("owner").addAll( 554 request.object().values("owner") 555 ); 556 auditEntry.values("accessLevelBrowse").addAll( 557 request.object().values("accessLevelBrowse") 558 ); 559 auditEntry.values("accessLevelUpdate").add( 560 new Short (SecurityKeys.ACCESS_LEVEL_NA) 561 ); 562 auditEntry.values("accessLevelDelete").add( 563 new Short (SecurityKeys.ACCESS_LEVEL_NA) 564 ); 565 566 try { 568 super.create( 569 header, 570 new DataproviderRequest( 571 auditEntry, 572 DataproviderOperations.OBJECT_CREATION, 573 AttributeSelectors.NO_ATTRIBUTES, 574 null 575 ) 576 ); 577 } 578 catch(ServiceException e) { 579 e.log(); 580 } 581 } 582 583 return super.create( 585 header, 586 request 587 ); 588 } 589 590 public DataproviderReply remove( 592 ServiceHeader header, 593 DataproviderRequest request 594 ) throws ServiceException { 595 596 if( 599 (request.path().size() > 5) && 600 this.isAuditSegment(header, request.path()) 601 ) { 602 603 DataproviderObject_1_0 existing = super.get( 605 header, 606 new DataproviderRequest( 607 request.object(), 608 DataproviderOperations.OBJECT_RETRIEVAL, 609 AttributeSelectors.ALL_ATTRIBUTES, 610 null 611 ) 612 ).getObject(); 613 614 if(this.isAuditee(existing)) { 616 DataproviderObject auditEntry = new DataproviderObject( 617 request.path().getPrefix(5).getDescendant(new String []{"audit", super.uidAsString()}) 618 ); 619 auditEntry.values(SystemAttributes.OBJECT_CLASS).add( 620 "org:opencrx:kernel:base:ObjectRemovalAuditEntry" 621 ); 622 auditEntry.values("auditee").add( 623 request.path().toXri() 624 ); 625 if(this.unitOfWorkId != null) { 626 auditEntry.values("unitOfWork").add( 627 this.unitOfWorkId 628 ); 629 } 630 auditEntry.values("beforeImage").add( 631 this.getBeforeImageAsString(existing) 632 ); 633 auditEntry.values(SystemAttributes.MODIFIED_AT).addAll( 634 request.object().values(SystemAttributes.MODIFIED_AT) 635 ); 636 auditEntry.values(SystemAttributes.MODIFIED_BY).addAll( 637 request.object().values(SystemAttributes.MODIFIED_BY) 638 ); 639 auditEntry.values(SystemAttributes.CREATED_AT).addAll( 640 request.object().values(SystemAttributes.MODIFIED_AT) 641 ); 642 auditEntry.values(SystemAttributes.CREATED_BY).addAll( 643 request.object().values(SystemAttributes.MODIFIED_BY) 644 ); 645 auditEntry.values("owner").addAll( 646 existing.values("owner") 647 ); 648 auditEntry.values("accessLevelBrowse").addAll( 649 existing.values("accessLevelBrowse") 650 ); 651 auditEntry.values("accessLevelUpdate").add( 652 new Short (SecurityKeys.ACCESS_LEVEL_NA) 653 ); 654 auditEntry.values("accessLevelDelete").add( 655 new Short (SecurityKeys.ACCESS_LEVEL_NA) 656 ); 657 658 try { 660 super.create( 661 header, 662 new DataproviderRequest( 663 auditEntry, 664 DataproviderOperations.OBJECT_CREATION, 665 AttributeSelectors.NO_ATTRIBUTES, 666 null 667 ) 668 ); 669 } 670 catch(ServiceException e) { 671 e.log(); 672 } 673 } 674 } 675 676 return super.remove( 678 header, 679 request 680 ); 681 682 } 683 684 protected DataproviderObject otherOperation( 686 ServiceHeader header, 687 DataproviderRequest request, 688 String operation, 689 Path replyPath 690 ) throws ServiceException { 691 if("testAndSetVisitedBy".equals(operation)) { 692 Path auditEntryIdentity = request.path().getPrefix(request.path().size() - 2); 693 DataproviderObject auditEntry = super.get( 694 header, 695 new DataproviderRequest( 696 new DataproviderObject(auditEntryIdentity), 697 DataproviderOperations.OBJECT_RETRIEVAL, 698 AttributeSelectors.ALL_ATTRIBUTES, 699 null 700 ) 701 ).getObject(); 702 String visitorId = (String )request.object().values("visitorId").get(0); 703 DataproviderObject reply = this.createResult( 704 request, 705 "org:opencrx:kernel:base:TestAndSetVisitedByResult" 706 ); 707 if(visitorId == null) { 708 reply.values("visitStatus").add( 709 new Short ((short)2) 710 ); 711 } 712 else if(auditEntry.values("visitedBy").contains(visitorId)) { 713 reply.values("visitStatus").add( 714 new Short ((short)1) 715 ); 716 } 717 else { 718 auditEntry.values("visitedBy").add( 719 visitorId 720 ); 721 String visitedAt = auditEntry.values("visitedAt").size() > 0 722 ? (String )auditEntry.values("visitedAt").get(0) + "\n" 723 : ""; 724 visitedAt += visitorId + ": " + org.openmdx.base.text.format.DateFormat.getInstance().format(new Date ()); 725 auditEntry.values("visitedAt").add( 726 visitedAt 727 ); 728 super.replace( 729 header, 730 new DataproviderRequest( 731 auditEntry, 732 DataproviderOperations.OBJECT_REPLACEMENT, 733 AttributeSelectors.NO_ATTRIBUTES, 734 null 735 ) 736 ); 737 reply.values("visitStatus").add( 738 new Short ((short)0) 739 ); 740 } 741 return reply; 742 } 743 else { 745 return super.otherOperation( 746 header, 747 request, 748 operation, 749 replyPath 750 ); 751 } 752 } 753 754 private String unitOfWorkId = null; 756 private final Map auditSegments = new HashMap (); 757 758 } 759 760 | Popular Tags |