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