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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.api; 028 import org.opends.messages.Message; 029 030 031 032 import java.util.ArrayList; 033 import java.util.LinkedHashSet; 034 import java.util.List; 035 import java.util.Set; 036 037 import org.opends.server.admin.Configuration; 038 import org.opends.server.config.ConfigException; 039 import org.opends.server.core.AddOperation; 040 import org.opends.server.core.DeleteOperation; 041 import org.opends.server.core.DirectoryServer; 042 import org.opends.server.core.ModifyOperation; 043 import org.opends.server.core.ModifyDNOperation; 044 import org.opends.server.core.SearchOperation; 045 import org.opends.server.monitors.BackendMonitor; 046 import org.opends.server.types.AttributeType; 047 import org.opends.server.types.BackupConfig; 048 import org.opends.server.types.BackupDirectory; 049 import org.opends.server.types.CanceledOperationException; 050 import org.opends.server.types.DirectoryException; 051 import org.opends.server.types.DN; 052 import org.opends.server.types.Entry; 053 import org.opends.server.types.IndexType; 054 import org.opends.server.types.InitializationException; 055 import org.opends.server.types.LDIFExportConfig; 056 import org.opends.server.types.LDIFImportConfig; 057 import org.opends.server.types.LDIFImportResult; 058 import org.opends.server.types.RestoreConfig; 059 import org.opends.server.types.SearchFilter; 060 import org.opends.server.types.WritabilityMode; 061 import org.opends.server.types.ConditionResult; 062 063 import static org.opends.messages.BackendMessages.*; 064 065 066 067 /** 068 * This class defines the set of methods and structures that must be 069 * implemented for a Directory Server backend. 070 */ 071 @org.opends.server.types.PublicAPI( 072 stability=org.opends.server.types.StabilityLevel.VOLATILE, 073 mayInstantiate=false, 074 mayExtend=true, 075 mayInvoke=false) 076 public abstract class Backend 077 { 078 // The backend that holds a portion of the DIT that is 079 // hierarchically above the information in this backend. 080 private Backend parentBackend; 081 082 // The set of backends that hold portions of the DIT that are 083 // hierarchically below the information in this backend. 084 private Backend[] subordinateBackends; 085 086 // The backend monitor associated with this backend. 087 private BackendMonitor backendMonitor; 088 089 // Indicates whether this is a private backend or one that holds 090 // user data. 091 private boolean isPrivateBackend; 092 093 // The unique identifier for this backend. 094 private String backendID; 095 096 // The writability mode for this backend. 097 private WritabilityMode writabilityMode; 098 099 100 101 /** 102 * Creates a new backend with the provided information. All backend 103 * implementations must implement a default constructor that use 104 * {@code super} to invoke this constructor. 105 */ 106 protected Backend() 107 { 108 backendID = null; 109 parentBackend = null; 110 subordinateBackends = new Backend[0]; 111 isPrivateBackend = false; 112 writabilityMode = WritabilityMode.ENABLED; 113 backendMonitor = null; 114 } 115 116 117 118 /** 119 * Configure this backend based on the information in the provided 120 * configuration. 121 * 122 * @param cfg The configuration of this backend. 123 * 124 * @throws ConfigException 125 * If there is an error in the configuration. 126 */ 127 public abstract void configureBackend(Configuration cfg) 128 throws ConfigException; 129 130 131 132 /** 133 * Indicates whether the provided configuration is acceptable for 134 * this backend. It should be possible to call this method on an 135 * uninitialized backend instance in order to determine whether the 136 * backend would be able to use the provided configuration. 137 * <BR><BR> 138 * Note that implementations which use a subclass of the provided 139 * configuration class will likely need to cast the configuration 140 * to the appropriate subclass type. 141 * 142 * @param configuration The backend configuration for which 143 * to make the determination. 144 * @param unacceptableReasons A list that may be used to hold the 145 * reasons that the provided 146 * configuration is not acceptable. 147 * 148 * @return {@code true} if the provided configuration is acceptable 149 * for this backend, or {@code false} if not. 150 */ 151 public boolean isConfigurationAcceptable( 152 Configuration configuration, 153 List<Message> unacceptableReasons) 154 { 155 // This default implementation does not perform any special 156 // validation. It should be overridden by backend implementations 157 // that wish to perform more detailed validation. 158 return true; 159 } 160 161 162 163 /** 164 * Initializes this backend based on the information provided 165 * when the backend was configured. 166 * 167 * @see #configureBackend 168 * 169 * @throws ConfigException If an unrecoverable problem arises in 170 * the process of performing the 171 * initialization. 172 * 173 * @throws InitializationException If a problem occurs during 174 * initialization that is not 175 * related to the server 176 * configuration. 177 */ 178 public abstract void initializeBackend() 179 throws ConfigException, InitializationException; 180 181 182 183 /** 184 * Performs any necessary work to finalize this backend, including 185 * closing any underlying databases or connections and deregistering 186 * any suffixes that it manages with the Directory Server. This may 187 * be called during the Directory Server shutdown process or if a 188 * backend is disabled with the server online. It must not return 189 * until the backend is closed. 190 * <BR><BR> 191 * This method may not throw any exceptions. If any problems are 192 * encountered, then they may be logged but the closure should 193 * progress as completely as possible. 194 */ 195 public abstract void finalizeBackend(); 196 197 198 199 /** 200 * Retrieves the set of base-level DNs that may be used within this 201 * backend. 202 * 203 * @return The set of base-level DNs that may be used within this 204 * backend. 205 */ 206 public abstract DN[] getBaseDNs(); 207 208 209 210 /** 211 * Attempts to pre-load all the entries stored within this backend 212 * into the entry cache. Note that the caller must ensure that the 213 * backend stays in read-only state until this method returns as 214 * no entry locking is performed during this operation. Also note 215 * that any backend implementing this method should implement pre- 216 * load progress reporting and error handling specific to its own 217 * implementation. 218 * 219 * @throws UnsupportedOperationException if backend does not 220 * support this operation. 221 */ 222 public abstract void preloadEntryCache() 223 throws UnsupportedOperationException; 224 225 226 227 /** 228 * Indicates whether the data associated with this backend may be 229 * considered local (i.e., in a repository managed by the Directory 230 * Server) rather than remote (i.e., in an external repository 231 * accessed by the Directory Server but managed through some other 232 * means). 233 * 234 * @return {@code true} if the data associated with this backend 235 * may be considered local, or {@code false} if it is 236 * remote. 237 */ 238 public abstract boolean isLocal(); 239 240 241 242 /** 243 * Indicates whether search operations which target the specified 244 * attribute in the indicated manner would be considered indexed 245 * in this backend. The operation should be considered indexed only 246 * if the specified operation can be completed efficiently within 247 * the backend. 248 * <BR><BR> 249 * Note that this method should return a general result that covers 250 * all values of the specified attribute. If a the specified 251 * attribute is indexed in the indicated manner but some particular 252 * values may still be treated as unindexed (e.g., if the number of 253 * entries with that attribute value exceeds some threshold), then 254 * this method should still return {@code true} for the specified 255 * attribute and index type. 256 * 257 * @param attributeType The attribute type for which to make the 258 * determination. 259 * @param indexType The index type for which to make the 260 * determination. 261 * 262 * @return {@code true} if search operations targeting the 263 * specified attribute in the indicated manner should be 264 * considered indexed, or {@code false} if not. 265 */ 266 public abstract boolean isIndexed(AttributeType attributeType, 267 IndexType indexType); 268 269 270 271 /** 272 * Indicates whether extensible match search operations that target 273 * the specified attribute with the given matching rule should be 274 * considered indexed in this backend. 275 * 276 * @param attributeType The attribute type for which to make the 277 * determination. 278 * @param matchingRule The matching rule for which to make the 279 * determination. 280 * 281 * @return {@code true} if extensible match search operations 282 * targeting the specified attribute with the given 283 * matching rule should be considered indexed, or 284 * {@code false} if not. 285 */ 286 public boolean isIndexed(AttributeType attributeType, 287 MatchingRule matchingRule) 288 { 289 return false; 290 } 291 292 293 294 /** 295 * Indicates whether a subtree search using the provided filter 296 * would be indexed in this backend. This default implementation 297 * uses a rough set of logic that makes a best-effort determination. 298 * Subclasses that provide a more complete indexing mechanism may 299 * wish to override this method and provide a more accurate result. 300 * 301 * @param filter The search filter for which to make the 302 * determination. 303 * 304 * @return {@code true} if it is believed that the provided filter 305 * would be indexed in this backend, or {@code false} if 306 * not. 307 */ 308 public boolean isIndexed(SearchFilter filter) 309 { 310 switch (filter.getFilterType()) 311 { 312 case AND: 313 // At least one of the subordinate filter components must be 314 // indexed. 315 for (SearchFilter f : filter.getFilterComponents()) 316 { 317 if (isIndexed(f)) 318 { 319 return true; 320 } 321 } 322 return false; 323 324 325 case OR: 326 for (SearchFilter f : filter.getFilterComponents()) 327 { 328 if (! isIndexed(f)) 329 { 330 return false; 331 } 332 } 333 return (! filter.getFilterComponents().isEmpty()); 334 335 336 case NOT: 337 // NOT filters are not considered indexed by default. 338 return false; 339 340 341 case EQUALITY: 342 return isIndexed(filter.getAttributeType(), 343 IndexType.EQUALITY); 344 345 346 case SUBSTRING: 347 return isIndexed(filter.getAttributeType(), 348 IndexType.SUBSTRING); 349 350 351 case GREATER_OR_EQUAL: 352 return isIndexed(filter.getAttributeType(), 353 IndexType.GREATER_OR_EQUAL); 354 355 356 case LESS_OR_EQUAL: 357 return isIndexed(filter.getAttributeType(), 358 IndexType.LESS_OR_EQUAL); 359 360 361 case PRESENT: 362 return isIndexed(filter.getAttributeType(), 363 IndexType.PRESENCE); 364 365 366 case APPROXIMATE_MATCH: 367 return isIndexed(filter.getAttributeType(), 368 IndexType.APPROXIMATE); 369 370 371 case EXTENSIBLE_MATCH: 372 // The attribute type must be provided for us to make the 373 // determination. If a matching rule ID is provided, then 374 // we'll use it as well, but if not then we'll use the 375 // default equality matching rule for the attribute type. 376 AttributeType attrType = filter.getAttributeType(); 377 if (attrType == null) 378 { 379 return false; 380 } 381 382 MatchingRule matchingRule; 383 String matchingRuleID = filter.getMatchingRuleID(); 384 if (matchingRuleID == null) 385 { 386 matchingRule = DirectoryServer.getMatchingRule( 387 matchingRuleID.toLowerCase()); 388 } 389 else 390 { 391 matchingRule = attrType.getEqualityMatchingRule(); 392 } 393 394 if (matchingRule == null) 395 { 396 return false; 397 } 398 else 399 { 400 return isIndexed(attrType, matchingRule); 401 } 402 403 404 default: 405 return false; 406 } 407 } 408 409 410 411 /** 412 * Retrieves the requested entry from this backend. Note that the 413 * caller must hold a read or write lock on the specified DN. 414 * 415 * @param entryDN The distinguished name of the entry to retrieve. 416 * 417 * @return The requested entry, or {@code null} if the entry does 418 * not exist. 419 * 420 * @throws DirectoryException If a problem occurs while trying to 421 * retrieve the entry. 422 */ 423 public abstract Entry getEntry(DN entryDN) 424 throws DirectoryException; 425 426 427 428 /** 429 * Indicates whether the requested entry has any subordinates. 430 * 431 * @param entryDN The distinguished name of the entry. 432 * 433 * @return {@code ConditionResult.TRUE} if the entry has one or more 434 * subordinates or {@code ConditionResult.FALSE} otherwise 435 * or {@code ConditionResult.UNDEFINED} if it can not be 436 * determined. 437 * 438 * @throws DirectoryException If a problem occurs while trying to 439 * retrieve the entry. 440 */ 441 public abstract ConditionResult hasSubordinates(DN entryDN) 442 throws DirectoryException; 443 444 445 446 /** 447 * Retrieves the number of subordinates for the requested entry. 448 * 449 * @param entryDN The distinguished name of the entry. 450 * 451 * @param subtree <code>true</code> to include all entries from the 452 * requested entry to the lowest level in the 453 * tree or <code>false</code> to only include 454 * the entries immediately below the requested 455 * entry. 456 * 457 * @return The number of subordinate entries for the requested entry 458 * or -1 if it can not be determined. 459 * 460 * @throws DirectoryException If a problem occurs while trying to 461 * retrieve the entry. 462 */ 463 public abstract long numSubordinates(DN entryDN, boolean subtree) 464 throws DirectoryException; 465 466 467 468 /** 469 * Indicates whether an entry with the specified DN exists in the 470 * backend. The default implementation obtains a read lock and calls 471 * {@code getEntry}, but backend implementations may override this 472 * with a more efficient version that does not require a lock. The 473 * caller is not required to hold any locks on the specified DN. 474 * 475 * @param entryDN The DN of the entry for which to determine 476 * existence. 477 * 478 * @return {@code true} if the specified entry exists in this 479 * backend, or {@code false} if it does not. 480 * 481 * @throws DirectoryException If a problem occurs while trying to 482 * make the determination. 483 */ 484 public boolean entryExists(DN entryDN) 485 throws DirectoryException 486 { 487 return (getEntry(entryDN) != null); 488 } 489 490 491 492 /** 493 * Adds the provided entry to this backend. This method must ensure 494 * that the entry is appropriate for the backend and that no entry 495 * already exists with the same DN. The caller must hold a write 496 * lock on the DN of the provided entry. 497 * 498 * @param entry The entry to add to this backend. 499 * @param addOperation The add operation with which the new entry 500 * is associated. This may be {@code null} 501 * for adds performed internally. 502 * 503 * @throws DirectoryException If a problem occurs while trying to 504 * add the entry. 505 * 506 * @throws CanceledOperationException If this backend noticed and 507 * reacted to a request to 508 * cancel or abandon the add 509 * operation. 510 */ 511 public abstract void addEntry(Entry entry, 512 AddOperation addOperation) 513 throws DirectoryException, CanceledOperationException; 514 515 516 517 /** 518 * Removes the specified entry from this backend. This method must 519 * ensure that the entry exists and that it does not have any 520 * subordinate entries (unless the backend supports a subtree delete 521 * operation and the client included the appropriate information in 522 * the request). The caller must hold a write lock on the provided 523 * entry DN. 524 * 525 * @param entryDN The DN of the entry to remove from this 526 * backend. 527 * @param deleteOperation The delete operation with which this 528 * action is associated. This may be 529 * {@code null} for deletes performed 530 * internally. 531 * 532 * @throws DirectoryException If a problem occurs while trying to 533 * remove the entry. 534 * 535 * @throws CanceledOperationException If this backend noticed and 536 * reacted to a request to 537 * cancel or abandon the 538 * delete operation. 539 */ 540 public abstract void deleteEntry(DN entryDN, 541 DeleteOperation deleteOperation) 542 throws DirectoryException, CanceledOperationException; 543 544 545 546 /** 547 * Replaces the specified entry with the provided entry in this 548 * backend. The backend must ensure that an entry already exists 549 * with the same DN as the provided entry. The caller must hold a 550 * write lock on the DN of the provided entry. 551 * 552 * @param entry The new entry to use in place of the 553 * existing entry with the same DN. 554 * @param modifyOperation The modify operation with which this 555 * action is associated. This may be 556 * {@code null} for modifications performed 557 * internally. 558 * 559 * @throws DirectoryException If a problem occurs while trying to 560 * replace the entry. 561 * 562 * @throws CanceledOperationException If this backend noticed and 563 * reacted to a request to 564 * cancel or abandon the 565 * modify operation. 566 */ 567 public abstract void replaceEntry(Entry entry, 568 ModifyOperation modifyOperation) 569 throws DirectoryException, CanceledOperationException; 570 571 572 573 /** 574 * Moves and/or renames the provided entry in this backend, altering 575 * any subordinate entries as necessary. This must ensure that an 576 * entry already exists with the provided current DN, and that no 577 * entry exists with the target DN of the provided entry. The 578 * caller must hold write locks on both the current DN and the new 579 * DN for the entry. 580 * 581 * @param currentDN The current DN of the entry to be 582 * replaced. 583 * @param entry The new content to use for the entry. 584 * @param modifyDNOperation The modify DN operation with which 585 * this action is associated. This may 586 * be {@code null} for modify DN 587 * operations performed internally. 588 * 589 * @throws DirectoryException If a problem occurs while trying to 590 * perform the rename. 591 * 592 * @throws CanceledOperationException If this backend noticed and 593 * reacted to a request to 594 * cancel or abandon the 595 * modify DN operation. 596 */ 597 public abstract void renameEntry(DN currentDN, Entry entry, 598 ModifyDNOperation modifyDNOperation) 599 throws DirectoryException, CanceledOperationException; 600 601 602 603 /** 604 * Processes the specified search in this backend. Matching entries 605 * should be provided back to the core server using the 606 * {@code SearchOperation.returnEntry} method. The caller is not 607 * required to have any locks when calling this operation. 608 * 609 * @param searchOperation The search operation to be processed. 610 * 611 * @throws DirectoryException If a problem occurs while processing 612 * the search. 613 * 614 * @throws CanceledOperationException If this backend noticed and 615 * reacted to a request to 616 * cancel or abandon the 617 * search operation. 618 */ 619 public abstract void search(SearchOperation searchOperation) 620 throws DirectoryException, CanceledOperationException; 621 622 623 624 /** 625 * Retrieves the OIDs of the controls that may be supported by this 626 * backend. 627 * 628 * @return The OIDs of the controls that may be supported by this 629 * backend. 630 */ 631 public abstract Set<String> getSupportedControls(); 632 633 634 635 /** 636 * Indicates whether this backend supports the specified control. 637 * 638 * @param controlOID The OID of the control for which to make the 639 * determination. 640 * 641 * @return {@code true} if this backends supports the control with 642 * the specified OID, or {@code false} if it does not. 643 */ 644 public final boolean supportsControl(String controlOID) 645 { 646 Set<String> supportedControls = getSupportedControls(); 647 return ((supportedControls != null) && 648 supportedControls.contains(controlOID)); 649 } 650 651 652 653 /** 654 * Retrieves the OIDs of the features that may be supported by this 655 * backend. 656 * 657 * @return The OIDs of the features that may be supported by this 658 * backend. 659 */ 660 public abstract Set<String> getSupportedFeatures(); 661 662 663 664 /** 665 * Indicates whether this backend supports the specified feature. 666 * 667 * @param featureOID The OID of the feature for which to make the 668 * determination. 669 * 670 * @return {@code true} if this backend supports the feature with 671 * the specified OID, or {@code false} if it does not. 672 */ 673 public final boolean supportsFeature(String featureOID) 674 { 675 Set<String> supportedFeatures = getSupportedFeatures(); 676 return ((supportedFeatures != null) && 677 supportedFeatures.contains(featureOID)); 678 } 679 680 681 682 /** 683 * Indicates whether this backend provides a mechanism to export the 684 * data it contains to an LDIF file. 685 * 686 * @return {@code true} if this backend provides an LDIF export 687 * mechanism, or {@code false} if not. 688 */ 689 public abstract boolean supportsLDIFExport(); 690 691 692 693 /** 694 * Exports the contents of this backend to LDIF. This method should 695 * only be called if {@code supportsLDIFExport} returns 696 * {@code true}. Note that the server will not explicitly 697 * initialize this backend before calling this method. 698 * 699 * @param exportConfig The configuration to use when performing 700 * the export. 701 * 702 * @throws DirectoryException If a problem occurs while performing 703 * the LDIF export. 704 */ 705 public abstract void exportLDIF(LDIFExportConfig exportConfig) 706 throws DirectoryException; 707 708 709 710 /** 711 * Indicates whether this backend provides a mechanism to import its 712 * data from an LDIF file. 713 * 714 * @return {@code true} if this backend provides an LDIF import 715 * mechanism, or {@code false} if not. 716 */ 717 public abstract boolean supportsLDIFImport(); 718 719 720 721 /** 722 * Imports information from an LDIF file into this backend. This 723 * method should only be called if {@code supportsLDIFImport} 724 * returns {@code true}. Note that the server will not explicitly 725 * initialize this backend before calling this method. 726 * 727 * @param importConfig The configuration to use when performing 728 * the import. 729 * 730 * @return Information about the result of the import processing. 731 * 732 * @throws DirectoryException If a problem occurs while performing 733 * the LDIF import. 734 */ 735 public abstract LDIFImportResult importLDIF( 736 LDIFImportConfig importConfig) 737 throws DirectoryException; 738 739 740 741 /** 742 * Indicates whether this backend provides a backup mechanism of any 743 * kind. This method is used by the backup process when backing up 744 * all backends to determine whether this backend is one that should 745 * be skipped. It should only return {@code true} for backends that 746 * it is not possible to archive directly (e.g., those that don't 747 * store their data locally, but rather pass through requests to 748 * some other repository). 749 * 750 * @return {@code true} if this backend provides any kind of backup 751 * mechanism, or {@code false} if it does not. 752 */ 753 public abstract boolean supportsBackup(); 754 755 756 757 /** 758 * Indicates whether this backend provides a mechanism to perform a 759 * backup of its contents in a form that can be restored later, 760 * based on the provided configuration. 761 * 762 * @param backupConfig The configuration of the backup for 763 * which to make the determination. 764 * @param unsupportedReason A buffer to which a message can be 765 * appended 766 * explaining why the requested backup is 767 * not supported. 768 * 769 * @return {@code true} if this backend provides a mechanism for 770 * performing backups with the provided configuration, or 771 * {@code false} if not. 772 */ 773 public abstract boolean supportsBackup(BackupConfig backupConfig, 774 StringBuilder unsupportedReason); 775 776 777 778 /** 779 * Creates a backup of the contents of this backend in a form that 780 * may be restored at a later date if necessary. This method should 781 * only be called if {@code supportsBackup} returns {@code true}. 782 * Note that the server will not explicitly initialize this backend 783 * before calling this method. 784 * 785 * @param backupConfig The configuration to use when performing 786 * the backup. 787 * 788 * @throws DirectoryException If a problem occurs while performing 789 * the backup. 790 */ 791 public abstract void createBackup(BackupConfig backupConfig) 792 throws DirectoryException; 793 794 795 796 /** 797 * Removes the specified backup if it is possible to do so. 798 * 799 * @param backupDirectory The backup directory structure with 800 * which the specified backup is 801 * associated. 802 * @param backupID The backup ID for the backup to be 803 * removed. 804 * 805 * @throws DirectoryException If it is not possible to remove the 806 * specified backup for some reason 807 * (e.g., no such backup exists or 808 * there are other backups that are 809 * dependent upon it). 810 */ 811 public abstract void removeBackup(BackupDirectory backupDirectory, 812 String backupID) 813 throws DirectoryException; 814 815 816 817 /** 818 * Indicates whether this backend provides a mechanism to restore a 819 * backup. 820 * 821 * @return {@code true} if this backend provides a mechanism for 822 * restoring backups, or {@code false} if not. 823 */ 824 public abstract boolean supportsRestore(); 825 826 827 828 /** 829 * Restores a backup of the contents of this backend. This method 830 * should only be called if {@code supportsRestore} returns 831 * {@code true}. Note that the server will not explicitly 832 * initialize this backend before calling this method. 833 * 834 * @param restoreConfig The configuration to use when performing 835 * the restore. 836 * 837 * @throws DirectoryException If a problem occurs while performing 838 * the restore. 839 */ 840 public abstract void restoreBackup(RestoreConfig restoreConfig) 841 throws DirectoryException; 842 843 844 845 /** 846 * Retrieves the unique identifier for this backend. 847 * 848 * @return The unique identifier for this backend. 849 */ 850 public final String getBackendID() 851 { 852 return backendID; 853 } 854 855 856 857 /** 858 * Specifies the unique identifier for this backend. 859 * 860 * @param backendID The unique identifier for this backend. 861 */ 862 public final void setBackendID(String backendID) 863 { 864 this.backendID = backendID; 865 } 866 867 868 869 /** 870 * Indicates whether this backend holds private data or user data. 871 * 872 * @return {@code true} if this backend holds private data, or 873 * {@code false} if it holds user data. 874 */ 875 public final boolean isPrivateBackend() 876 { 877 return isPrivateBackend; 878 } 879 880 881 882 /** 883 * Specifies whether this backend holds private data or user data. 884 * 885 * @param isPrivateBackend Specifies whether this backend holds 886 * private data or user data. 887 */ 888 public final void setPrivateBackend(boolean isPrivateBackend) 889 { 890 this.isPrivateBackend = isPrivateBackend; 891 } 892 893 894 895 /** 896 * Retrieves the writability mode for this backend. 897 * 898 * @return The writability mode for this backend. 899 */ 900 public final WritabilityMode getWritabilityMode() 901 { 902 return writabilityMode; 903 } 904 905 906 907 /** 908 * Specifies the writability mode for this backend. 909 * 910 * @param writabilityMode The writability mode for this backend. 911 */ 912 public final void setWritabilityMode( 913 WritabilityMode writabilityMode) 914 { 915 if (writabilityMode == null) 916 { 917 this.writabilityMode = WritabilityMode.ENABLED; 918 } 919 else 920 { 921 this.writabilityMode = writabilityMode; 922 } 923 } 924 925 926 927 /** 928 * Retrieves the backend monitor that is associated with this 929 * backend. 930 * 931 * @return The backend monitor that is associated with this 932 * backend, or {@code null} if none has been assigned. 933 */ 934 public final BackendMonitor getBackendMonitor() 935 { 936 return backendMonitor; 937 } 938 939 940 941 /** 942 * Sets the backend monitor for this backend. 943 * 944 * @param backendMonitor The backend monitor for this backend. 945 */ 946 public final void setBackendMonitor(BackendMonitor backendMonitor) 947 { 948 this.backendMonitor = backendMonitor; 949 } 950 951 952 953 /** 954 * Retrieves the total number of entries contained in this backend, 955 * if that information is available. 956 * 957 * @return The total number of entries contained in this backend, 958 * or -1 if that information is not available. 959 */ 960 public abstract long getEntryCount(); 961 962 963 964 /** 965 * Retrieves the parent backend for this backend. 966 * 967 * @return The parent backend for this backend, or {@code null} if 968 * there is none. 969 */ 970 public final Backend getParentBackend() 971 { 972 return parentBackend; 973 } 974 975 976 977 /** 978 * Specifies the parent backend for this backend. 979 * 980 * @param parentBackend The parent backend for this backend. 981 */ 982 public final void setParentBackend(Backend parentBackend) 983 { 984 synchronized (this) 985 { 986 this.parentBackend = parentBackend; 987 } 988 } 989 990 991 992 /** 993 * Retrieves the set of subordinate backends for this backend. 994 * 995 * @return The set of subordinate backends for this backend, or an 996 * empty array if none exist. 997 */ 998 public final Backend[] getSubordinateBackends() 999 { 1000 return subordinateBackends; 1001 } 1002 1003 1004 1005 /** 1006 * Specifies the set of subordinate backends for this backend. 1007 * 1008 * @param subordinateBackends The set of subordinate backends for 1009 * this backend. 1010 */ 1011 public final void setSubordinateBackends( 1012 Backend[] subordinateBackends) 1013 { 1014 synchronized (this) 1015 { 1016 this.subordinateBackends = subordinateBackends; 1017 } 1018 } 1019 1020 1021 1022 /** 1023 * Indicates whether this backend has a subordinate backend 1024 * registered with the provided base DN. This may check recursively 1025 * if a subordinate backend has its own subordinate backends. 1026 * 1027 * @param subSuffixDN The DN of the sub-suffix for which to make 1028 * the determination. 1029 * 1030 * @return {@code true} if this backend has a subordinate backend 1031 * registered with the provided base DN, or {@code false} 1032 * if it does not. 1033 */ 1034 public final boolean hasSubSuffix(DN subSuffixDN) 1035 { 1036 Backend[] subBackends = subordinateBackends; 1037 for (Backend b : subBackends) 1038 { 1039 for (DN baseDN : b.getBaseDNs()) 1040 { 1041 if (baseDN.equals(subSuffixDN)) 1042 { 1043 return true; 1044 } 1045 } 1046 1047 if (b.hasSubSuffix(subSuffixDN)) 1048 { 1049 return true; 1050 } 1051 } 1052 1053 return false; 1054 } 1055 1056 1057 1058 /** 1059 * Removes the backend associated with the specified sub-suffix if 1060 * it is registered. This may check recursively if a subordinate 1061 * backend has its own subordinate backends. 1062 * 1063 * @param subSuffixDN The DN of the sub-suffix to remove from this 1064 * backend. 1065 * @param parentDN The superior DN for the sub-suffix DN that 1066 * matches one of the subordinate base DNs for 1067 * this backend. 1068 * 1069 * @throws ConfigException If the sub-suffix exists but it is not 1070 * possible to remove it for some reason. 1071 */ 1072 public final void removeSubSuffix(DN subSuffixDN, DN parentDN) 1073 throws ConfigException 1074 { 1075 synchronized (this) 1076 { 1077 boolean matchFound = false; 1078 ArrayList<Backend> subBackendList = 1079 new ArrayList<Backend>(subordinateBackends.length); 1080 for (Backend b : subordinateBackends) 1081 { 1082 boolean thisMatches = false; 1083 DN[] subBaseDNs = b.getBaseDNs(); 1084 for (DN dn : subBaseDNs) 1085 { 1086 if (dn.equals(subSuffixDN)) 1087 { 1088 if (subBaseDNs.length > 1) 1089 { 1090 Message message = 1091 ERR_BACKEND_CANNOT_REMOVE_MULTIBASE_SUB_SUFFIX. 1092 get(String.valueOf(subSuffixDN), 1093 String.valueOf(parentDN)); 1094 throw new ConfigException(message); 1095 } 1096 1097 thisMatches = true; 1098 matchFound = true; 1099 break; 1100 } 1101 } 1102 1103 if (! thisMatches) 1104 { 1105 if (b.hasSubSuffix(subSuffixDN)) 1106 { 1107 b.removeSubSuffix(subSuffixDN, parentDN); 1108 } 1109 else 1110 { 1111 subBackendList.add(b); 1112 } 1113 } 1114 } 1115 1116 if (matchFound) 1117 { 1118 Backend[] newSubordinateBackends = 1119 new Backend[subBackendList.size()]; 1120 subBackendList.toArray(newSubordinateBackends); 1121 subordinateBackends = newSubordinateBackends; 1122 } 1123 } 1124 } 1125 1126 1127 1128 /** 1129 * Adds the provided backend to the set of subordinate backends for 1130 * this backend. 1131 * 1132 * @param subordinateBackend The backend to add to the set of 1133 * subordinate backends for this 1134 * backend. 1135 */ 1136 public final void addSubordinateBackend(Backend subordinateBackend) 1137 { 1138 synchronized (this) 1139 { 1140 LinkedHashSet<Backend> backendSet = 1141 new LinkedHashSet<Backend>(); 1142 1143 for (Backend b : subordinateBackends) 1144 { 1145 backendSet.add(b); 1146 } 1147 1148 if (backendSet.add(subordinateBackend)) 1149 { 1150 Backend[] newSubordinateBackends = 1151 new Backend[backendSet.size()]; 1152 backendSet.toArray(newSubordinateBackends); 1153 subordinateBackends = newSubordinateBackends; 1154 } 1155 } 1156 } 1157 1158 1159 1160 /** 1161 * Removes the provided backend from the set of subordinate backends 1162 * for this backend. 1163 * 1164 * @param subordinateBackend The backend to remove from the set of 1165 * subordinate backends for this 1166 * backend. 1167 */ 1168 public final void removeSubordinateBackend( 1169 Backend subordinateBackend) 1170 { 1171 synchronized (this) 1172 { 1173 ArrayList<Backend> backendList = 1174 new ArrayList<Backend>(subordinateBackends.length); 1175 1176 boolean found = false; 1177 for (Backend b : subordinateBackends) 1178 { 1179 if (b.equals(subordinateBackend)) 1180 { 1181 found = true; 1182 } 1183 else 1184 { 1185 backendList.add(b); 1186 } 1187 } 1188 1189 if (found) 1190 { 1191 Backend[] newSubordinateBackends = 1192 new Backend[backendList.size()]; 1193 backendList.toArray(newSubordinateBackends); 1194 subordinateBackends = newSubordinateBackends; 1195 } 1196 } 1197 } 1198 1199 1200 1201 /** 1202 * Indicates whether this backend should be used to handle 1203 * operations for the provided entry. 1204 * 1205 * @param entryDN The DN of the entry for which to make the 1206 * determination. 1207 * 1208 * @return {@code true} if this backend handles operations for the 1209 * provided entry, or {@code false} if it does not. 1210 */ 1211 public final boolean handlesEntry(DN entryDN) 1212 { 1213 DN[] baseDNs = getBaseDNs(); 1214 for (DN dn : baseDNs) 1215 { 1216 if (entryDN.isDescendantOf(dn)) 1217 { 1218 Backend[] subBackends = subordinateBackends; 1219 for (Backend b : subBackends) 1220 { 1221 if (b.handlesEntry(entryDN)) 1222 { 1223 return false; 1224 } 1225 } 1226 return true; 1227 } 1228 } 1229 return false; 1230 } 1231 1232 1233 1234 /** 1235 * Indicates whether a backend should be used to handle operations 1236 * for the provided entry given the set of base DNs and exclude DNs. 1237 * 1238 * @param entryDN The DN of the entry for which to make the 1239 * determination. 1240 * @param baseDNs The set of base DNs for the backend. 1241 * @param excludeDNs The set of DNs that should be excluded from 1242 * the backend. 1243 * 1244 * @return {@code true} if the backend should handle operations for 1245 * the provided entry, or {@code false} if it does not. 1246 */ 1247 public static final boolean handlesEntry(DN entryDN, 1248 List<DN> baseDNs, 1249 List<DN> excludeDNs) 1250 { 1251 for (DN baseDN : baseDNs) 1252 { 1253 if (entryDN.isDescendantOf(baseDN)) 1254 { 1255 if ((excludeDNs == null) || excludeDNs.isEmpty()) 1256 { 1257 return true; 1258 } 1259 1260 boolean isExcluded = false; 1261 for (DN excludeDN : excludeDNs) 1262 { 1263 if (entryDN.isDescendantOf(excludeDN)) 1264 { 1265 isExcluded = true; 1266 break; 1267 } 1268 } 1269 1270 if (! isExcluded) 1271 { 1272 return true; 1273 } 1274 } 1275 } 1276 1277 return false; 1278 } 1279 }