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    }