001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2009-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.server.core; 028 029import java.util.*; 030import java.util.concurrent.CopyOnWriteArrayList; 031import java.util.concurrent.locks.ReadWriteLock; 032import java.util.concurrent.locks.ReentrantReadWriteLock; 033 034import org.forgerock.i18n.slf4j.LocalizedLogger; 035import org.forgerock.opendj.ldap.ResultCode; 036import org.forgerock.opendj.ldap.SearchScope; 037import org.opends.server.api.Backend; 038import org.opends.server.api.BackendInitializationListener; 039import org.opends.server.api.ClientConnection; 040import org.opends.server.api.DITCacheMap; 041import org.opends.server.api.SubentryChangeListener; 042import org.opends.server.api.plugin.InternalDirectoryServerPlugin; 043import org.opends.server.api.plugin.PluginResult; 044import org.opends.server.api.plugin.PluginResult.PostOperation; 045import org.opends.server.api.plugin.PluginResult.PreOperation; 046import org.opends.server.api.plugin.PluginType; 047import org.opends.server.controls.SubentriesControl; 048import org.opends.server.protocols.internal.InternalClientConnection; 049import org.opends.server.protocols.internal.InternalSearchOperation; 050import org.opends.server.protocols.internal.SearchRequest; 051import org.opends.server.types.*; 052import org.opends.server.types.operation.PostOperationAddOperation; 053import org.opends.server.types.operation.PostOperationDeleteOperation; 054import org.opends.server.types.operation.PostOperationModifyDNOperation; 055import org.opends.server.types.operation.PostOperationModifyOperation; 056import org.opends.server.types.operation.PostSynchronizationAddOperation; 057import org.opends.server.types.operation.PostSynchronizationDeleteOperation; 058import org.opends.server.types.operation.PostSynchronizationModifyDNOperation; 059import org.opends.server.types.operation.PostSynchronizationModifyOperation; 060import org.opends.server.types.operation.PreOperationAddOperation; 061import org.opends.server.types.operation.PreOperationDeleteOperation; 062import org.opends.server.types.operation.PreOperationModifyDNOperation; 063import org.opends.server.types.operation.PreOperationModifyOperation; 064import org.opends.server.workflowelement.localbackend.LocalBackendSearchOperation; 065 066import static org.opends.messages.CoreMessages.*; 067import static org.opends.server.config.ConfigConstants.*; 068import static org.opends.server.protocols.internal.InternalClientConnection.*; 069import static org.opends.server.protocols.internal.Requests.*; 070import static org.opends.server.util.CollectionUtils.*; 071import static org.opends.server.util.ServerConstants.*; 072 073/** 074 * This class provides a mechanism for interacting with subentries defined in 075 * the Directory Server. It will handle all necessary processing at server 076 * startup to identify and load subentries within the server. 077 * <BR><BR> 078 * FIXME: At the present time, it assumes that all of the necessary 079 * information about subentries defined in the server can be held in 080 * memory. If it is determined that this approach is not workable 081 * in all cases, then we will need an alternate strategy. 082 */ 083public class SubentryManager extends InternalDirectoryServerPlugin 084 implements BackendInitializationListener 085{ 086 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 087 088 /** A mapping between the DNs and applicable subentries. */ 089 private Map<DN,List<SubEntry>> dn2SubEntry; 090 091 /** A mapping between the DNs and applicable collective subentries. */ 092 private Map<DN,List<SubEntry>> dn2CollectiveSubEntry; 093 094 /** A mapping between subentry DNs and subentry objects. */ 095 private DITCacheMap<SubEntry> dit2SubEntry; 096 097 /** Internal search all operational attributes. */ 098 private Set<String> requestAttrs; 099 100 /** Lock to protect internal data structures. */ 101 private final ReadWriteLock lock; 102 103 /** The set of change notification listeners. */ 104 private List<SubentryChangeListener> changeListeners; 105 106 /** Dummy configuration DN for Subentry Manager. */ 107 private static final String CONFIG_DN = "cn=Subentry Manager,cn=config"; 108 109 /** 110 * Creates a new instance of this subentry manager. 111 * 112 * @throws DirectoryException If a problem occurs while 113 * creating an instance of 114 * the subentry manager. 115 */ 116 public SubentryManager() throws DirectoryException 117 { 118 super(DN.valueOf(CONFIG_DN), EnumSet.of( 119 PluginType.PRE_OPERATION_ADD, 120 PluginType.PRE_OPERATION_DELETE, 121 PluginType.PRE_OPERATION_MODIFY, 122 PluginType.PRE_OPERATION_MODIFY_DN, 123 PluginType.POST_OPERATION_ADD, 124 PluginType.POST_OPERATION_DELETE, 125 PluginType.POST_OPERATION_MODIFY, 126 PluginType.POST_OPERATION_MODIFY_DN, 127 PluginType.POST_SYNCHRONIZATION_ADD, 128 PluginType.POST_SYNCHRONIZATION_DELETE, 129 PluginType.POST_SYNCHRONIZATION_MODIFY, 130 PluginType.POST_SYNCHRONIZATION_MODIFY_DN), 131 true); 132 133 lock = new ReentrantReadWriteLock(); 134 135 dn2SubEntry = new HashMap<>(); 136 dn2CollectiveSubEntry = new HashMap<>(); 137 dit2SubEntry = new DITCacheMap<>(); 138 changeListeners = new CopyOnWriteArrayList<>(); 139 requestAttrs = newLinkedHashSet("*", "+"); 140 141 DirectoryServer.registerInternalPlugin(this); 142 DirectoryServer.registerBackendInitializationListener(this); 143 } 144 145 /** 146 * Perform any required finalization tasks for Subentry Manager. 147 * This should only be called at Directory Server shutdown. 148 */ 149 public void finalizeSubentryManager() 150 { 151 // Deregister as internal plugin and 152 // backend initialization listener. 153 DirectoryServer.deregisterInternalPlugin(this); 154 DirectoryServer.deregisterBackendInitializationListener(this); 155 } 156 157 /** 158 * Registers the provided change notification listener with this manager 159 * so that it will be notified of any add, delete, modify, or modify DN 160 * operations that are performed. 161 * 162 * @param changeListener The change notification listener to register 163 * with this manager. 164 */ 165 public void registerChangeListener( 166 SubentryChangeListener changeListener) 167 { 168 changeListeners.add(changeListener); 169 } 170 171 /** 172 * Deregisters the provided change notification listener with this manager 173 * so that it will no longer be notified of any add, delete, modify, or 174 * modify DN operations that are performed. 175 * 176 * @param changeListener The change notification listener to deregister 177 * with this manager. 178 */ 179 public void deregisterChangeListener( 180 SubentryChangeListener changeListener) 181 { 182 changeListeners.remove(changeListener); 183 } 184 185 /** 186 * Add a given entry to this subentry manager. 187 * @param entry to add. 188 */ 189 private void addSubentry(Entry entry) throws DirectoryException 190 { 191 SubEntry subEntry = new SubEntry(entry); 192 SubtreeSpecification subSpec = 193 subEntry.getSubTreeSpecification(); 194 DN subDN = subSpec.getBaseDN(); 195 List<SubEntry> subList = null; 196 lock.writeLock().lock(); 197 try 198 { 199 if (subEntry.isCollective() || subEntry.isInheritedCollective()) 200 { 201 subList = dn2CollectiveSubEntry.get(subDN); 202 } 203 else 204 { 205 subList = dn2SubEntry.get(subDN); 206 } 207 if (subList == null) 208 { 209 subList = new ArrayList<>(); 210 if (subEntry.isCollective() || subEntry.isInheritedCollective()) 211 { 212 dn2CollectiveSubEntry.put(subDN, subList); 213 } 214 else 215 { 216 dn2SubEntry.put(subDN, subList); 217 } 218 } 219 dit2SubEntry.put(entry.getName(), subEntry); 220 subList.add(subEntry); 221 } 222 finally 223 { 224 lock.writeLock().unlock(); 225 } 226 } 227 228 /** 229 * Remove a given entry from this subentry manager. 230 * @param entry to remove. 231 */ 232 private void removeSubentry(Entry entry) 233 { 234 lock.writeLock().lock(); 235 try 236 { 237 boolean removed = false; 238 Iterator<Map.Entry<DN, List<SubEntry>>> setIterator = 239 dn2SubEntry.entrySet().iterator(); 240 while (setIterator.hasNext()) 241 { 242 Map.Entry<DN, List<SubEntry>> mapEntry = setIterator.next(); 243 List<SubEntry> subList = mapEntry.getValue(); 244 Iterator<SubEntry> listIterator = subList.iterator(); 245 while (listIterator.hasNext()) 246 { 247 SubEntry subEntry = listIterator.next(); 248 if (subEntry.getDN().equals(entry.getName())) 249 { 250 dit2SubEntry.remove(entry.getName()); 251 listIterator.remove(); 252 removed = true; 253 break; 254 } 255 } 256 if (subList.isEmpty()) 257 { 258 setIterator.remove(); 259 } 260 if (removed) 261 { 262 return; 263 } 264 } 265 setIterator = dn2CollectiveSubEntry.entrySet().iterator(); 266 while (setIterator.hasNext()) 267 { 268 Map.Entry<DN, List<SubEntry>> mapEntry = setIterator.next(); 269 List<SubEntry> subList = mapEntry.getValue(); 270 Iterator<SubEntry> listIterator = subList.iterator(); 271 while (listIterator.hasNext()) 272 { 273 SubEntry subEntry = listIterator.next(); 274 if (subEntry.getDN().equals(entry.getName())) 275 { 276 dit2SubEntry.remove(entry.getName()); 277 listIterator.remove(); 278 removed = true; 279 break; 280 } 281 } 282 if (subList.isEmpty()) 283 { 284 setIterator.remove(); 285 } 286 if (removed) 287 { 288 return; 289 } 290 } 291 } 292 finally 293 { 294 lock.writeLock().unlock(); 295 } 296 } 297 298 /** 299 * {@inheritDoc} In this case, the server will search the backend to find 300 * all subentries that it may contain and register them with this manager. 301 */ 302 @Override 303 public void performBackendPreInitializationProcessing(Backend<?> backend) 304 { 305 InternalClientConnection conn = getRootConnection(); 306 SubentriesControl control = new SubentriesControl(true, true); 307 308 SearchFilter filter = null; 309 try 310 { 311 filter = SearchFilter.createFilterFromString("(|" + 312 "(" + ATTR_OBJECTCLASS + "=" + OC_SUBENTRY + ")" + 313 "(" + ATTR_OBJECTCLASS + "=" + OC_LDAP_SUBENTRY + ")" + 314 ")"); 315 if (backend.getEntryCount() > 0 && ! backend.isIndexed(filter)) 316 { 317 logger.warn(WARN_SUBENTRY_FILTER_NOT_INDEXED, filter, backend.getBackendID()); 318 } 319 } 320 catch (Exception e) 321 { 322 logger.traceException(e); 323 } 324 325 for (DN baseDN : backend.getBaseDNs()) 326 { 327 try 328 { 329 if (! backend.entryExists(baseDN)) 330 { 331 continue; 332 } 333 } 334 catch (Exception e) 335 { 336 logger.traceException(e); 337 338 // FIXME -- Is there anything that we need to do here? 339 continue; 340 } 341 342 SearchRequest request = newSearchRequest(baseDN, SearchScope.WHOLE_SUBTREE, filter) 343 .addAttribute(requestAttrs) 344 .addControl(control); 345 InternalSearchOperation internalSearch = 346 new InternalSearchOperation(conn, nextOperationID(), nextMessageID(), request); 347 LocalBackendSearchOperation localSearch = new LocalBackendSearchOperation(internalSearch); 348 349 try 350 { 351 backend.search(localSearch); 352 } 353 catch (Exception e) 354 { 355 logger.traceException(e); 356 357 // FIXME -- Is there anything that we need to do here? 358 continue; 359 } 360 361 for (SearchResultEntry entry : internalSearch.getSearchEntries()) 362 { 363 if (entry.isSubentry() || entry.isLDAPSubentry()) 364 { 365 try 366 { 367 addSubentry(entry); 368 369 // Notify change listeners. 370 for (SubentryChangeListener changeListener : 371 changeListeners) 372 { 373 try 374 { 375 changeListener.handleSubentryAdd(entry); 376 } 377 catch (Exception e) 378 { 379 logger.traceException(e); 380 } 381 } 382 } 383 catch (Exception e) 384 { 385 logger.traceException(e); 386 387 // FIXME -- Handle this. 388 continue; 389 } 390 } 391 } 392 } 393 } 394 395 /** 396 * Return all subentries for this manager. 397 * Note that this getter will skip any collective subentries, 398 * returning only applicable regular subentries. 399 * @return all subentries for this manager. 400 */ 401 public List<SubEntry> getSubentries() 402 { 403 if (dn2SubEntry.isEmpty()) 404 { 405 return Collections.emptyList(); 406 } 407 408 List<SubEntry> subentries = new ArrayList<>(); 409 410 lock.readLock().lock(); 411 try 412 { 413 for (List<SubEntry> subList : dn2SubEntry.values()) 414 { 415 subentries.addAll(subList); 416 } 417 } 418 finally 419 { 420 lock.readLock().unlock(); 421 } 422 423 return subentries; 424 } 425 426 /** 427 * Return subentries applicable to specific DN. 428 * Note that this getter will skip any collective subentries, 429 * returning only applicable regular subentries. 430 * @param dn for which to retrieve applicable 431 * subentries. 432 * @return applicable subentries. 433 */ 434 public List<SubEntry> getSubentries(DN dn) 435 { 436 if (dn2SubEntry.isEmpty()) 437 { 438 return Collections.emptyList(); 439 } 440 441 List<SubEntry> subentries = new ArrayList<>(); 442 lock.readLock().lock(); 443 try 444 { 445 for (DN subDN = dn; subDN != null; 446 subDN = subDN.parent()) 447 { 448 List<SubEntry> subList = dn2SubEntry.get(subDN); 449 if (subList != null) 450 { 451 for (SubEntry subEntry : subList) 452 { 453 SubtreeSpecification subSpec = 454 subEntry.getSubTreeSpecification(); 455 if (subSpec.isDNWithinScope(dn)) 456 { 457 subentries.add(subEntry); 458 } 459 } 460 } 461 } 462 } 463 finally 464 { 465 lock.readLock().unlock(); 466 } 467 468 return subentries; 469 } 470 471 /** 472 * Return subentries applicable to specific entry. 473 * Note that this getter will skip any collective subentries, 474 * returning only applicable regular subentries. 475 * @param entry for which to retrieve applicable 476 * subentries. 477 * @return applicable subentries. 478 */ 479 public List<SubEntry> getSubentries(Entry entry) 480 { 481 if (dn2SubEntry.isEmpty()) 482 { 483 return Collections.emptyList(); 484 } 485 486 List<SubEntry> subentries = new ArrayList<>(); 487 488 lock.readLock().lock(); 489 try 490 { 491 for (DN subDN = entry.getName(); subDN != null; 492 subDN = subDN.parent()) 493 { 494 List<SubEntry> subList = dn2SubEntry.get(subDN); 495 if (subList != null) 496 { 497 for (SubEntry subEntry : subList) 498 { 499 SubtreeSpecification subSpec = 500 subEntry.getSubTreeSpecification(); 501 if (subSpec.isWithinScope(entry)) 502 { 503 subentries.add(subEntry); 504 } 505 } 506 } 507 } 508 } 509 finally 510 { 511 lock.readLock().unlock(); 512 } 513 514 return subentries; 515 } 516 517 /** 518 * Return collective subentries applicable to specific DN. 519 * Note that this getter will skip any regular subentries, 520 * returning only applicable collective subentries. 521 * @param dn for which to retrieve applicable 522 * subentries. 523 * @return applicable subentries. 524 */ 525 public List<SubEntry> getCollectiveSubentries(DN dn) 526 { 527 if (dn2CollectiveSubEntry.isEmpty()) 528 { 529 return Collections.emptyList(); 530 } 531 532 List<SubEntry> subentries = new ArrayList<>(); 533 534 lock.readLock().lock(); 535 try 536 { 537 for (DN subDN = dn; subDN != null; 538 subDN = subDN.parent()) 539 { 540 List<SubEntry> subList = dn2CollectiveSubEntry.get(subDN); 541 if (subList != null) 542 { 543 for (SubEntry subEntry : subList) 544 { 545 SubtreeSpecification subSpec = 546 subEntry.getSubTreeSpecification(); 547 if (subSpec.isDNWithinScope(dn)) 548 { 549 subentries.add(subEntry); 550 } 551 } 552 } 553 } 554 } 555 finally 556 { 557 lock.readLock().unlock(); 558 } 559 560 return subentries; 561 } 562 563 /** 564 * Return collective subentries applicable to specific entry. 565 * Note that this getter will skip any regular subentries, 566 * returning only applicable collective subentries. 567 * @param entry for which to retrieve applicable 568 * subentries. 569 * @return applicable subentries. 570 */ 571 public List<SubEntry> getCollectiveSubentries(Entry entry) 572 { 573 if (dn2CollectiveSubEntry.isEmpty()) 574 { 575 return Collections.emptyList(); 576 } 577 578 List<SubEntry> subentries = new ArrayList<>(); 579 580 lock.readLock().lock(); 581 try 582 { 583 for (DN subDN = entry.getName(); subDN != null; 584 subDN = subDN.parent()) 585 { 586 List<SubEntry> subList = dn2CollectiveSubEntry.get(subDN); 587 if (subList != null) 588 { 589 for (SubEntry subEntry : subList) 590 { 591 SubtreeSpecification subSpec = 592 subEntry.getSubTreeSpecification(); 593 if (subSpec.isWithinScope(entry)) 594 { 595 subentries.add(subEntry); 596 } 597 } 598 } 599 } 600 } 601 finally 602 { 603 lock.readLock().unlock(); 604 } 605 606 return subentries; 607 } 608 609 /** 610 * {@inheritDoc} In this case, the server will de-register 611 * all subentries associated with the provided backend. 612 */ 613 @Override 614 public void performBackendPostFinalizationProcessing(Backend<?> backend) 615 { 616 lock.writeLock().lock(); 617 try 618 { 619 Iterator<Map.Entry<DN, List<SubEntry>>> setIterator = 620 dn2SubEntry.entrySet().iterator(); 621 while (setIterator.hasNext()) 622 { 623 Map.Entry<DN, List<SubEntry>> mapEntry = setIterator.next(); 624 List<SubEntry> subList = mapEntry.getValue(); 625 Iterator<SubEntry> listIterator = subList.iterator(); 626 while (listIterator.hasNext()) 627 { 628 SubEntry subEntry = listIterator.next(); 629 if (backend.handlesEntry(subEntry.getDN())) 630 { 631 dit2SubEntry.remove(subEntry.getDN()); 632 listIterator.remove(); 633 634 // Notify change listeners. 635 for (SubentryChangeListener changeListener : 636 changeListeners) 637 { 638 try 639 { 640 changeListener.handleSubentryDelete( 641 subEntry.getEntry()); 642 } 643 catch (Exception e) 644 { 645 logger.traceException(e); 646 } 647 } 648 } 649 } 650 if (subList.isEmpty()) 651 { 652 setIterator.remove(); 653 } 654 } 655 setIterator = dn2CollectiveSubEntry.entrySet().iterator(); 656 while (setIterator.hasNext()) 657 { 658 Map.Entry<DN, List<SubEntry>> mapEntry = setIterator.next(); 659 List<SubEntry> subList = mapEntry.getValue(); 660 Iterator<SubEntry> listIterator = subList.iterator(); 661 while (listIterator.hasNext()) 662 { 663 SubEntry subEntry = listIterator.next(); 664 if (backend.handlesEntry(subEntry.getDN())) 665 { 666 dit2SubEntry.remove(subEntry.getDN()); 667 listIterator.remove(); 668 669 // Notify change listeners. 670 for (SubentryChangeListener changeListener : 671 changeListeners) 672 { 673 try 674 { 675 changeListener.handleSubentryDelete( 676 subEntry.getEntry()); 677 } 678 catch (Exception e) 679 { 680 logger.traceException(e); 681 } 682 } 683 } 684 } 685 if (subList.isEmpty()) 686 { 687 setIterator.remove(); 688 } 689 } 690 } 691 finally 692 { 693 lock.writeLock().unlock(); 694 } 695 } 696 697 @Override 698 public void performBackendPostInitializationProcessing(Backend<?> backend) { 699 // Nothing to do. 700 } 701 702 @Override 703 public void performBackendPreFinalizationProcessing(Backend<?> backend) { 704 // Nothing to do. 705 } 706 707 private void doPostAdd(Entry entry) 708 { 709 if (entry.isSubentry() || entry.isLDAPSubentry()) 710 { 711 lock.writeLock().lock(); 712 try 713 { 714 try 715 { 716 addSubentry(entry); 717 718 // Notify change listeners. 719 for (SubentryChangeListener changeListener : 720 changeListeners) 721 { 722 try 723 { 724 changeListener.handleSubentryAdd(entry); 725 } 726 catch (Exception e) 727 { 728 logger.traceException(e); 729 } 730 } 731 } 732 catch (Exception e) 733 { 734 logger.traceException(e); 735 736 // FIXME -- Handle this. 737 } 738 } 739 finally 740 { 741 lock.writeLock().unlock(); 742 } 743 } 744 } 745 746 private void doPostDelete(Entry entry) 747 { 748 lock.writeLock().lock(); 749 try 750 { 751 for (SubEntry subEntry : dit2SubEntry.getSubtree(entry.getName())) 752 { 753 removeSubentry(subEntry.getEntry()); 754 755 // Notify change listeners. 756 for (SubentryChangeListener changeListener : 757 changeListeners) 758 { 759 try 760 { 761 changeListener.handleSubentryDelete(subEntry.getEntry()); 762 } 763 catch (Exception e) 764 { 765 logger.traceException(e); 766 } 767 } 768 } 769 } 770 finally 771 { 772 lock.writeLock().unlock(); 773 } 774 } 775 776 private void doPostModify(Entry oldEntry, Entry newEntry) 777 { 778 boolean notify = false; 779 780 lock.writeLock().lock(); 781 try 782 { 783 if (oldEntry.isSubentry() || oldEntry.isLDAPSubentry()) 784 { 785 removeSubentry(oldEntry); 786 notify = true; 787 } 788 if (newEntry.isSubentry() || newEntry.isLDAPSubentry()) 789 { 790 try 791 { 792 addSubentry(newEntry); 793 notify = true; 794 } 795 catch (Exception e) 796 { 797 logger.traceException(e); 798 799 // FIXME -- Handle this. 800 } 801 } 802 803 if (notify) 804 { 805 // Notify change listeners. 806 for (SubentryChangeListener changeListener : 807 changeListeners) 808 { 809 try 810 { 811 changeListener.handleSubentryModify( 812 oldEntry, newEntry); 813 } 814 catch (Exception e) 815 { 816 logger.traceException(e); 817 } 818 } 819 } 820 } 821 finally 822 { 823 lock.writeLock().unlock(); 824 } 825 } 826 827 private void doPostModifyDN(final Entry oldEntry, final Entry newEntry) 828 { 829 lock.writeLock().lock(); 830 try 831 { 832 Collection<SubEntry> setToDelete = dit2SubEntry.getSubtree(oldEntry.getName()); 833 for (SubEntry subentry : setToDelete) 834 { 835 final Entry currentSubentry = subentry.getEntry(); 836 removeSubentry(currentSubentry); 837 838 Entry renamedSubentry = null; 839 try 840 { 841 renamedSubentry = currentSubentry.duplicate(false); 842 final DN renamedDN = currentSubentry.getName().rename(oldEntry.getName(), newEntry.getName()); 843 renamedSubentry.setDN(renamedDN); 844 addSubentry(renamedSubentry); 845 } 846 catch (Exception e) 847 { 848 // Shouldnt happen. 849 logger.traceException(e); 850 } 851 852 // Notify change listeners. 853 for (SubentryChangeListener changeListener : 854 changeListeners) 855 { 856 try 857 { 858 changeListener.handleSubentryModify(currentSubentry, renamedSubentry); 859 } 860 catch (Exception e) 861 { 862 logger.traceException(e); 863 } 864 } 865 } 866 } 867 finally 868 { 869 lock.writeLock().unlock(); 870 } 871 } 872 873 /** {@inheritDoc} */ 874 @Override 875 public PreOperation doPreOperation( 876 PreOperationAddOperation addOperation) 877 { 878 Entry entry = addOperation.getEntryToAdd(); 879 880 if (entry.isSubentry() || entry.isLDAPSubentry()) 881 { 882 ClientConnection conn = addOperation.getClientConnection(); 883 if (!conn.hasPrivilege(Privilege.SUBENTRY_WRITE, 884 conn.getOperationInProgress( 885 addOperation.getMessageID()))) 886 { 887 return PluginResult.PreOperation.stopProcessing( 888 ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 889 ERR_SUBENTRY_WRITE_INSUFFICIENT_PRIVILEGES.get()); 890 } 891 for (SubentryChangeListener changeListener : 892 changeListeners) 893 { 894 try 895 { 896 changeListener.checkSubentryAddAcceptable(entry); 897 } 898 catch (DirectoryException de) 899 { 900 logger.traceException(de); 901 902 return PluginResult.PreOperation.stopProcessing( 903 de.getResultCode(), de.getMessageObject()); 904 } 905 } 906 } 907 908 return PluginResult.PreOperation.continueOperationProcessing(); 909 } 910 911 /** {@inheritDoc} */ 912 @Override 913 public PreOperation doPreOperation( 914 PreOperationDeleteOperation deleteOperation) 915 { 916 Entry entry = deleteOperation.getEntryToDelete(); 917 boolean hasSubentryWritePrivilege = false; 918 919 lock.readLock().lock(); 920 try 921 { 922 for (SubEntry subEntry : dit2SubEntry.getSubtree(entry.getName())) 923 { 924 if (!hasSubentryWritePrivilege) 925 { 926 ClientConnection conn = deleteOperation.getClientConnection(); 927 if (!conn.hasPrivilege(Privilege.SUBENTRY_WRITE, 928 conn.getOperationInProgress( 929 deleteOperation.getMessageID()))) 930 { 931 return PluginResult.PreOperation.stopProcessing( 932 ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 933 ERR_SUBENTRY_WRITE_INSUFFICIENT_PRIVILEGES.get()); 934 } 935 hasSubentryWritePrivilege = true; 936 } 937 for (SubentryChangeListener changeListener : 938 changeListeners) 939 { 940 try 941 { 942 changeListener.checkSubentryDeleteAcceptable( 943 subEntry.getEntry()); 944 } 945 catch (DirectoryException de) 946 { 947 logger.traceException(de); 948 949 return PluginResult.PreOperation.stopProcessing( 950 de.getResultCode(), de.getMessageObject()); 951 } 952 } 953 } 954 } 955 finally 956 { 957 lock.readLock().unlock(); 958 } 959 960 return PluginResult.PreOperation.continueOperationProcessing(); 961 } 962 963 /** {@inheritDoc} */ 964 @Override 965 public PreOperation doPreOperation( 966 PreOperationModifyOperation modifyOperation) 967 { 968 Entry oldEntry = modifyOperation.getCurrentEntry(); 969 Entry newEntry = modifyOperation.getModifiedEntry(); 970 971 if (newEntry.isSubentry() || newEntry.isLDAPSubentry() || 972 oldEntry.isSubentry() || oldEntry.isLDAPSubentry()) 973 { 974 ClientConnection conn = modifyOperation.getClientConnection(); 975 if (!conn.hasPrivilege(Privilege.SUBENTRY_WRITE, 976 conn.getOperationInProgress( 977 modifyOperation.getMessageID()))) 978 { 979 return PluginResult.PreOperation.stopProcessing( 980 ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 981 ERR_SUBENTRY_WRITE_INSUFFICIENT_PRIVILEGES.get()); 982 } 983 for (SubentryChangeListener changeListener : 984 changeListeners) 985 { 986 try 987 { 988 changeListener.checkSubentryModifyAcceptable( 989 oldEntry, newEntry); 990 } 991 catch (DirectoryException de) 992 { 993 logger.traceException(de); 994 995 return PluginResult.PreOperation.stopProcessing( 996 de.getResultCode(), de.getMessageObject()); 997 } 998 } 999 } 1000 1001 return PluginResult.PreOperation.continueOperationProcessing(); 1002 } 1003 1004 /** {@inheritDoc} */ 1005 @Override 1006 public PreOperation doPreOperation(PreOperationModifyDNOperation modifyDNOperation) 1007 { 1008 boolean hasSubentryWritePrivilege = false; 1009 1010 lock.readLock().lock(); 1011 try 1012 { 1013 final Entry oldEntry = modifyDNOperation.getOriginalEntry(); 1014 Collection<SubEntry> setToDelete = 1015 dit2SubEntry.getSubtree(oldEntry.getName()); 1016 for (SubEntry subentry : setToDelete) 1017 { 1018 if (!hasSubentryWritePrivilege) 1019 { 1020 ClientConnection conn = modifyDNOperation.getClientConnection(); 1021 if (!conn.hasPrivilege(Privilege.SUBENTRY_WRITE, 1022 conn.getOperationInProgress( 1023 modifyDNOperation.getMessageID()))) 1024 { 1025 return PluginResult.PreOperation.stopProcessing( 1026 ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 1027 ERR_SUBENTRY_WRITE_INSUFFICIENT_PRIVILEGES.get()); 1028 } 1029 hasSubentryWritePrivilege = true; 1030 } 1031 1032 final Entry newEntry = modifyDNOperation.getUpdatedEntry(); 1033 final Entry currentSubentry = subentry.getEntry(); 1034 final Entry renamedSubentry = currentSubentry.duplicate(false); 1035 final DN renamedDN = currentSubentry.getName().rename(oldEntry.getName(), newEntry.getName()); 1036 renamedSubentry.setDN(renamedDN); 1037 1038 for (SubentryChangeListener changeListener : changeListeners) 1039 { 1040 try 1041 { 1042 changeListener.checkSubentryModifyAcceptable(currentSubentry, renamedSubentry); 1043 } 1044 catch (DirectoryException de) 1045 { 1046 logger.traceException(de); 1047 1048 return PluginResult.PreOperation.stopProcessing( 1049 de.getResultCode(), de.getMessageObject()); 1050 } 1051 } 1052 } 1053 } 1054 finally 1055 { 1056 lock.readLock().unlock(); 1057 } 1058 1059 return PluginResult.PreOperation.continueOperationProcessing(); 1060 } 1061 1062 /** {@inheritDoc} */ 1063 @Override 1064 public PostOperation doPostOperation( 1065 PostOperationAddOperation addOperation) 1066 { 1067 // Only do something if the operation is successful, meaning there 1068 // has been a change. 1069 if (addOperation.getResultCode() == ResultCode.SUCCESS) 1070 { 1071 doPostAdd(addOperation.getEntryToAdd()); 1072 } 1073 1074 // If we've gotten here, then everything is acceptable. 1075 return PluginResult.PostOperation.continueOperationProcessing(); 1076 } 1077 1078 /** {@inheritDoc} */ 1079 @Override 1080 public PostOperation doPostOperation( 1081 PostOperationDeleteOperation deleteOperation) 1082 { 1083 // Only do something if the operation is successful, meaning there 1084 // has been a change. 1085 if (deleteOperation.getResultCode() == ResultCode.SUCCESS) 1086 { 1087 doPostDelete(deleteOperation.getEntryToDelete()); 1088 } 1089 1090 // If we've gotten here, then everything is acceptable. 1091 return PluginResult.PostOperation.continueOperationProcessing(); 1092 } 1093 1094 /** {@inheritDoc} */ 1095 @Override 1096 public PostOperation doPostOperation( 1097 PostOperationModifyOperation modifyOperation) 1098 { 1099 // Only do something if the operation is successful, meaning there 1100 // has been a change. 1101 if (modifyOperation.getResultCode() == ResultCode.SUCCESS) 1102 { 1103 doPostModify(modifyOperation.getCurrentEntry(), 1104 modifyOperation.getModifiedEntry()); 1105 } 1106 1107 // If we've gotten here, then everything is acceptable. 1108 return PluginResult.PostOperation.continueOperationProcessing(); 1109 } 1110 1111 /** {@inheritDoc} */ 1112 @Override 1113 public PostOperation doPostOperation( 1114 PostOperationModifyDNOperation modifyDNOperation) 1115 { 1116 // Only do something if the operation is successful, meaning there 1117 // has been a change. 1118 if (modifyDNOperation.getResultCode() == ResultCode.SUCCESS) 1119 { 1120 doPostModifyDN(modifyDNOperation.getOriginalEntry(), 1121 modifyDNOperation.getUpdatedEntry()); 1122 } 1123 1124 // If we've gotten here, then everything is acceptable. 1125 return PluginResult.PostOperation.continueOperationProcessing(); 1126 } 1127 1128 /** {@inheritDoc} */ 1129 @Override 1130 public void doPostSynchronization( 1131 PostSynchronizationAddOperation addOperation) 1132 { 1133 Entry entry = addOperation.getEntryToAdd(); 1134 if (entry != null) 1135 { 1136 doPostAdd(entry); 1137 } 1138 } 1139 1140 /** {@inheritDoc} */ 1141 @Override 1142 public void doPostSynchronization( 1143 PostSynchronizationDeleteOperation deleteOperation) 1144 { 1145 Entry entry = deleteOperation.getEntryToDelete(); 1146 if (entry != null) 1147 { 1148 doPostDelete(entry); 1149 } 1150 } 1151 1152 /** {@inheritDoc} */ 1153 @Override 1154 public void doPostSynchronization( 1155 PostSynchronizationModifyOperation modifyOperation) 1156 { 1157 Entry entry = modifyOperation.getCurrentEntry(); 1158 Entry modEntry = modifyOperation.getModifiedEntry(); 1159 if (entry != null && modEntry != null) 1160 { 1161 doPostModify(entry, modEntry); 1162 } 1163 } 1164 1165 /** {@inheritDoc} */ 1166 @Override 1167 public void doPostSynchronization( 1168 PostSynchronizationModifyDNOperation modifyDNOperation) 1169 { 1170 Entry oldEntry = modifyDNOperation.getOriginalEntry(); 1171 Entry newEntry = modifyDNOperation.getUpdatedEntry(); 1172 if (oldEntry != null && newEntry != null) 1173 { 1174 doPostModifyDN(oldEntry, newEntry); 1175 } 1176 } 1177}