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