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