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 2007-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.backends.jeb;
028    import org.opends.messages.Message;
029    
030    import java.io.IOException;
031    import java.io.File;
032    import java.util.concurrent.atomic.AtomicInteger;
033    import java.util.concurrent.locks.Lock;
034    
035    import java.io.FileInputStream;
036    import java.io.FilenameFilter;
037    import java.util.*;
038    import java.util.zip.Adler32;
039    import java.util.zip.CheckedInputStream;
040    
041    import com.sleepycat.je.DatabaseException;
042    import com.sleepycat.je.EnvironmentConfig;
043    import com.sleepycat.je.RunRecoveryException;
044    
045    import org.opends.server.admin.std.meta.LocalDBIndexCfgDefn;
046    import org.opends.server.admin.std.server.MonitorProviderCfg;
047    import org.opends.server.api.Backend;
048    import org.opends.server.api.MonitorProvider;
049    import org.opends.server.api.AlertGenerator;
050    import org.opends.server.config.ConfigException;
051    import org.opends.server.core.AddOperation;
052    import org.opends.server.core.DeleteOperation;
053    import org.opends.server.core.DirectoryServer;
054    import org.opends.server.core.ModifyOperation;
055    import org.opends.server.core.ModifyDNOperation;
056    import org.opends.server.core.SearchOperation;
057    import org.opends.server.util.LDIFException;
058    import org.opends.server.util.Validator;
059    import static org.opends.server.util.StaticUtils.*;
060    
061    import static org.opends.messages.BackendMessages.*;
062    import static org.opends.messages.JebMessages.*;
063    import static org.opends.server.loggers.ErrorLogger.logError;
064    import static org.opends.server.loggers.debug.DebugLogger.*;
065    import org.opends.server.loggers.debug.DebugTracer;
066    import org.opends.server.types.*;
067    import static org.opends.server.util.ServerConstants.*;
068    import org.opends.server.admin.std.server.LocalDBBackendCfg;
069    import org.opends.server.admin.Configuration;
070    import org.opends.server.admin.server.ConfigurationChangeListener;
071    import org.opends.server.types.DN;
072    import org.opends.server.backends.jeb.importLDIF.Importer;
073    
074    /**
075     * This is an implementation of a Directory Server Backend which stores entries
076     * locally in a Berkeley DB JE database.
077     */
078    public class BackendImpl
079        extends Backend
080        implements ConfigurationChangeListener<LocalDBBackendCfg>, AlertGenerator
081    {
082      /**
083       * The tracer object for the debug logger.
084       */
085      private static final DebugTracer TRACER = getTracer();
086    
087    
088      /**
089        * The fully-qualified name of this class.
090        */
091       private static final String CLASS_NAME =
092            "org.opends.server.backends.jeb.BackendImpl";
093    
094    
095      /**
096       * The configuration of this JE backend.
097       */
098      private LocalDBBackendCfg cfg;
099    
100      /**
101       * The root JE container to use for this backend.
102       */
103      private RootContainer rootContainer;
104    
105      /**
106       * A count of the total operation threads currently in the backend.
107       */
108      private AtomicInteger threadTotalCount = new AtomicInteger(0);
109    
110      /**
111       * A count of the write operation threads currently in the backend.
112       */
113      private AtomicInteger threadWriteCount = new AtomicInteger(0);
114    
115      /**
116       * A list of monitor providers created for this backend instance.
117       */
118      private ArrayList<MonitorProvider<?>> monitorProviders =
119          new ArrayList<MonitorProvider<?>>();
120    
121      /**
122       * The base DNs defined for this backend instance.
123       */
124      private DN[] baseDNs;
125    
126      /**
127       * The controls supported by this backend.
128       */
129      private static HashSet<String> supportedControls;
130    
131      static
132      {
133        // Set our supported controls.
134        supportedControls = new HashSet<String>();
135        supportedControls.add(OID_SUBTREE_DELETE_CONTROL);
136        supportedControls.add(OID_PAGED_RESULTS_CONTROL);
137        supportedControls.add(OID_MANAGE_DSAIT_CONTROL);
138        supportedControls.add(OID_SERVER_SIDE_SORT_REQUEST_CONTROL);
139        supportedControls.add(OID_VLV_REQUEST_CONTROL);
140      }
141    
142      /**
143       * The features supported by this backend.
144       */
145      private static HashSet<String> supportedFeatures;
146    
147      static {
148        // Set our supported features.
149        supportedFeatures = new HashSet<String>();
150    
151        //NYI
152      }
153    
154    
155    
156      /**
157       * Begin a Backend API method that reads the database.
158       */
159      private void readerBegin()
160      {
161        threadTotalCount.getAndIncrement();
162      }
163    
164    
165    
166      /**
167       * End a Backend API method that reads the database.
168       */
169      private void readerEnd()
170      {
171        threadTotalCount.getAndDecrement();
172      }
173    
174    
175    
176      /**
177       * Begin a Backend API method that writes the database.
178       */
179      private void writerBegin()
180      {
181        threadTotalCount.getAndIncrement();
182        threadWriteCount.getAndIncrement();
183      }
184    
185    
186    
187      /**
188       * End a Backend API method that writes the database.
189       */
190      private void writerEnd()
191      {
192        threadWriteCount.getAndDecrement();
193        threadTotalCount.getAndDecrement();
194      }
195    
196    
197    
198      /**
199       * Wait until there are no more threads accessing the database. It is assumed
200       * that new threads have been prevented from entering the database at the time
201       * this method is called.
202       */
203      private void waitUntilQuiescent()
204      {
205        while (threadTotalCount.get() > 0)
206        {
207          // Still have threads in the database so sleep a little
208          try
209          {
210            Thread.sleep(500);
211          }
212          catch (InterruptedException e)
213          {
214            if (debugEnabled())
215            {
216              TRACER.debugCaught(DebugLogLevel.ERROR, e);
217            }
218          }
219        }
220      }
221    
222      /**
223       * This method will attempt to checksum the current JE db environment by
224       * computing the Adler-32 checksum on the latest JE log file available.
225       *
226       * @return  The checksum of JE db environment or zero if checksum failed.
227       */
228      private long checksumDbEnv() {
229    
230        File parentDirectory = getFileForPath(cfg.getDBDirectory());
231        File backendDirectory = new File(parentDirectory, cfg.getBackendId());
232    
233        List<File> jdbFiles = new ArrayList<File>();
234        if(backendDirectory.isDirectory())
235        {
236          jdbFiles =
237              Arrays.asList(backendDirectory.listFiles(new FilenameFilter() {
238                public boolean accept(File dir, String name) {
239                  return name.endsWith(".jdb");
240                }
241              }));
242        }
243    
244        if ( !jdbFiles.isEmpty() ) {
245          Collections.sort(jdbFiles, Collections.reverseOrder());
246          FileInputStream fis = null;
247          try {
248            fis = new FileInputStream(jdbFiles.get(0).toString());
249            CheckedInputStream cis = new CheckedInputStream(fis, new Adler32());
250            byte[] tempBuf = new byte[8192];
251            while (cis.read(tempBuf) >= 0) {
252            }
253    
254            return cis.getChecksum().getValue();
255          } catch (Exception e) {
256            if (debugEnabled())
257            {
258              TRACER.debugCaught(DebugLogLevel.ERROR, e);
259            }
260          } finally {
261            if (fis != null) {
262              try {
263                fis.close();
264              } catch (Exception e) {
265                if (debugEnabled())
266                {
267                  TRACER.debugCaught(DebugLogLevel.ERROR, e);
268                }
269              }
270            }
271          }
272        }
273    
274        return 0;
275      }
276    
277    
278    
279      /**
280       * {@inheritDoc}
281       */
282      public void configureBackend(Configuration cfg)
283          throws ConfigException
284      {
285        Validator.ensureNotNull(cfg);
286        Validator.ensureTrue(cfg instanceof LocalDBBackendCfg);
287    
288        this.cfg = (LocalDBBackendCfg)cfg;
289    
290        Set<DN> dnSet = this.cfg.getBaseDN();
291        baseDNs = new DN[dnSet.size()];
292        dnSet.toArray(baseDNs);
293      }
294    
295    
296    
297      /**
298       * {@inheritDoc}
299       */
300      @Override()
301      public void initializeBackend()
302          throws ConfigException, InitializationException
303      {
304        // Checksum this db environment and register its offline state id/checksum.
305        DirectoryServer.registerOfflineBackendStateID(this.getBackendID(),
306                                                      checksumDbEnv());
307    
308        if(rootContainer == null)
309        {
310          EnvironmentConfig envConfig =
311              ConfigurableEnvironment.parseConfigEntry(cfg);
312          envConfig.setLockTimeout(0);
313          rootContainer = initializeRootContainer(envConfig);
314        }
315    
316        // Preload the database cache.
317        rootContainer.preload(cfg.getPreloadTimeLimit());
318    
319        try
320        {
321          // Log an informational message about the number of entries.
322          Message message = NOTE_JEB_BACKEND_STARTED.get(
323              cfg.getBackendId(), rootContainer.getEntryCount());
324          logError(message);
325        }
326        catch(DatabaseException databaseException)
327        {
328          if (debugEnabled())
329          {
330            TRACER.debugCaught(DebugLogLevel.ERROR, databaseException);
331          }
332          Message message =
333              WARN_JEB_GET_ENTRY_COUNT_FAILED.get(databaseException.getMessage());
334          throw new InitializationException(
335                                            message, databaseException);
336        }
337    
338        for (DN dn : cfg.getBaseDN())
339        {
340          try
341          {
342            DirectoryServer.registerBaseDN(dn, this, false);
343          }
344          catch (Exception e)
345          {
346            if (debugEnabled())
347            {
348              TRACER.debugCaught(DebugLogLevel.ERROR, e);
349            }
350    
351            Message message = ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(
352                String.valueOf(dn), String.valueOf(e));
353            throw new InitializationException(message, e);
354          }
355        }
356    
357        // Register a monitor provider for the environment.
358        MonitorProvider<? extends MonitorProviderCfg> monitorProvider =
359            rootContainer.getMonitorProvider();
360        monitorProviders.add(monitorProvider);
361        DirectoryServer.registerMonitorProvider(monitorProvider);
362    
363        //Register as an AlertGenerator.
364        DirectoryServer.registerAlertGenerator(this);
365        // Register this backend as a change listener.
366        cfg.addLocalDBChangeListener(this);
367      }
368    
369    
370    
371      /**
372       * {@inheritDoc}
373       */
374      @Override()
375      public void finalizeBackend()
376      {
377        // Deregister as a change listener.
378        cfg.removeLocalDBChangeListener(this);
379    
380        // Deregister our base DNs.
381        for (DN dn : rootContainer.getBaseDNs())
382        {
383          try
384          {
385            DirectoryServer.deregisterBaseDN(dn);
386          }
387          catch (Exception e)
388          {
389            if (debugEnabled())
390            {
391              TRACER.debugCaught(DebugLogLevel.ERROR, e);
392            }
393          }
394        }
395    
396        // Deregister our monitor providers.
397        for (MonitorProvider<?> monitor : monitorProviders)
398        {
399          DirectoryServer.deregisterMonitorProvider(
400               monitor.getMonitorInstanceName().toLowerCase());
401        }
402        monitorProviders = new ArrayList<MonitorProvider<?>>();
403    
404        // We presume the server will prevent more operations coming into this
405        // backend, but there may be existing operations already in the
406        // backend. We need to wait for them to finish.
407        waitUntilQuiescent();
408    
409        // Close the database.
410        try
411        {
412          rootContainer.close();
413          rootContainer = null;
414        }
415        catch (DatabaseException e)
416        {
417          if (debugEnabled())
418          {
419            TRACER.debugCaught(DebugLogLevel.ERROR, e);
420          }
421          Message message = ERR_JEB_DATABASE_EXCEPTION.get(e.getMessage());
422          logError(message);
423        }
424    
425        // Checksum this db environment and register its offline state id/checksum.
426        DirectoryServer.registerOfflineBackendStateID(this.getBackendID(),
427          checksumDbEnv());
428    
429        //Deregister the alert generator.
430        DirectoryServer.deregisterAlertGenerator(this);
431    
432        // Make sure the thread counts are zero for next initialization.
433        threadTotalCount.set(0);
434        threadWriteCount.set(0);
435    
436        // Log an informational message.
437        Message message = NOTE_BACKEND_OFFLINE.get(cfg.getBackendId());
438        logError(message);
439      }
440    
441    
442    
443      /**
444       * {@inheritDoc}
445       */
446      @Override()
447      public boolean isLocal()
448      {
449        return true;
450      }
451    
452    
453    
454      /**
455       * {@inheritDoc}
456       */
457      @Override()
458      public boolean isIndexed(AttributeType attributeType, IndexType indexType)
459      {
460        try
461        {
462          EntryContainer ec = rootContainer.getEntryContainer(baseDNs[0]);
463          AttributeIndex ai = ec.getAttributeIndex(attributeType);
464          if (ai == null)
465          {
466            return false;
467          }
468    
469          Set<LocalDBIndexCfgDefn.IndexType> indexTypes =
470               ai.getConfiguration().getIndexType();
471          switch (indexType)
472          {
473            case PRESENCE:
474              return indexTypes.contains(LocalDBIndexCfgDefn.IndexType.PRESENCE);
475    
476            case EQUALITY:
477              return indexTypes.contains(LocalDBIndexCfgDefn.IndexType.EQUALITY);
478    
479            case SUBSTRING:
480            case SUBINITIAL:
481            case SUBANY:
482            case SUBFINAL:
483              return indexTypes.contains(LocalDBIndexCfgDefn.IndexType.SUBSTRING);
484    
485            case GREATER_OR_EQUAL:
486            case LESS_OR_EQUAL:
487              return indexTypes.contains(LocalDBIndexCfgDefn.IndexType.ORDERING);
488    
489            case APPROXIMATE:
490              return indexTypes.contains(LocalDBIndexCfgDefn.IndexType.APPROXIMATE);
491    
492            default:
493              return false;
494          }
495        }
496        catch (Exception e)
497        {
498          if (debugEnabled())
499          {
500            TRACER.debugCaught(DebugLogLevel.ERROR, e);
501          }
502    
503          return false;
504        }
505      }
506    
507    
508    
509      /**
510       * {@inheritDoc}
511       */
512      @Override()
513      public boolean supportsLDIFExport()
514      {
515        return true;
516      }
517    
518    
519    
520      /**
521       * {@inheritDoc}
522       */
523      @Override()
524      public boolean supportsLDIFImport()
525      {
526        return true;
527      }
528    
529    
530    
531      /**
532       * {@inheritDoc}
533       */
534      @Override()
535      public boolean supportsBackup()
536      {
537        return true;
538      }
539    
540    
541    
542      /**
543       * {@inheritDoc}
544       */
545      @Override()
546      public boolean supportsBackup(BackupConfig backupConfig,
547                                    StringBuilder unsupportedReason)
548      {
549        return true;
550      }
551    
552    
553    
554      /**
555       * {@inheritDoc}
556       */
557      @Override()
558      public boolean supportsRestore()
559      {
560        return true;
561      }
562    
563    
564    
565      /**
566       * {@inheritDoc}
567       */
568      @Override()
569      public HashSet<String> getSupportedFeatures()
570      {
571        return supportedFeatures;
572      }
573    
574    
575    
576      /**
577       * {@inheritDoc}
578       */
579      @Override()
580      public HashSet<String> getSupportedControls()
581      {
582        return supportedControls;
583      }
584    
585    
586    
587      /**
588       * {@inheritDoc}
589       */
590      @Override()
591      public DN[] getBaseDNs()
592      {
593        return baseDNs;
594      }
595    
596    
597    
598      /**
599       * {@inheritDoc}
600       */
601      @Override()
602      public long getEntryCount()
603      {
604        if (rootContainer != null)
605        {
606          try
607          {
608            return rootContainer.getEntryCount();
609          }
610          catch (Exception e)
611          {
612            if (debugEnabled())
613            {
614              TRACER.debugCaught(DebugLogLevel.ERROR, e);
615            }
616          }
617        }
618    
619        return -1;
620      }
621    
622    
623    
624      /**
625       * {@inheritDoc}
626       */
627      @Override()
628      public ConditionResult hasSubordinates(DN entryDN)
629             throws DirectoryException
630      {
631        long ret = numSubordinates(entryDN, false);
632        if(ret < 0)
633        {
634          return ConditionResult.UNDEFINED;
635        }
636        else if(ret == 0)
637        {
638          return ConditionResult.FALSE;
639        }
640        else
641        {
642          return ConditionResult.TRUE;
643        }
644      }
645    
646    
647    
648      /**
649       * {@inheritDoc}
650       */
651      @Override()
652      public long numSubordinates(DN entryDN, boolean subtree)
653          throws DirectoryException
654      {
655        EntryContainer ec;
656        if (rootContainer != null)
657        {
658          ec = rootContainer.getEntryContainer(entryDN);
659        }
660        else
661        {
662          Message message = ERR_ROOT_CONTAINER_NOT_INITIALIZED.get(getBackendID());
663          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
664                  message);
665        }
666    
667        if(ec == null)
668        {
669          return -1;
670        }
671    
672        readerBegin();
673        ec.sharedLock.lock();
674        try
675        {
676          long count = ec.getNumSubordinates(entryDN, subtree);
677          if(count == Long.MAX_VALUE)
678          {
679            // The index entry limit has exceeded and there is no count maintained.
680            return -1;
681          }
682          return count;
683        }
684        catch (DatabaseException e)
685        {
686          if (debugEnabled())
687          {
688            TRACER.debugCaught(DebugLogLevel.ERROR, e);
689          }
690          throw createDirectoryException(e);
691        }
692        finally
693        {
694          ec.sharedLock.unlock();
695          readerEnd();
696        }
697      }
698    
699    
700    
701      /**
702       * {@inheritDoc}
703       */
704      @Override()
705      public Entry getEntry(DN entryDN) throws DirectoryException
706      {
707        readerBegin();
708    
709        EntryContainer ec;
710        if (rootContainer != null)
711        {
712          ec = rootContainer.getEntryContainer(entryDN);
713        }
714        else
715        {
716          Message message = ERR_ROOT_CONTAINER_NOT_INITIALIZED.get(getBackendID());
717          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
718                  message);
719        }
720    
721        ec.sharedLock.lock();
722        Entry entry;
723        try
724        {
725          entry = ec.getEntry(entryDN);
726        }
727        catch (DatabaseException e)
728        {
729          if (debugEnabled())
730          {
731            TRACER.debugCaught(DebugLogLevel.ERROR, e);
732          }
733          throw createDirectoryException(e);
734        }
735        catch (JebException e)
736        {
737          if (debugEnabled())
738          {
739            TRACER.debugCaught(DebugLogLevel.ERROR, e);
740          }
741          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
742                                       e.getMessageObject());
743        }
744        finally
745        {
746          ec.sharedLock.unlock();
747          readerEnd();
748        }
749    
750        return entry;
751      }
752    
753    
754    
755      /**
756       * {@inheritDoc}
757       */
758      @Override()
759      public void addEntry(Entry entry, AddOperation addOperation)
760          throws DirectoryException
761      {
762        writerBegin();
763        DN entryDN = entry.getDN();
764    
765        EntryContainer ec;
766        if (rootContainer != null)
767        {
768          ec = rootContainer.getEntryContainer(entryDN);
769        }
770        else
771        {
772          Message message = ERR_ROOT_CONTAINER_NOT_INITIALIZED.get(getBackendID());
773          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
774                  message);
775        }
776    
777        ec.sharedLock.lock();
778        try
779        {
780          ec.addEntry(entry, addOperation);
781        }
782        catch (DatabaseException e)
783        {
784          if (debugEnabled())
785          {
786            TRACER.debugCaught(DebugLogLevel.ERROR, e);
787          }
788          throw createDirectoryException(e);
789        }
790        catch (JebException e)
791        {
792          if (debugEnabled())
793          {
794            TRACER.debugCaught(DebugLogLevel.ERROR, e);
795          }
796          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
797                                       e.getMessageObject());
798        }
799        finally
800        {
801          ec.sharedLock.unlock();
802          writerEnd();
803        }
804      }
805    
806    
807    
808      /**
809       * {@inheritDoc}
810       */
811      @Override()
812      public void deleteEntry(DN entryDN, DeleteOperation deleteOperation)
813          throws DirectoryException
814      {
815        writerBegin();
816    
817        EntryContainer ec;
818        if (rootContainer != null)
819        {
820          ec = rootContainer.getEntryContainer(entryDN);
821        }
822        else
823        {
824          Message message = ERR_ROOT_CONTAINER_NOT_INITIALIZED.get(getBackendID());
825          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
826                  message);
827        }
828    
829        ec.sharedLock.lock();
830        try
831        {
832          ec.deleteEntry(entryDN, deleteOperation);
833        }
834        catch (DatabaseException e)
835        {
836          if (debugEnabled())
837          {
838            TRACER.debugCaught(DebugLogLevel.ERROR, e);
839          }
840          throw createDirectoryException(e);
841        }
842        catch (JebException e)
843        {
844          if (debugEnabled())
845          {
846            TRACER.debugCaught(DebugLogLevel.ERROR, e);
847          }
848          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
849                                       e.getMessageObject());
850        }
851        finally
852        {
853          ec.sharedLock.unlock();
854          writerEnd();
855        }
856      }
857    
858    
859    
860      /**
861       * {@inheritDoc}
862       */
863      @Override()
864      public void replaceEntry(Entry entry, ModifyOperation modifyOperation)
865          throws DirectoryException
866      {
867        writerBegin();
868    
869        DN entryDN = entry.getDN();
870        EntryContainer ec;
871        if (rootContainer != null)
872        {
873          ec = rootContainer.getEntryContainer(entryDN);
874        }
875        else
876        {
877          Message message = ERR_ROOT_CONTAINER_NOT_INITIALIZED.get(getBackendID());
878          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
879                  message);
880        }
881    
882        ec.sharedLock.lock();
883    
884        try
885        {
886          ec.replaceEntry(entry, modifyOperation);
887        }
888        catch (DatabaseException e)
889        {
890          if (debugEnabled())
891          {
892            TRACER.debugCaught(DebugLogLevel.ERROR, e);
893          }
894          throw createDirectoryException(e);
895        }
896        catch (JebException e)
897        {
898          if (debugEnabled())
899          {
900            TRACER.debugCaught(DebugLogLevel.ERROR, e);
901          }
902          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
903                                       e.getMessageObject());
904        }
905        finally
906        {
907          ec.sharedLock.unlock();
908          writerEnd();
909        }
910      }
911    
912    
913    
914      /**
915       * {@inheritDoc}
916       */
917      @Override()
918      public void renameEntry(DN currentDN, Entry entry,
919                              ModifyDNOperation modifyDNOperation)
920          throws DirectoryException, CanceledOperationException {
921        writerBegin();
922    
923        EntryContainer currentContainer;
924        if (rootContainer != null)
925        {
926          currentContainer = rootContainer.getEntryContainer(currentDN);
927        }
928        else
929        {
930          Message message = ERR_ROOT_CONTAINER_NOT_INITIALIZED.get(getBackendID());
931          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
932                  message);
933        }
934    
935        EntryContainer container = rootContainer.getEntryContainer(entry.getDN());
936    
937        if (currentContainer != container)
938        {
939          // FIXME: No reason why we cannot implement a move between containers
940          // since the containers share the same database environment.
941          Message msg = WARN_JEB_FUNCTION_NOT_SUPPORTED.get();
942          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
943                                       msg);
944        }
945    
946        Lock containerLock = currentContainer.sharedLock;
947        try
948        {
949          containerLock.lock();
950    
951          if(currentContainer.getNumSubordinates(currentDN, true) >
952             currentContainer.getSubtreeDeleteBatchSize())
953          {
954            containerLock.unlock();
955            containerLock = currentContainer.exclusiveLock;
956            containerLock.lock();
957          }
958    
959          currentContainer.renameEntry(currentDN, entry, modifyDNOperation);
960        }
961        catch (DatabaseException e)
962        {
963          if (debugEnabled())
964          {
965            TRACER.debugCaught(DebugLogLevel.ERROR, e);
966          }
967          throw createDirectoryException(e);
968        }
969        catch (JebException e)
970        {
971          if (debugEnabled())
972          {
973            TRACER.debugCaught(DebugLogLevel.ERROR, e);
974          }
975          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
976                                       e.getMessageObject());
977        }
978        finally
979        {
980          containerLock.unlock();
981          writerEnd();
982        }
983      }
984    
985    
986    
987      /**
988       * {@inheritDoc}
989       */
990      @Override()
991      public void search(SearchOperation searchOperation)
992          throws DirectoryException
993      {
994        readerBegin();
995    
996        EntryContainer ec;
997        if (rootContainer != null)
998        {
999          ec = rootContainer.getEntryContainer(searchOperation.getBaseDN());
1000        }
1001        else
1002        {
1003          Message message = ERR_ROOT_CONTAINER_NOT_INITIALIZED.get(getBackendID());
1004          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1005                  message);
1006        }
1007        ec.sharedLock.lock();
1008    
1009        try
1010        {
1011          ec.search(searchOperation);
1012        }
1013        catch (DatabaseException e)
1014        {
1015          if (debugEnabled())
1016          {
1017            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1018          }
1019          throw createDirectoryException(e);
1020        }
1021        catch (JebException e)
1022        {
1023          if (debugEnabled())
1024          {
1025            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1026          }
1027          Message message = ERR_JEB_DATABASE_EXCEPTION.get(e.getMessage());
1028          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1029                                       message);
1030        }
1031        finally
1032        {
1033          ec.sharedLock.unlock();
1034          readerEnd();
1035        }
1036      }
1037    
1038    
1039    
1040      /**
1041       * {@inheritDoc}
1042       */
1043      @Override()
1044      public void exportLDIF(LDIFExportConfig exportConfig)
1045          throws DirectoryException
1046      {
1047        // If the backend already has the root container open, we must use the same
1048        // underlying root container
1049        boolean openRootContainer = rootContainer == null;
1050    
1051        try
1052        {
1053          if(openRootContainer)
1054          {
1055            EnvironmentConfig envConfig =
1056                ConfigurableEnvironment.parseConfigEntry(cfg);
1057    
1058            envConfig.setReadOnly(true);
1059            envConfig.setAllowCreate(false);
1060            envConfig.setTransactional(false);
1061            envConfig.setTxnNoSync(false);
1062            envConfig.setConfigParam("je.env.isLocking", "true");
1063            envConfig.setConfigParam("je.env.runCheckpointer", "true");
1064    
1065            rootContainer = initializeRootContainer(envConfig);
1066          }
1067    
1068    
1069          ExportJob exportJob = new ExportJob(exportConfig);
1070          exportJob.exportLDIF(rootContainer);
1071        }
1072        catch (IOException ioe)
1073        {
1074          if (debugEnabled())
1075          {
1076            TRACER.debugCaught(DebugLogLevel.ERROR, ioe);
1077          }
1078          Message message = ERR_JEB_IO_ERROR.get(ioe.getMessage());
1079          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1080                                       message);
1081        }
1082        catch (JebException je)
1083        {
1084          if (debugEnabled())
1085          {
1086            TRACER.debugCaught(DebugLogLevel.ERROR, je);
1087          }
1088          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1089                                       je.getMessageObject());
1090        }
1091        catch (DatabaseException de)
1092        {
1093          if (debugEnabled())
1094          {
1095            TRACER.debugCaught(DebugLogLevel.ERROR, de);
1096          }
1097          throw createDirectoryException(de);
1098        }
1099        catch (LDIFException e)
1100        {
1101          if (debugEnabled())
1102          {
1103            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1104          }
1105          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1106                                       e.getMessageObject());
1107        }
1108        catch (InitializationException ie)
1109        {
1110          if (debugEnabled())
1111          {
1112            TRACER.debugCaught(DebugLogLevel.ERROR, ie);
1113          }
1114          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1115                                       ie.getMessageObject());
1116        }
1117        catch (ConfigException ce)
1118        {
1119          if (debugEnabled())
1120          {
1121            TRACER.debugCaught(DebugLogLevel.ERROR, ce);
1122          }
1123          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1124                                       ce.getMessageObject());
1125        }
1126        finally
1127        {
1128          //If a root container was opened in this method as read only, close it
1129          //to leave the backend in the same state.
1130          if (openRootContainer && rootContainer != null)
1131          {
1132            try
1133            {
1134              rootContainer.close();
1135              rootContainer = null;
1136            }
1137            catch (DatabaseException e)
1138            {
1139              if (debugEnabled())
1140              {
1141                TRACER.debugCaught(DebugLogLevel.ERROR, e);
1142              }
1143            }
1144          }
1145        }
1146      }
1147    
1148    
1149    
1150      /**
1151       * {@inheritDoc}
1152       */
1153      @Override()
1154      public LDIFImportResult importLDIF(LDIFImportConfig importConfig)
1155          throws DirectoryException
1156      {
1157        // If the backend already has the root container open, we must use the same
1158        // underlying root container
1159        boolean openRootContainer = rootContainer == null;
1160    
1161        // If the rootContainer is open, the backend is initialized by something
1162        // else.
1163        // We can't do import while the backend is online.
1164        if(!openRootContainer)
1165        {
1166          Message message = ERR_JEB_IMPORT_BACKEND_ONLINE.get();
1167          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1168                                       message);
1169        }
1170    
1171        try
1172        {
1173          EnvironmentConfig envConfig =
1174                  ConfigurableEnvironment.parseConfigEntry(cfg);
1175          if(!importConfig.appendToExistingData()) {
1176            if(importConfig.clearBackend() || cfg.getBaseDN().size() <= 1) {
1177              // We have the writer lock on the environment, now delete the
1178              // environment and re-open it. Only do this when we are
1179              // importing to all the base DNs in the backend or if the backend only
1180              // have one base DN.
1181              File parentDirectory = getFileForPath(cfg.getDBDirectory());
1182              File backendDirectory = new File(parentDirectory, cfg.getBackendId());
1183              // If the backend does not exist the import will create it.
1184              if (backendDirectory.exists()) {
1185                EnvManager.removeFiles(backendDirectory.getPath());
1186              }
1187            }
1188          }
1189          envConfig.setReadOnly(false);
1190          envConfig.setAllowCreate(true);
1191          envConfig.setTransactional(false);
1192          envConfig.setTxnNoSync(false);
1193          envConfig.setConfigParam("je.env.isLocking", "false");
1194          envConfig.setConfigParam("je.env.runCheckpointer", "false");
1195          Importer importer = new Importer(importConfig);
1196          envConfig.setConfigParam("je.maxMemory", importer.getDBCacheSize());
1197          rootContainer = initializeRootContainer(envConfig);
1198          return importer.processImport(rootContainer);
1199        }
1200        catch (IOException ioe)
1201        {
1202          if (debugEnabled())
1203          {
1204            TRACER.debugCaught(DebugLogLevel.ERROR, ioe);
1205          }
1206          Message message = ERR_JEB_IO_ERROR.get(ioe.getMessage());
1207          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1208                                       message);
1209        }
1210        catch (JebException je)
1211        {
1212          if (debugEnabled())
1213          {
1214            TRACER.debugCaught(DebugLogLevel.ERROR, je);
1215          }
1216          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1217                                       je.getMessageObject());
1218        }
1219        catch (DatabaseException de)
1220        {
1221          if (debugEnabled())
1222          {
1223            TRACER.debugCaught(DebugLogLevel.ERROR, de);
1224          }
1225          throw createDirectoryException(de);
1226        }
1227        catch (InitializationException ie)
1228        {
1229          if (debugEnabled())
1230          {
1231            TRACER.debugCaught(DebugLogLevel.ERROR, ie);
1232          }
1233          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1234                                       ie.getMessageObject());
1235        }
1236        catch (ConfigException ce)
1237        {
1238          if (debugEnabled())
1239          {
1240            TRACER.debugCaught(DebugLogLevel.ERROR, ce);
1241          }
1242          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1243                                       ce.getMessageObject());
1244        }
1245        finally
1246        {
1247          // leave the backend in the same state.
1248          try
1249          {
1250            if (rootContainer != null)
1251            {
1252              long startTime = System.currentTimeMillis();
1253              rootContainer.close();
1254              long finishTime = System.currentTimeMillis();
1255              long closeTime = (finishTime - startTime) / 1000;
1256              Message msg =
1257                           NOTE_JEB_IMPORT_LDIF_ROOTCONTAINER_CLOSE.get(closeTime);
1258              logError(msg);
1259              rootContainer = null;
1260            }
1261    
1262            // Sync the environment to disk.
1263            if (debugEnabled())
1264            {
1265              Message message = NOTE_JEB_IMPORT_CLOSING_DATABASE.get();
1266              TRACER.debugInfo(message.toString());
1267            }
1268          }
1269          catch (DatabaseException de)
1270          {
1271            if (debugEnabled())
1272            {
1273              TRACER.debugCaught(DebugLogLevel.ERROR, de);
1274            }
1275          }
1276        }
1277      }
1278    
1279    
1280    
1281      /**
1282       * Verify the integrity of the backend instance.
1283       * @param verifyConfig The verify configuration.
1284       * @param statEntry Optional entry to save stats into.
1285       * @return The error count.
1286       * @throws  ConfigException  If an unrecoverable problem arises during
1287       *                           initialization.
1288       * @throws  InitializationException  If a problem occurs during initialization
1289       *                                   that is not related to the server
1290       *                                   configuration.
1291       * @throws DirectoryException If a Directory Server error occurs.
1292       */
1293      public long verifyBackend(VerifyConfig verifyConfig, Entry statEntry)
1294          throws InitializationException, ConfigException, DirectoryException
1295      {
1296        // If the backend already has the root container open, we must use the same
1297        // underlying root container
1298        boolean openRootContainer = rootContainer == null;
1299        long errorCount = 0 ;
1300    
1301        try
1302        {
1303          if(openRootContainer)
1304          {
1305            EnvironmentConfig envConfig =
1306                ConfigurableEnvironment.parseConfigEntry(cfg);
1307    
1308            envConfig.setReadOnly(true);
1309            envConfig.setAllowCreate(false);
1310            envConfig.setTransactional(false);
1311            envConfig.setTxnNoSync(false);
1312            envConfig.setConfigParam("je.env.isLocking", "true");
1313            envConfig.setConfigParam("je.env.runCheckpointer", "true");
1314    
1315            rootContainer = initializeRootContainer(envConfig);
1316          }
1317    
1318          VerifyJob verifyJob = new VerifyJob(verifyConfig);
1319          errorCount = verifyJob.verifyBackend(rootContainer, statEntry);
1320        }
1321        catch (DatabaseException e)
1322        {
1323          if (debugEnabled())
1324          {
1325            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1326          }
1327          throw createDirectoryException(e);
1328        }
1329        catch (JebException e)
1330        {
1331          if (debugEnabled())
1332          {
1333            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1334          }
1335          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1336                                       e.getMessageObject());
1337        }
1338        finally
1339        {
1340          //If a root container was opened in this method as read only, close it
1341          //to leave the backend in the same state.
1342          if (openRootContainer && rootContainer != null)
1343          {
1344            try
1345            {
1346              rootContainer.close();
1347              rootContainer = null;
1348            }
1349            catch (DatabaseException e)
1350            {
1351              if (debugEnabled())
1352              {
1353                TRACER.debugCaught(DebugLogLevel.ERROR, e);
1354              }
1355            }
1356          }
1357        }
1358        return errorCount;
1359      }
1360    
1361    
1362      /**
1363       * Rebuild index(es) in the backend instance. Note that the server will not
1364       * explicitly initialize this backend before calling this method.
1365       * @param rebuildConfig The rebuild configuration.
1366       * @throws  ConfigException  If an unrecoverable problem arises during
1367       *                           initialization.
1368       * @throws  InitializationException  If a problem occurs during initialization
1369       *                                   that is not related to the server
1370       *                                   configuration.
1371       * @throws DirectoryException If a Directory Server error occurs.
1372       */
1373      public void rebuildBackend(RebuildConfig rebuildConfig)
1374          throws InitializationException, ConfigException, DirectoryException
1375      {
1376        // If the backend already has the root container open, we must use the same
1377        // underlying root container
1378        boolean openRootContainer = rootContainer == null;
1379    
1380        // If the rootContainer is open, the backend is initialized by something
1381        // else.
1382        // We can't do any rebuild of system indexes while others are using this
1383        // backend. Throw error. TODO: Need to make baseDNs disablable.
1384        if(!openRootContainer && rebuildConfig.includesSystemIndex())
1385        {
1386          Message message = ERR_JEB_REBUILD_BACKEND_ONLINE.get();
1387          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1388                                       message);
1389        }
1390    
1391        try
1392        {
1393          if (openRootContainer)
1394          {
1395            EnvironmentConfig envConfig =
1396                ConfigurableEnvironment.parseConfigEntry(cfg);
1397    
1398            rootContainer = initializeRootContainer(envConfig);
1399          }
1400    
1401          RebuildJob rebuildJob = new RebuildJob(rebuildConfig);
1402          rebuildJob.rebuildBackend(rootContainer);
1403        }
1404        catch (DatabaseException e)
1405        {
1406          if (debugEnabled())
1407          {
1408            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1409          }
1410          throw createDirectoryException(e);
1411        }
1412        catch (JebException e)
1413        {
1414          if (debugEnabled())
1415          {
1416            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1417          }
1418          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1419                                       e.getMessageObject());
1420        }
1421        finally
1422        {
1423          //If a root container was opened in this method as read only, close it
1424          //to leave the backend in the same state.
1425          if (openRootContainer && rootContainer != null)
1426          {
1427            try
1428            {
1429              rootContainer.close();
1430              rootContainer = null;
1431            }
1432            catch (DatabaseException e)
1433            {
1434              if (debugEnabled())
1435              {
1436                TRACER.debugCaught(DebugLogLevel.ERROR, e);
1437              }
1438            }
1439          }
1440        }
1441      }
1442    
1443    
1444    
1445      /**
1446       * {@inheritDoc}
1447       */
1448      @Override()
1449      public void createBackup(BackupConfig backupConfig)
1450          throws DirectoryException
1451      {
1452        BackupManager backupManager =
1453            new BackupManager(getBackendID());
1454        File parentDir = getFileForPath(cfg.getDBDirectory());
1455        File backendDir = new File(parentDir, cfg.getBackendId());
1456        backupManager.createBackup(backendDir, backupConfig);
1457      }
1458    
1459    
1460    
1461      /**
1462       * {@inheritDoc}
1463       */
1464      @Override()
1465      public void removeBackup(BackupDirectory backupDirectory, String backupID)
1466          throws DirectoryException
1467      {
1468        BackupManager backupManager =
1469            new BackupManager(getBackendID());
1470        backupManager.removeBackup(backupDirectory, backupID);
1471      }
1472    
1473    
1474    
1475      /**
1476       * {@inheritDoc}
1477       */
1478      @Override()
1479      public void restoreBackup(RestoreConfig restoreConfig)
1480          throws DirectoryException
1481      {
1482        BackupManager backupManager =
1483            new BackupManager(getBackendID());
1484        File parentDir = getFileForPath(cfg.getDBDirectory());
1485        File backendDir = new File(parentDir, cfg.getBackendId());
1486        backupManager.restoreBackup(backendDir, restoreConfig);
1487      }
1488    
1489    
1490    
1491      /**
1492       * {@inheritDoc}
1493       */
1494      @Override()
1495      public boolean isConfigurationAcceptable(Configuration configuration,
1496                                               List<Message> unacceptableReasons)
1497      {
1498        LocalDBBackendCfg config = (LocalDBBackendCfg) configuration;
1499        return isConfigurationChangeAcceptable(config, unacceptableReasons);
1500      }
1501    
1502    
1503    
1504      /**
1505       * {@inheritDoc}
1506       */
1507      public boolean isConfigurationChangeAcceptable(
1508          LocalDBBackendCfg cfg,
1509          List<Message> unacceptableReasons)
1510      {
1511        // Make sure that the logging level value is acceptable.
1512        String loggingLevel = cfg.getDBLoggingLevel();
1513        if (! (loggingLevel.equals("OFF") ||
1514               loggingLevel.equals("SEVERE") ||
1515               loggingLevel.equals("WARNING") ||
1516               loggingLevel.equals("INFORMATION") ||
1517               loggingLevel.equals("CONFIG") ||
1518               loggingLevel.equals("FINE") ||
1519               loggingLevel.equals("FINER") ||
1520               loggingLevel.equals("FINEST") ||
1521               loggingLevel.equals("OFF")))
1522        {
1523    
1524          Message message = ERR_JEB_INVALID_LOGGING_LEVEL.get(
1525                  String.valueOf(cfg.getDBLoggingLevel()),
1526                  String.valueOf(cfg.dn()));
1527          unacceptableReasons.add(message);
1528          return false;
1529        }
1530    
1531        return true;
1532      }
1533    
1534    
1535    
1536      /**
1537       * {@inheritDoc}
1538       */
1539      public ConfigChangeResult applyConfigurationChange(LocalDBBackendCfg newCfg)
1540      {
1541        ConfigChangeResult ccr;
1542        ResultCode resultCode = ResultCode.SUCCESS;
1543        ArrayList<Message> messages = new ArrayList<Message>();
1544    
1545    
1546        try
1547        {
1548          if(rootContainer != null)
1549          {
1550            DN[] newBaseDNs = new DN[newCfg.getBaseDN().size()];
1551            newBaseDNs = newCfg.getBaseDN().toArray(newBaseDNs);
1552    
1553            // Check for changes to the base DNs.
1554            for (DN baseDN : cfg.getBaseDN())
1555            {
1556              boolean found = false;
1557              for (DN dn : newBaseDNs)
1558              {
1559                if (dn.equals(baseDN))
1560                {
1561                  found = true;
1562                }
1563              }
1564              if (!found)
1565              {
1566                // The base DN was deleted.
1567                DirectoryServer.deregisterBaseDN(baseDN);
1568                EntryContainer ec =
1569                    rootContainer.unregisterEntryContainer(baseDN);
1570                ec.delete();
1571              }
1572            }
1573    
1574            for (DN baseDN : newBaseDNs)
1575            {
1576              if (!rootContainer.getBaseDNs().contains(baseDN))
1577              {
1578                try
1579                {
1580                  // The base DN was added.
1581                  EntryContainer ec =
1582                      rootContainer.openEntryContainer(baseDN, null);
1583                  rootContainer.registerEntryContainer(baseDN, ec);
1584                  DirectoryServer.registerBaseDN(baseDN, this, false);
1585                }
1586                catch (Exception e)
1587                {
1588                  if (debugEnabled())
1589                  {
1590                    TRACER.debugCaught(DebugLogLevel.ERROR, e);
1591                  }
1592    
1593                  resultCode = DirectoryServer.getServerErrorResultCode();
1594    
1595    
1596                  messages.add(ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(
1597                          String.valueOf(baseDN),
1598                          String.valueOf(e)));
1599                  ccr = new ConfigChangeResult(resultCode, false, messages);
1600                  return ccr;
1601                }
1602              }
1603            }
1604    
1605            baseDNs = newBaseDNs;
1606          }
1607          // Put the new configuration in place.
1608          this.cfg = newCfg;
1609        }
1610        catch (Exception e)
1611        {
1612          messages.add(Message.raw(stackTraceToSingleLineString(e)));
1613          ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
1614                                       false, messages);
1615          return ccr;
1616        }
1617    
1618        ccr = new ConfigChangeResult(resultCode, false, messages);
1619        return ccr;
1620      }
1621    
1622      /**
1623       * Returns a handle to the JE root container currently used by this backend.
1624       * The rootContainer could be NULL if the backend is not initialized.
1625       *
1626       * @return The RootContainer object currently used by this backend.
1627       */
1628      public RootContainer getRootContainer()
1629      {
1630        return rootContainer;
1631      }
1632    
1633      /**
1634       * Returns a new read-only handle to the JE root container for this backend.
1635       * The caller is responsible for closing the root container after use.
1636       *
1637       * @return The read-only RootContainer object for this backend.
1638       *
1639       * @throws  ConfigException  If an unrecoverable problem arises during
1640       *                           initialization.
1641       * @throws  InitializationException  If a problem occurs during initialization
1642       *                                   that is not related to the server
1643       *                                   configuration.
1644       */
1645      public RootContainer getReadOnlyRootContainer()
1646          throws ConfigException, InitializationException
1647      {
1648        EnvironmentConfig envConfig =
1649            ConfigurableEnvironment.parseConfigEntry(cfg);
1650    
1651        envConfig.setReadOnly(true);
1652        envConfig.setAllowCreate(false);
1653        envConfig.setTransactional(false);
1654        envConfig.setTxnNoSync(false);
1655        envConfig.setConfigParam("je.env.isLocking", "true");
1656        envConfig.setConfigParam("je.env.runCheckpointer", "true");
1657    
1658        return initializeRootContainer(envConfig);
1659      }
1660    
1661      /**
1662       * Clears all the entries from the backend.  This method is for test cases
1663       * that use the JE backend.
1664       *
1665       * @throws  ConfigException  If an unrecoverable problem arises in the
1666       *                           process of performing the initialization.
1667       *
1668       * @throws  JebException     If an error occurs while removing the data.
1669       */
1670      public void clearBackend()
1671          throws ConfigException, JebException
1672      {
1673        // Determine the backend database directory.
1674        File parentDirectory = getFileForPath(cfg.getDBDirectory());
1675        File backendDirectory = new File(parentDirectory, cfg.getBackendId());
1676        EnvManager.removeFiles(backendDirectory.getPath());
1677      }
1678    
1679      /**
1680       * Creates a customized DirectoryException from the DatabaseException thrown
1681       * by JE backend.
1682       *
1683       * @param  e The DatabaseException to be converted.
1684       * @return  DirectoryException created from exception.
1685       */
1686      DirectoryException createDirectoryException(DatabaseException e)
1687      {
1688        ResultCode resultCode = DirectoryServer.getServerErrorResultCode();
1689        Message message = null;
1690        if(e instanceof RunRecoveryException)
1691        {
1692          message = NOTE_BACKEND_ENVIRONMENT_UNUSABLE.get(getBackendID());
1693          logError(message);
1694          DirectoryServer.sendAlertNotification(DirectoryServer.getInstance(),
1695                  ALERT_TYPE_BACKEND_ENVIRONMENT_UNUSABLE, message);
1696        }
1697    
1698        String jeMessage = e.getMessage();
1699        if (jeMessage == null)
1700        {
1701          jeMessage = stackTraceToSingleLineString(e);
1702        }
1703        message = ERR_JEB_DATABASE_EXCEPTION.get(jeMessage);
1704        return new DirectoryException(resultCode, message, e);
1705      }
1706    
1707      /**
1708       * {@inheritDoc}
1709       */
1710      public String getClassName()
1711      {
1712        return CLASS_NAME;
1713      }
1714    
1715      /**
1716       * {@inheritDoc}
1717       */
1718      public LinkedHashMap<String,String> getAlerts()
1719      {
1720        LinkedHashMap<String,String> alerts = new LinkedHashMap<String,String>();
1721    
1722        alerts.put(ALERT_TYPE_BACKEND_ENVIRONMENT_UNUSABLE,
1723                   ALERT_DESCRIPTION_BACKEND_ENVIRONMENT_UNUSABLE);
1724        return alerts;
1725      }
1726    
1727      /**
1728       * {@inheritDoc}
1729       */
1730      public DN getComponentEntryDN()
1731      {
1732        return cfg.dn();
1733      }
1734    
1735      private RootContainer initializeRootContainer(EnvironmentConfig envConfig)
1736          throws ConfigException, InitializationException
1737      {
1738        // Open the database environment
1739        try
1740        {
1741          RootContainer rc = new RootContainer(this, cfg);
1742          rc.open(envConfig);
1743          return rc;
1744        }
1745        catch (DatabaseException e)
1746        {
1747          if (debugEnabled())
1748          {
1749            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1750          }
1751          Message message = ERR_JEB_OPEN_ENV_FAIL.get(e.getMessage());
1752          throw new InitializationException(message, e);
1753        }
1754      }
1755    
1756      /**
1757       * {@inheritDoc}
1758       */
1759      public void preloadEntryCache() throws
1760        UnsupportedOperationException
1761      {
1762        EntryCachePreloader preloader =
1763          new EntryCachePreloader(this);
1764        preloader.preload();
1765      }
1766    }