001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.api; 018 019import static org.opends.messages.BackendMessages.*; 020 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.LinkedHashSet; 025import java.util.List; 026import java.util.Queue; 027import java.util.Set; 028import java.util.concurrent.ConcurrentLinkedQueue; 029 030import org.forgerock.i18n.LocalizableMessage; 031import org.forgerock.opendj.config.server.ConfigException; 032import org.forgerock.opendj.ldap.ConditionResult; 033import org.forgerock.opendj.ldap.ResultCode; 034import org.forgerock.opendj.ldap.schema.MatchingRule; 035import org.opends.server.admin.Configuration; 036import org.opends.server.backends.RebuildConfig; 037import org.opends.server.backends.VerifyConfig; 038import org.opends.server.core.AddOperation; 039import org.opends.server.core.DeleteOperation; 040import org.opends.server.core.DirectoryServer; 041import org.opends.server.core.ModifyDNOperation; 042import org.opends.server.core.ModifyOperation; 043import org.opends.server.core.PersistentSearch; 044import org.opends.server.core.PersistentSearch.CancellationCallback; 045import org.opends.server.core.SearchOperation; 046import org.opends.server.core.ServerContext; 047import org.opends.server.monitors.BackendMonitor; 048import org.forgerock.opendj.ldap.schema.AttributeType; 049import org.opends.server.types.BackupConfig; 050import org.opends.server.types.BackupDirectory; 051import org.opends.server.types.CanceledOperationException; 052import org.forgerock.opendj.ldap.DN; 053import org.opends.server.types.DirectoryException; 054import org.opends.server.types.Entry; 055import org.opends.server.types.IndexType; 056import org.opends.server.types.InitializationException; 057import org.opends.server.types.LDIFExportConfig; 058import org.opends.server.types.LDIFImportConfig; 059import org.opends.server.types.LDIFImportResult; 060import org.opends.server.types.RestoreConfig; 061import org.opends.server.types.SearchFilter; 062import org.opends.server.types.WritabilityMode; 063/** 064 * This class defines the set of methods and structures that must be 065 * implemented for a Directory Server backend. 066 * 067 * @param <C> 068 * the type of the BackendCfg for the current backend 069 */ 070@org.opends.server.types.PublicAPI( 071 stability=org.opends.server.types.StabilityLevel.VOLATILE, 072 mayInstantiate=false, 073 mayExtend=true, 074 mayInvoke=false) 075public abstract class Backend<C extends Configuration> 076// should have been BackendCfg instead of Configuration 077{ 078 /** 079 * The backend that holds a portion of the DIT that is hierarchically above 080 * the information in this backend. 081 */ 082 private Backend<?> parentBackend; 083 084 /** 085 * The set of backends that hold portions of the DIT that are hierarchically 086 * below the information in this backend. 087 */ 088 private Backend<?>[] subordinateBackends = new Backend[0]; 089 090 /** The backend monitor associated with this backend. */ 091 private BackendMonitor backendMonitor; 092 093 /** Indicates whether this is a private backend or one that holds user data. */ 094 private boolean isPrivateBackend; 095 096 /** The unique identifier for this backend. */ 097 private String backendID; 098 099 /** The writability mode for this backend. */ 100 private WritabilityMode writabilityMode = WritabilityMode.ENABLED; 101 102 /** The set of persistent searches registered with this backend. */ 103 private final ConcurrentLinkedQueue<PersistentSearch> persistentSearches = new ConcurrentLinkedQueue<>(); 104 105 /** 106 * Configure this backend based on the information in the provided configuration. 107 * When the method returns, the backend will have been configured (ready to be opened) but still unable 108 * to process operations. 109 * 110 * @param cfg The configuration of this backend. 111 * @param serverContext The server context for this instance 112 * @throws ConfigException 113 * If there is an error in the configuration. 114 */ 115 public abstract void configureBackend(C cfg, ServerContext serverContext) throws ConfigException; 116 117 /** 118 * Indicates whether the provided configuration is acceptable for 119 * this backend. It should be possible to call this method on an 120 * uninitialized backend instance in order to determine whether the 121 * backend would be able to use the provided configuration. 122 * <BR><BR> 123 * Note that implementations which use a subclass of the provided 124 * configuration class will likely need to cast the configuration 125 * to the appropriate subclass type. 126 * 127 * @param configuration The backend configuration for which 128 * to make the determination. 129 * @param unacceptableReasons A list that may be used to hold the 130 * reasons that the provided 131 * configuration is not acceptable. 132 * @param serverContext this Directory Server instance's server context 133 * @return {@code true} if the provided configuration is acceptable 134 * for this backend, or {@code false} if not. 135 */ 136 public boolean isConfigurationAcceptable( 137 C configuration, 138 List<LocalizableMessage> unacceptableReasons, ServerContext serverContext) 139 { 140 // This default implementation does not perform any special 141 // validation. It should be overridden by backend implementations 142 // that wish to perform more detailed validation. 143 return true; 144 } 145 146 /** 147 * Opens this backend based on the information provided when the backend was configured. 148 * It also should open any underlying storage and register all suffixes with the server. 149 * 150 * @see #configureBackend 151 * 152 * @throws ConfigException If an unrecoverable problem arises while opening the backend. 153 * 154 * @throws InitializationException If a problem occurs during opening that is not 155 * related to the server configuration. 156 */ 157 public abstract void openBackend() throws ConfigException, InitializationException; 158 159 /** 160 * Performs any necessary work to finalize this backend. The backend must be an opened backend, 161 * so do not use this method on backends where only <code>configureBackend()</code> has been called. 162 * This may be called during the Directory Server shutdown process or if a backend is disabled 163 * with the server online. 164 * It must not return until the backend is closed. 165 * <p> 166 * This method may not throw any exceptions. If any problems are encountered, 167 * then they may be logged but the closure should progress as completely as 168 * possible. 169 * <p> 170 */ 171 public final void finalizeBackend() 172 { 173 for (PersistentSearch psearch : persistentSearches) 174 { 175 psearch.cancel(); 176 } 177 persistentSearches.clear(); 178 closeBackend(); 179 } 180 181 /** 182 * Performs any necessary work to finally close this backend, particularly 183 * closing any underlying databases or connections and deregistering 184 * any suffixes that it manages with the Directory Server. 185 * <p> 186 * It will be called as final step of <code>finalizeBackend()</code>, 187 * so subclasses might override it. 188 * </p> 189 */ 190 public void closeBackend() 191 { 192 } 193 194 /** 195 * Retrieves the set of base-level DNs that may be used within this 196 * backend. 197 * 198 * @return The set of base-level DNs that may be used within this 199 * backend. 200 */ 201 public abstract DN[] getBaseDNs(); 202 203 /** 204 * Indicates whether search operations which target the specified 205 * attribute in the indicated manner would be considered indexed 206 * in this backend. The operation should be considered indexed only 207 * if the specified operation can be completed efficiently within 208 * the backend. 209 * <BR><BR> 210 * Note that this method should return a general result that covers 211 * all values of the specified attribute. If a the specified 212 * attribute is indexed in the indicated manner but some particular 213 * values may still be treated as unindexed (e.g., if the number of 214 * entries with that attribute value exceeds some threshold), then 215 * this method should still return {@code true} for the specified 216 * attribute and index type. 217 * 218 * @param attributeType The attribute type for which to make the 219 * determination. 220 * @param indexType The index type for which to make the 221 * determination. 222 * 223 * @return {@code true} if search operations targeting the 224 * specified attribute in the indicated manner should be 225 * considered indexed, or {@code false} if not. 226 */ 227 public abstract boolean isIndexed(AttributeType attributeType, IndexType indexType); 228 229 /** 230 * Indicates whether extensible match search operations that target 231 * the specified attribute with the given matching rule should be 232 * considered indexed in this backend. 233 * 234 * @param attributeType The attribute type for which to make the 235 * determination. 236 * @param matchingRule The matching rule for which to make the 237 * determination. 238 * 239 * @return {@code true} if extensible match search operations 240 * targeting the specified attribute with the given 241 * matching rule should be considered indexed, or 242 * {@code false} if not. 243 */ 244 private boolean isIndexed(AttributeType attributeType, MatchingRule matchingRule) 245 { 246 return false; // FIXME This should be overridden by the JE Backend at least! 247 } 248 249 /** 250 * Indicates whether a subtree search using the provided filter 251 * would be indexed in this backend. This default implementation 252 * uses a rough set of logic that makes a best-effort determination. 253 * Subclasses that provide a more complete indexing mechanism may 254 * wish to override this method and provide a more accurate result. 255 * 256 * @param filter The search filter for which to make the 257 * determination. 258 * 259 * @return {@code true} if it is believed that the provided filter 260 * would be indexed in this backend, or {@code false} if 261 * not. 262 */ 263 public boolean isIndexed(SearchFilter filter) 264 { 265 switch (filter.getFilterType()) 266 { 267 case AND: 268 // At least one of the subordinate filter components must be 269 // indexed. 270 for (SearchFilter f : filter.getFilterComponents()) 271 { 272 if (isIndexed(f)) 273 { 274 return true; 275 } 276 } 277 return false; 278 279 280 case OR: 281 for (SearchFilter f : filter.getFilterComponents()) 282 { 283 if (! isIndexed(f)) 284 { 285 return false; 286 } 287 } 288 return !filter.getFilterComponents().isEmpty(); 289 290 291 case NOT: 292 // NOT filters are not considered indexed by default. 293 return false; 294 295 case EQUALITY: 296 return isIndexed(filter.getAttributeType(), IndexType.EQUALITY); 297 298 case SUBSTRING: 299 return isIndexed(filter.getAttributeType(), IndexType.SUBSTRING); 300 301 case GREATER_OR_EQUAL: 302 return isIndexed(filter.getAttributeType(), IndexType.GREATER_OR_EQUAL); 303 304 case LESS_OR_EQUAL: 305 return isIndexed(filter.getAttributeType(), IndexType.LESS_OR_EQUAL); 306 307 case PRESENT: 308 return isIndexed(filter.getAttributeType(), IndexType.PRESENCE); 309 310 case APPROXIMATE_MATCH: 311 return isIndexed(filter.getAttributeType(), IndexType.APPROXIMATE); 312 313 case EXTENSIBLE_MATCH: 314 // The attribute type must be provided for us to make the 315 // determination. If a matching rule ID is provided, then 316 // we'll use it as well, but if not then we'll use the 317 // default equality matching rule for the attribute type. 318 AttributeType attrType = filter.getAttributeType(); 319 if (attrType == null) 320 { 321 return false; 322 } 323 324 MatchingRule matchingRule; 325 String matchingRuleID = filter.getMatchingRuleID(); 326 if (matchingRuleID != null) 327 { 328 matchingRule = DirectoryServer.getMatchingRule( 329 matchingRuleID.toLowerCase()); 330 } 331 else 332 { 333 matchingRule = attrType.getEqualityMatchingRule(); 334 } 335 // FIXME isIndexed() always return false down below 336 return matchingRule != null && isIndexed(attrType, matchingRule); 337 338 339 default: 340 return false; 341 } 342 } 343 344 /** 345 * Retrieves the requested entry from this backend. The caller is not required to hold any locks 346 * on the specified DN. 347 * 348 * @param entryDN 349 * The distinguished name of the entry to retrieve. 350 * @return The requested entry, or {@code null} if the entry does not exist. 351 * @throws DirectoryException 352 * If a problem occurs while trying to retrieve the entry. 353 */ 354 public abstract Entry getEntry(DN entryDN) throws DirectoryException; 355 356 /** 357 * Indicates whether the requested entry has any subordinates. 358 * 359 * @param entryDN The distinguished name of the entry. 360 * 361 * @return {@code ConditionResult.TRUE} if the entry has one or more 362 * subordinates or {@code ConditionResult.FALSE} otherwise 363 * or {@code ConditionResult.UNDEFINED} if it can not be 364 * determined. 365 * 366 * @throws DirectoryException If a problem occurs while trying to 367 * retrieve the entry. 368 */ 369 public abstract ConditionResult hasSubordinates(DN entryDN) throws DirectoryException; 370 371 /** 372 * Retrieves the number of subordinates immediately below the requested entry. 373 * 374 * @param parentDN 375 * The distinguished name of the parent. 376 * @return The number of subordinate entries for the requested entry. 377 * @throws DirectoryException 378 * If baseDN isn't a base dn managed by this backend or if a problem occurs while trying to retrieve the 379 * entry. 380 * @throws NullPointerException 381 * if baseDN is null. 382 */ 383 public abstract long getNumberOfChildren(DN parentDN) throws DirectoryException; 384 385 /** 386 * Retrieves the number of entries for the specified base DN including all entries from the requested entry to the 387 * lowest level in the tree. 388 * 389 * @param baseDN 390 * The base distinguished name. 391 * @return The number of subordinate entries including the base dn. 392 * @throws DirectoryException 393 * If baseDN isn't a base dn managed by this backend or if a problem occurs while trying to retrieve the 394 * entry. 395 * @throws NullPointerException 396 * if baseDN is null. 397 */ 398 public abstract long getNumberOfEntriesInBaseDN(DN baseDN) throws DirectoryException; 399 400 /** 401 * Indicates whether an entry with the specified DN exists in the backend. The default 402 * implementation calls {@code getEntry}, but backend implementations may override this with a 403 * more efficient version. The caller is not required to hold any locks on the specified DN. 404 * 405 * @param entryDN 406 * The DN of the entry for which to determine existence. 407 * @return {@code true} if the specified entry exists in this backend, or {@code false} if it does 408 * not. 409 * @throws DirectoryException 410 * If a problem occurs while trying to make the determination. 411 */ 412 public boolean entryExists(DN entryDN) throws DirectoryException 413 { 414 return getEntry(entryDN) != null; 415 } 416 417 /** 418 * Adds the provided entry to this backend. This method must ensure 419 * that the entry is appropriate for the backend and that no entry 420 * already exists with the same DN. The caller must hold a write 421 * lock on the DN of the provided entry. 422 * 423 * @param entry The entry to add to this backend. 424 * @param addOperation The add operation with which the new entry 425 * is associated. This may be {@code null} 426 * for adds performed internally. 427 * 428 * @throws DirectoryException If a problem occurs while trying to 429 * add the entry. 430 * 431 * @throws CanceledOperationException If this backend noticed and 432 * reacted to a request to 433 * cancel or abandon the add 434 * operation. 435 */ 436 public abstract void addEntry(Entry entry, AddOperation addOperation) 437 throws DirectoryException, CanceledOperationException; 438 439 /** 440 * Removes the specified entry from this backend. This method must 441 * ensure that the entry exists and that it does not have any 442 * subordinate entries (unless the backend supports a subtree delete 443 * operation and the client included the appropriate information in 444 * the request). The caller must hold a write lock on the provided 445 * entry DN. 446 * 447 * @param entryDN The DN of the entry to remove from this 448 * backend. 449 * @param deleteOperation The delete operation with which this 450 * action is associated. This may be 451 * {@code null} for deletes performed 452 * internally. 453 * 454 * @throws DirectoryException If a problem occurs while trying to 455 * remove the entry. 456 * 457 * @throws CanceledOperationException If this backend noticed and 458 * reacted to a request to 459 * cancel or abandon the 460 * delete operation. 461 */ 462 public abstract void deleteEntry(DN entryDN, DeleteOperation deleteOperation) 463 throws DirectoryException, CanceledOperationException; 464 465 /** 466 * Replaces the specified entry with the provided entry in this 467 * backend. The backend must ensure that an entry already exists 468 * with the same DN as the provided entry. The caller must hold a 469 * write lock on the DN of the provided entry. 470 * 471 * @param oldEntry 472 * The original entry that is being replaced. 473 * @param newEntry 474 * The new entry to use in place of the existing entry with 475 * the same DN. 476 * @param modifyOperation 477 * The modify operation with which this action is 478 * associated. This may be {@code null} for modifications 479 * performed internally. 480 * @throws DirectoryException 481 * If a problem occurs while trying to replace the entry. 482 * @throws CanceledOperationException 483 * If this backend noticed and reacted to a request to 484 * cancel or abandon the modify operation. 485 */ 486 public abstract void replaceEntry(Entry oldEntry, Entry newEntry, 487 ModifyOperation modifyOperation) throws DirectoryException, 488 CanceledOperationException; 489 490 /** 491 * Moves and/or renames the provided entry in this backend, altering 492 * any subordinate entries as necessary. This must ensure that an 493 * entry already exists with the provided current DN, and that no 494 * entry exists with the target DN of the provided entry. The caller 495 * must hold write locks on both the current DN and the new DN for 496 * the entry. 497 * 498 * @param currentDN 499 * The current DN of the entry to be moved/renamed. 500 * @param entry 501 * The new content to use for the entry. 502 * @param modifyDNOperation 503 * The modify DN operation with which this action is 504 * associated. This may be {@code null} for modify DN 505 * operations performed internally. 506 * @throws DirectoryException 507 * If a problem occurs while trying to perform the rename. 508 * @throws CanceledOperationException 509 * If this backend noticed and reacted to a request to 510 * cancel or abandon the modify DN operation. 511 */ 512 public abstract void renameEntry(DN currentDN, Entry entry, ModifyDNOperation modifyDNOperation) 513 throws DirectoryException, CanceledOperationException; 514 515 /** 516 * Processes the specified search in this backend. Matching entries 517 * should be provided back to the core server using the 518 * {@code SearchOperation.returnEntry} method. The caller is not 519 * required to have any locks when calling this operation. 520 * 521 * @param searchOperation The search operation to be processed. 522 * 523 * @throws DirectoryException If a problem occurs while processing 524 * the search. 525 * 526 * @throws CanceledOperationException If this backend noticed and 527 * reacted to a request to 528 * cancel or abandon the 529 * search operation. 530 */ 531 public abstract void search(SearchOperation searchOperation) 532 throws DirectoryException, CanceledOperationException; 533 534 /** 535 * Retrieves the OIDs of the controls that may be supported by this 536 * backend. 537 * 538 * @return The OIDs of the controls that may be supported by this 539 * backend. 540 */ 541 public abstract Set<String> getSupportedControls(); 542 543 /** 544 * Indicates whether this backend supports the specified control. 545 * 546 * @param controlOID The OID of the control for which to make the 547 * determination. 548 * 549 * @return {@code true} if this backends supports the control with 550 * the specified OID, or {@code false} if it does not. 551 */ 552 public final boolean supportsControl(String controlOID) 553 { 554 Set<String> supportedControls = getSupportedControls(); 555 return supportedControls != null && supportedControls.contains(controlOID); 556 } 557 558 /** 559 * Retrieves the OIDs of the features that may be supported by this 560 * backend. 561 * 562 * @return The OIDs of the features that may be supported by this 563 * backend. 564 */ 565 public abstract Set<String> getSupportedFeatures(); 566 567 /** Enumeration of optional backend operations. */ 568 public static enum BackendOperation 569 { 570 /** Indicates whether this backend supports indexing attributes to speed up searches. */ 571 INDEXING, 572 /** Indicates whether this backend supports exporting the data it contains to an LDIF file. */ 573 LDIF_EXPORT, 574 /** Indicates whether this backend supports importing its data from an LDIF file. */ 575 LDIF_IMPORT, 576 /** 577 * Indicates whether this backend provides a backup mechanism of any kind. This method is used 578 * by the backup process when backing up all backends to determine whether this backend is one 579 * that should be skipped. It should only return {@code true} for backends that it is not 580 * possible to archive directly (e.g., those that don't store their data locally, but rather 581 * pass through requests to some other repository). 582 */ 583 BACKUP, 584 /** Indicates whether this backend can restore a backup. */ 585 RESTORE; 586 } 587 588 /** 589 * Indicates whether this backend supports the provided backend operation. 590 * 591 * @param backendOperation 592 * the backend operation 593 * @return {@code true} if this backend supports the provided backend operation, {@code false} 594 * otherwise. 595 */ 596 public abstract boolean supports(BackendOperation backendOperation); 597 598 /** 599 * Exports the contents of this backend to LDIF. This method should only be called if 600 * {@link #supports(BackendOperation)} with {@link BackendOperation#LDIF_EXPORT} returns 601 * {@code true}. 602 * <p> 603 * Note that the server will not explicitly initialize this backend before calling this method. 604 * 605 * @param exportConfig 606 * The configuration to use when performing the export. 607 * @throws DirectoryException 608 * If a problem occurs while performing the LDIF export. 609 */ 610 public abstract void exportLDIF(LDIFExportConfig exportConfig) throws DirectoryException; 611 612 /** 613 * Imports information from an LDIF file into this backend. This method should only be called if 614 * {@link #supports(BackendOperation)} with {@link BackendOperation#LDIF_IMPORT} returns 615 * {@code true}. 616 * <p> 617 * Note that the server will not explicitly initialize this backend before calling this method. 618 * 619 * @param importConfig 620 * The configuration to use when performing the import. 621 * @param serverContext 622 * The server context 623 * @return Information about the result of the import processing. 624 * @throws DirectoryException 625 * If a problem occurs while performing the LDIF import. 626 */ 627 public abstract LDIFImportResult importLDIF(LDIFImportConfig importConfig, ServerContext serverContext) 628 throws DirectoryException; 629 630 /** 631 * Verify the integrity of the backend instance. 632 * 633 * @param verifyConfig 634 * The verify configuration. 635 * @return The results of the operation. 636 * @throws ConfigException 637 * If an unrecoverable problem arises during initialization. 638 * @throws InitializationException 639 * If a problem occurs during initialization that is not related to the server 640 * configuration. 641 * @throws DirectoryException 642 * If a Directory Server error occurs. 643 */ 644 public long verifyBackend(VerifyConfig verifyConfig) 645 throws InitializationException, ConfigException, DirectoryException 646 { 647 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 648 ERR_INDEXES_NOT_SUPPORTED.get(getBackendID())); 649 } 650 651 /** 652 * Rebuild indexes in the backend instance. Note that the server will not explicitly initialize 653 * this backend before calling this method. 654 * 655 * @param rebuildConfig 656 * The rebuild configuration. 657 * @param serverContext 658 * The server context for this instance 659 * @throws ConfigException 660 * If an unrecoverable problem arises during initialization. 661 * @throws InitializationException 662 * If a problem occurs during initialization that is not related to the server 663 * configuration. 664 * @throws DirectoryException 665 * If a Directory Server error occurs. 666 */ 667 public void rebuildBackend(RebuildConfig rebuildConfig, ServerContext serverContext) 668 throws InitializationException, ConfigException, DirectoryException 669 { 670 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 671 ERR_INDEXES_NOT_SUPPORTED.get(getBackendID())); 672 } 673 674 /** 675 * Creates a backup of the contents of this backend in a form that may be restored at a later date 676 * if necessary. This method should only be called if {@link #supports(BackendOperation)} with 677 * {@link BackendOperation#BACKUP} returns {@code true}. 678 * <p> 679 * Note that the server will not explicitly initialize this backend before calling this method. 680 * 681 * @param backupConfig 682 * The configuration to use when performing the backup. 683 * @throws DirectoryException 684 * If a problem occurs while performing the backup. 685 */ 686 public abstract void createBackup(BackupConfig backupConfig) throws DirectoryException; 687 688 /** 689 * Removes the specified backup if it is possible to do so. 690 * 691 * @param backupDirectory The backup directory structure with 692 * which the specified backup is 693 * associated. 694 * @param backupID The backup ID for the backup to be 695 * removed. 696 * 697 * @throws DirectoryException If it is not possible to remove the 698 * specified backup for some reason 699 * (e.g., no such backup exists or 700 * there are other backups that are 701 * dependent upon it). 702 */ 703 public abstract void removeBackup(BackupDirectory backupDirectory, String backupID) 704 throws DirectoryException; 705 706 /** 707 * Restores a backup of the contents of this backend. This method should only be called if 708 * {@link #supports(BackendOperation)} with {@link BackendOperation#RESTORE} returns {@code true}. 709 * <p> 710 * Note that the server will not explicitly initialize this backend before calling this method. 711 * 712 * @param restoreConfig 713 * The configuration to use when performing the restore. 714 * @throws DirectoryException 715 * If a problem occurs while performing the restore. 716 */ 717 public abstract void restoreBackup(RestoreConfig restoreConfig) throws DirectoryException; 718 719 /** 720 * Retrieves the unique identifier for this backend. 721 * 722 * @return The unique identifier for this backend. 723 */ 724 public final String getBackendID() 725 { 726 return backendID; 727 } 728 729 /** 730 * Specifies the unique identifier for this backend. 731 * 732 * @param backendID The unique identifier for this backend. 733 */ 734 public final void setBackendID(String backendID) 735 { 736 this.backendID = backendID; 737 } 738 739 /** 740 * Indicates whether this backend holds private data or user data. 741 * 742 * @return {@code true} if this backend holds private data, or 743 * {@code false} if it holds user data. 744 */ 745 public final boolean isPrivateBackend() 746 { 747 return isPrivateBackend; 748 } 749 750 /** 751 * Specifies whether this backend holds private data or user data. 752 * 753 * @param isPrivateBackend Specifies whether this backend holds 754 * private data or user data. 755 */ 756 public final void setPrivateBackend(boolean isPrivateBackend) 757 { 758 this.isPrivateBackend = isPrivateBackend; 759 } 760 761 /** 762 * Retrieves the writability mode for this backend. 763 * 764 * @return The writability mode for this backend. 765 */ 766 public final WritabilityMode getWritabilityMode() 767 { 768 return writabilityMode; 769 } 770 771 /** 772 * Specifies the writability mode for this backend. 773 * 774 * @param writabilityMode The writability mode for this backend. 775 */ 776 public final void setWritabilityMode(WritabilityMode writabilityMode) 777 { 778 this.writabilityMode = writabilityMode != null ? writabilityMode : WritabilityMode.ENABLED; 779 } 780 781 /** 782 * Retrieves the backend monitor that is associated with this 783 * backend. 784 * 785 * @return The backend monitor that is associated with this 786 * backend, or {@code null} if none has been assigned. 787 */ 788 public final BackendMonitor getBackendMonitor() 789 { 790 return backendMonitor; 791 } 792 793 /** 794 * Registers the provided persistent search operation with this backend so 795 * that it will be notified of any add, delete, modify, or modify DN 796 * operations that are performed. 797 * 798 * @param persistentSearch 799 * The persistent search operation to register with this backend 800 * @throws DirectoryException 801 * If a problem occurs while registering the persistent search 802 */ 803 public void registerPersistentSearch(PersistentSearch persistentSearch) throws DirectoryException 804 { 805 persistentSearches.add(persistentSearch); 806 807 persistentSearch.registerCancellationCallback(new CancellationCallback() 808 { 809 @Override 810 public void persistentSearchCancelled(PersistentSearch psearch) 811 { 812 persistentSearches.remove(psearch); 813 } 814 }); 815 } 816 817 /** 818 * Returns the persistent searches currently active against this local 819 * backend. 820 * 821 * @return the list of persistent searches currently active against this local 822 * backend 823 */ 824 public Queue<PersistentSearch> getPersistentSearches() 825 { 826 return persistentSearches; 827 } 828 829 /** 830 * Sets the backend monitor for this backend. 831 * 832 * @param backendMonitor The backend monitor for this backend. 833 */ 834 public final void setBackendMonitor(BackendMonitor backendMonitor) 835 { 836 this.backendMonitor = backendMonitor; 837 } 838 839 /** 840 * Retrieves the total number of entries contained in this backend, 841 * if that information is available. 842 * 843 * @return The total number of entries contained in this backend, 844 * or -1 if that information is not available. 845 */ 846 public abstract long getEntryCount(); 847 848 /** 849 * Retrieves the parent backend for this backend. 850 * 851 * @return The parent backend for this backend, or {@code null} if 852 * there is none. 853 */ 854 public final Backend<?> getParentBackend() 855 { 856 return parentBackend; 857 } 858 859 /** 860 * Specifies the parent backend for this backend. 861 * 862 * @param parentBackend The parent backend for this backend. 863 */ 864 public final synchronized void setParentBackend(Backend<?> parentBackend) 865 { 866 this.parentBackend = parentBackend; 867 } 868 869 /** 870 * Retrieves the set of subordinate backends for this backend. 871 * 872 * @return The set of subordinate backends for this backend, or an 873 * empty array if none exist. 874 */ 875 public final Backend<?>[] getSubordinateBackends() 876 { 877 return subordinateBackends; 878 } 879 880 /** 881 * Adds the provided backend to the set of subordinate backends for 882 * this backend. 883 * 884 * @param subordinateBackend The backend to add to the set of 885 * subordinate backends for this 886 * backend. 887 */ 888 public final synchronized void addSubordinateBackend(Backend<?> subordinateBackend) 889 { 890 LinkedHashSet<Backend<?>> backendSet = new LinkedHashSet<>(); 891 Collections.addAll(backendSet, subordinateBackends); 892 893 if (backendSet.add(subordinateBackend)) 894 { 895 subordinateBackends = backendSet.toArray(new Backend[backendSet.size()]); 896 } 897 } 898 899 /** 900 * Removes the provided backend from the set of subordinate backends 901 * for this backend. 902 * 903 * @param subordinateBackend The backend to remove from the set of 904 * subordinate backends for this 905 * backend. 906 */ 907 public final synchronized void removeSubordinateBackend(Backend<?> subordinateBackend) 908 { 909 ArrayList<Backend<?>> backendList = new ArrayList<>(subordinateBackends.length); 910 911 boolean found = false; 912 for (Backend<?> b : subordinateBackends) 913 { 914 if (b.equals(subordinateBackend)) 915 { 916 found = true; 917 } 918 else 919 { 920 backendList.add(b); 921 } 922 } 923 924 if (found) 925 { 926 subordinateBackends = backendList.toArray(new Backend[backendList.size()]); 927 } 928 } 929 930 /** 931 * Indicates whether this backend should be used to handle 932 * operations for the provided entry. 933 * 934 * @param entryDN The DN of the entry for which to make the 935 * determination. 936 * 937 * @return {@code true} if this backend handles operations for the 938 * provided entry, or {@code false} if it does not. 939 */ 940 public final boolean handlesEntry(DN entryDN) 941 { 942 for (DN dn : getBaseDNs()) 943 { 944 if (entryDN.isSubordinateOrEqualTo(dn)) 945 { 946 for (Backend<?> b : subordinateBackends) 947 { 948 if (b.handlesEntry(entryDN)) 949 { 950 return false; 951 } 952 } 953 return true; 954 } 955 } 956 return false; 957 } 958 959 /** 960 * Indicates whether a backend should be used to handle operations 961 * for the provided entry given the set of base DNs and exclude DNs. 962 * 963 * @param entryDN The DN of the entry for which to make the 964 * determination. 965 * @param baseDNs The set of base DNs for the backend. 966 * @param excludeDNs The set of DNs that should be excluded from 967 * the backend. 968 * 969 * @return {@code true} if the backend should handle operations for 970 * the provided entry, or {@code false} if it does not. 971 */ 972 public static boolean handlesEntry(DN entryDN, Collection<DN> baseDNs, Collection<DN> excludeDNs) 973 { 974 for (DN baseDN : baseDNs) 975 { 976 if (entryDN.isSubordinateOrEqualTo(baseDN) && !isExcluded(excludeDNs, entryDN)) 977 { 978 return true; 979 } 980 } 981 return false; 982 } 983 984 private static boolean isExcluded(Collection<DN> excludeDNs, DN entryDN) 985 { 986 if (excludeDNs == null || excludeDNs.isEmpty()) 987 { 988 return false; 989 } 990 for (DN excludeDN : excludeDNs) 991 { 992 if (entryDN.isSubordinateOrEqualTo(excludeDN)) 993 { 994 return true; 995 } 996 } 997 return false; 998 } 999}