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.backends;
028    
029    
030    
031    import java.util.HashMap;
032    import java.util.HashSet;
033    import java.util.LinkedHashMap;
034    import java.util.LinkedList;
035    import java.util.Set;
036    
037    import org.opends.messages.Message;
038    import org.opends.server.admin.Configuration;
039    import org.opends.server.admin.std.server.MemoryBackendCfg;
040    import org.opends.server.api.Backend;
041    import org.opends.server.config.ConfigException;
042    import org.opends.server.core.AddOperation;
043    import org.opends.server.core.DeleteOperation;
044    import org.opends.server.core.DirectoryServer;
045    import org.opends.server.core.ModifyOperation;
046    import org.opends.server.core.ModifyDNOperation;
047    import org.opends.server.core.SearchOperation;
048    import org.opends.server.loggers.debug.DebugTracer;
049    import org.opends.server.types.AttributeType;
050    import org.opends.server.types.BackupConfig;
051    import org.opends.server.types.BackupDirectory;
052    import org.opends.server.types.ConditionResult;
053    import org.opends.server.types.Control;
054    import org.opends.server.types.DebugLogLevel;
055    import org.opends.server.types.DirectoryException;
056    import org.opends.server.types.DN;
057    import org.opends.server.types.Entry;
058    import org.opends.server.types.IndexType;
059    import org.opends.server.types.InitializationException;
060    import org.opends.server.types.LDIFExportConfig;
061    import org.opends.server.types.LDIFImportConfig;
062    import org.opends.server.types.LDIFImportResult;
063    import org.opends.server.types.RestoreConfig;
064    import org.opends.server.types.ResultCode;
065    import org.opends.server.types.SearchFilter;
066    import org.opends.server.types.SearchScope;
067    import org.opends.server.util.LDIFException;
068    import org.opends.server.util.LDIFReader;
069    import org.opends.server.util.LDIFWriter;
070    import org.opends.server.util.Validator;
071    
072    import static org.opends.messages.BackendMessages.*;
073    import static org.opends.server.loggers.debug.DebugLogger.*;
074    import static org.opends.server.util.ServerConstants.*;
075    import static org.opends.server.util.StaticUtils.*;
076    
077    
078    
079    /**
080     * This class defines a very simple backend that stores its information in
081     * memory.  This is primarily intended for testing purposes with small data
082     * sets, as it does not have any indexing mechanism such as would be required to
083     * achieve high performance with large data sets.  It is also heavily
084     * synchronized for simplicity at the expense of performance, rather than
085     * providing a more fine-grained locking mechanism.
086     * <BR><BR>
087     * Entries stored in this backend are held in a
088     * <CODE>LinkedHashMap&lt;DN,Entry&gt;</CODE> object, which ensures that the
089     * order in which you iterate over the entries is the same as the order in which
090     * they were inserted.  By combining this with the constraint that no entry can
091     * be added before its parent, you can ensure that iterating through the entries
092     * will always process the parent entries before their children, which is
093     * important for both search result processing and LDIF exports.
094     * <BR><BR>
095     * As mentioned above, no data indexing is performed, so all non-baseObject
096     * searches require iteration through the entire data set.  If this is to become
097     * a more general-purpose backend, then additional
098     * <CODE>HashMap&lt;ByteString,Set&lt;DN&gt;&gt;</CODE> objects could be used
099     * to provide that capability.
100     * <BR><BR>
101     * There is actually one index that does get maintained within this backend,
102     * which is a mapping between the DN of an entry and the DNs of any immediate
103     * children of that entry.  This is needed to efficiently determine whether an
104     * entry has any children (which must not be the case for delete operations).
105     * Modify DN operations are not currently allowed, but if such support is added
106     * in the future, then this mapping would play an integral role in that process
107     * as well.
108     */
109    public class MemoryBackend
110           extends Backend
111    {
112      /**
113       * The tracer object for the debug logger.
114       */
115      private static final DebugTracer TRACER = getTracer();
116    
117    
118    
119      // The base DNs for this backend.
120      private DN[] baseDNs;
121    
122      // The mapping between parent DNs and their immediate children.
123      private HashMap<DN,HashSet<DN>> childDNs;
124    
125      // The base DNs for this backend, in a hash set.
126      private HashSet<DN> baseDNSet;
127    
128      // The set of supported controls for this backend.
129      private HashSet<String> supportedControls;
130    
131      // The set of supported features for this backend.
132      private HashSet<String> supportedFeatures;
133    
134      // The mapping between entry DNs and the corresponding entries.
135      private LinkedHashMap<DN,Entry> entryMap;
136    
137    
138    
139      /**
140       * Creates a new backend with the provided information.  All backend
141       * implementations must implement a default constructor that use
142       * <CODE>super()</CODE> to invoke this constructor.
143       */
144      public MemoryBackend()
145      {
146        super();
147    
148        // Perform all initialization in initializeBackend.
149      }
150    
151    
152      /**
153       * Set the base DNs for this backend.  This is used by the unit tests
154       * to set the base DNs without having to provide a configuration
155       * object when initializing the backend.
156       * @param baseDNs The set of base DNs to be served by this memory backend.
157       */
158      public void setBaseDNs(DN[] baseDNs)
159      {
160        this.baseDNs = baseDNs;
161      }
162    
163    
164      /**
165       * {@inheritDoc}
166       */
167      @Override()
168      public void configureBackend(Configuration config)
169             throws ConfigException
170      {
171        if (config != null)
172        {
173          Validator.ensureTrue(config instanceof MemoryBackendCfg);
174          MemoryBackendCfg cfg = (MemoryBackendCfg)config;
175          DN[] baseDNs = new DN[cfg.getBaseDN().size()];
176          cfg.getBaseDN().toArray(baseDNs);
177          setBaseDNs(baseDNs);
178        }
179      }
180    
181      /**
182       * {@inheritDoc}
183       */
184      @Override()
185      public synchronized void initializeBackend()
186           throws ConfigException, InitializationException
187      {
188        // We won't support anything other than exactly one base DN in this
189        // implementation.  If we were to add such support in the future, we would
190        // likely want to separate the data for each base DN into a separate entry
191        // map.
192        if ((baseDNs == null) || (baseDNs.length != 1))
193        {
194          Message message = ERR_MEMORYBACKEND_REQUIRE_EXACTLY_ONE_BASE.get();
195          throw new ConfigException(message);
196        }
197    
198        baseDNSet = new HashSet<DN>();
199        for (DN dn : baseDNs)
200        {
201          baseDNSet.add(dn);
202        }
203    
204        entryMap = new LinkedHashMap<DN,Entry>();
205        childDNs = new HashMap<DN,HashSet<DN>>();
206    
207        supportedControls = new HashSet<String>();
208        supportedControls.add(OID_SUBTREE_DELETE_CONTROL);
209    
210        supportedFeatures = new HashSet<String>();
211    
212        for (DN dn : baseDNs)
213        {
214          try
215          {
216            DirectoryServer.registerBaseDN(dn, this, false);
217          }
218          catch (Exception e)
219          {
220            if (debugEnabled())
221            {
222              TRACER.debugCaught(DebugLogLevel.ERROR, e);
223            }
224    
225            Message message = ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(
226                dn.toString(), getExceptionMessage(e));
227            throw new InitializationException(message, e);
228          }
229        }
230      }
231    
232    
233    
234      /**
235       * Removes any data that may have been stored in this backend.
236       */
237      public synchronized void clearMemoryBackend()
238      {
239        entryMap.clear();
240        childDNs.clear();
241      }
242    
243    
244    
245      /**
246       * {@inheritDoc}
247       */
248      @Override()
249      public synchronized void finalizeBackend()
250      {
251        clearMemoryBackend();
252    
253        for (DN dn : baseDNs)
254        {
255          try
256          {
257            DirectoryServer.deregisterBaseDN(dn);
258          }
259          catch (Exception e)
260          {
261            if (debugEnabled())
262            {
263              TRACER.debugCaught(DebugLogLevel.ERROR, e);
264            }
265          }
266        }
267      }
268    
269    
270    
271      /**
272       * {@inheritDoc}
273       */
274      @Override()
275      public DN[] getBaseDNs()
276      {
277        return baseDNs;
278      }
279    
280    
281    
282      /**
283       * {@inheritDoc}
284       */
285      @Override()
286      public synchronized long getEntryCount()
287      {
288        if (entryMap != null)
289        {
290          return entryMap.size();
291        }
292    
293        return -1;
294      }
295    
296    
297    
298      /**
299       * {@inheritDoc}
300       */
301      @Override()
302      public boolean isLocal()
303      {
304        // For the purposes of this method, this is a local backend.
305        return true;
306      }
307    
308    
309    
310      /**
311       * {@inheritDoc}
312       */
313      @Override()
314      public boolean isIndexed(AttributeType attributeType, IndexType indexType)
315      {
316        // All searches in this backend will always be considered indexed.
317        return true;
318      }
319    
320    
321    
322      /**
323       * {@inheritDoc}
324       */
325      @Override
326      public synchronized ConditionResult hasSubordinates(DN entryDN)
327             throws DirectoryException
328      {
329        long ret = numSubordinates(entryDN, false);
330        if(ret < 0)
331        {
332          return ConditionResult.UNDEFINED;
333        }
334        else if(ret == 0)
335        {
336          return ConditionResult.FALSE;
337        }
338        else
339        {
340          return ConditionResult.TRUE;
341        }
342      }
343    
344      /**
345       * {@inheritDoc}
346       */
347      @Override()
348      public synchronized long numSubordinates(DN entryDN, boolean subtree)
349             throws DirectoryException
350      {
351        // Try to look up the immediate children for the DN
352        Set<DN> children = childDNs.get(entryDN);
353        if (children == null)
354        {
355          if(entryMap.get(entryDN) != null)
356          {
357            // The entry does exist but just no children.
358            return 0;
359          }
360          return -1;
361        }
362    
363        if(!subtree)
364        {
365          return children.size();
366        }
367        else
368        {
369          long count = 0;
370          for(DN child : children)
371          {
372            count += numSubordinates(child, true);
373            count++;
374          }
375          return count;
376        }
377      }
378    
379      /**
380       * {@inheritDoc}
381       */
382      @Override()
383      public synchronized Entry getEntry(DN entryDN)
384      {
385        Entry entry = entryMap.get(entryDN);
386        if (entry != null)
387        {
388          entry = entry.duplicate(true);
389        }
390    
391        return entry;
392      }
393    
394    
395    
396      /**
397       * {@inheritDoc}
398       */
399      @Override()
400      public synchronized boolean entryExists(DN entryDN)
401      {
402        return entryMap.containsKey(entryDN);
403      }
404    
405    
406    
407      /**
408       * {@inheritDoc}
409       */
410      @Override()
411      public synchronized void addEntry(Entry entry, AddOperation addOperation)
412             throws DirectoryException
413      {
414        Entry e = entry.duplicate(false);
415    
416        // See if the target entry already exists.  If so, then fail.
417        DN entryDN = e.getDN();
418        if (entryMap.containsKey(entryDN))
419        {
420          Message message =
421              ERR_MEMORYBACKEND_ENTRY_ALREADY_EXISTS.get(String.valueOf(entryDN));
422          throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, message);
423        }
424    
425    
426        // If the entry is one of the base DNs, then add it.
427        if (baseDNSet.contains(entryDN))
428        {
429          entryMap.put(entryDN, e);
430          return;
431        }
432    
433    
434        // Get the parent DN and ensure that it exists in the backend.
435        DN parentDN = entryDN.getParentDNInSuffix();
436        if (parentDN == null)
437        {
438          Message message =
439              ERR_MEMORYBACKEND_ENTRY_DOESNT_BELONG.get(String.valueOf(entryDN));
440          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
441        }
442        else if (! entryMap.containsKey(parentDN))
443        {
444          Message message = ERR_MEMORYBACKEND_PARENT_DOESNT_EXIST.get(
445              String.valueOf(entryDN), String.valueOf(parentDN));
446          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
447        }
448    
449        entryMap.put(entryDN, e);
450        HashSet<DN> children = childDNs.get(parentDN);
451        if (children == null)
452        {
453          children = new HashSet<DN>();
454          childDNs.put(parentDN, children);
455        }
456    
457        children.add(entryDN);
458      }
459    
460    
461    
462      /**
463       * {@inheritDoc}
464       */
465      @Override()
466      public synchronized void deleteEntry(DN entryDN,
467                                           DeleteOperation deleteOperation)
468             throws DirectoryException
469      {
470        // Make sure the entry exists.  If not, then throw an exception.
471        if (! entryMap.containsKey(entryDN))
472        {
473          Message message =
474              ERR_MEMORYBACKEND_ENTRY_DOESNT_EXIST.get(String.valueOf(entryDN));
475          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
476        }
477    
478    
479        // Check to see if the entry contains a subtree delete control.
480        boolean subtreeDelete = false;
481        if (deleteOperation != null)
482        {
483          for (Control c : deleteOperation.getRequestControls())
484          {
485            if (c.getOID().equals(OID_SUBTREE_DELETE_CONTROL))
486            {
487              subtreeDelete = true;
488            }
489          }
490        }
491    
492        HashSet<DN> children = childDNs.get(entryDN);
493        if (subtreeDelete)
494        {
495          if (children != null)
496          {
497            HashSet<DN> childrenCopy = new HashSet<DN>(children);
498            for (DN childDN : childrenCopy)
499            {
500              try
501              {
502                deleteEntry(childDN, null);
503              }
504              catch (Exception e)
505              {
506                // This shouldn't happen, but we want the delete to continue anyway
507                // so just ignore it if it does for some reason.
508                if (debugEnabled())
509                {
510                  TRACER.debugCaught(DebugLogLevel.ERROR, e);
511                }
512              }
513            }
514          }
515        }
516        else
517        {
518          // Make sure the entry doesn't have any children.  If it does, then throw
519          // an exception.
520          if ((children != null) && (! children.isEmpty()))
521          {
522            Message message = ERR_MEMORYBACKEND_CANNOT_DELETE_ENTRY_WITH_CHILDREN.
523                get(String.valueOf(entryDN));
524            throw new DirectoryException(
525                    ResultCode.NOT_ALLOWED_ON_NONLEAF, message);
526          }
527        }
528    
529    
530        // Remove the entry from the backend.  Also remove the reference to it from
531        // its parent, if applicable.
532        childDNs.remove(entryDN);
533        entryMap.remove(entryDN);
534    
535        DN parentDN = entryDN.getParentDNInSuffix();
536        if (parentDN != null)
537        {
538          HashSet<DN> parentsChildren = childDNs.get(parentDN);
539          if (parentsChildren != null)
540          {
541            parentsChildren.remove(entryDN);
542            if (parentsChildren.isEmpty())
543            {
544              childDNs.remove(parentDN);
545            }
546          }
547        }
548      }
549    
550    
551    
552      /**
553       * {@inheritDoc}
554       */
555      @Override()
556      public synchronized void replaceEntry(Entry entry,
557                                            ModifyOperation modifyOperation)
558             throws DirectoryException
559      {
560        Entry e = entry.duplicate(false);
561    
562        // Make sure the entry exists.  If not, then throw an exception.
563        DN entryDN = e.getDN();
564        if (! entryMap.containsKey(entryDN))
565        {
566          Message message =
567              ERR_MEMORYBACKEND_ENTRY_DOESNT_EXIST.get(String.valueOf(entryDN));
568          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
569        }
570    
571    
572        // Replace the old entry with the new one.
573        entryMap.put(entryDN, e);
574      }
575    
576    
577    
578      /**
579       * {@inheritDoc}
580       */
581      @Override()
582      public synchronized void renameEntry(DN currentDN, Entry entry,
583                                           ModifyDNOperation modifyDNOperation)
584             throws DirectoryException
585      {
586        Entry e = entry.duplicate(false);
587    
588        // Make sure that the target entry exists.
589        if (! entryMap.containsKey(currentDN))
590        {
591          Message message =
592              ERR_MEMORYBACKEND_ENTRY_DOESNT_EXIST.get(String.valueOf(currentDN));
593          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
594        }
595    
596    
597        // Make sure that the target entry doesn't have any children.
598        HashSet<DN> children  = childDNs.get(currentDN);
599        if (children != null)
600        {
601          if (children.isEmpty())
602          {
603            childDNs.remove(currentDN);
604          }
605          else
606          {
607            Message message = ERR_MEMORYBACKEND_CANNOT_RENAME_ENRY_WITH_CHILDREN.
608                get(String.valueOf(currentDN));
609            throw new DirectoryException(
610                    ResultCode.NOT_ALLOWED_ON_NONLEAF, message);
611          }
612        }
613    
614    
615        // Make sure that no entry exists with the new DN.
616        if (entryMap.containsKey(e.getDN()))
617        {
618          Message message =
619              ERR_MEMORYBACKEND_ENTRY_ALREADY_EXISTS.get(String.valueOf(e.getDN()));
620          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
621        }
622    
623    
624        // Make sure that the new DN is in this backend.
625        boolean matchFound = false;
626        for (DN dn : baseDNs)
627        {
628          if (dn.isAncestorOf(e.getDN()))
629          {
630            matchFound = true;
631            break;
632          }
633        }
634    
635        if (! matchFound)
636        {
637          Message message = ERR_MEMORYBACKEND_CANNOT_RENAME_TO_ANOTHER_BACKEND.get(
638              String.valueOf(currentDN));
639          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
640        }
641    
642    
643        // Make sure that the parent of the new entry exists.
644        DN parentDN = e.getDN().getParentDNInSuffix();
645        if ((parentDN == null) || (! entryMap.containsKey(parentDN)))
646        {
647          Message message = ERR_MEMORYBACKEND_RENAME_PARENT_DOESNT_EXIST.get(
648              String.valueOf(currentDN), String.valueOf(parentDN));
649          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
650        }
651    
652    
653        // Delete the current entry and add the new one.
654        deleteEntry(currentDN, null);
655        addEntry(e, null);
656      }
657    
658    
659    
660      /**
661       * {@inheritDoc}
662       */
663      @Override()
664      public synchronized void search(SearchOperation searchOperation)
665             throws DirectoryException
666      {
667        // Get the base DN, scope, and filter for the search.
668        DN           baseDN = searchOperation.getBaseDN();
669        SearchScope  scope  = searchOperation.getScope();
670        SearchFilter filter = searchOperation.getFilter();
671    
672    
673        // Make sure the base entry exists if it's supposed to be in this backend.
674        Entry baseEntry = entryMap.get(baseDN);
675        if ((baseEntry == null) && handlesEntry(baseDN))
676        {
677          DN matchedDN = baseDN.getParentDNInSuffix();
678          while (matchedDN != null)
679          {
680            if (entryMap.containsKey(matchedDN))
681            {
682              break;
683            }
684    
685            matchedDN = matchedDN.getParentDNInSuffix();
686          }
687    
688          Message message =
689              ERR_MEMORYBACKEND_ENTRY_DOESNT_EXIST.get(String.valueOf(baseDN));
690          throw new DirectoryException(
691                  ResultCode.NO_SUCH_OBJECT, message, matchedDN, null);
692        }
693    
694        if (baseEntry != null)
695        {
696          baseEntry = baseEntry.duplicate(true);
697        }
698    
699    
700        // If it's a base-level search, then just get that entry and return it if it
701        // matches the filter.
702        if (scope == SearchScope.BASE_OBJECT)
703        {
704          if (filter.matchesEntry(baseEntry))
705          {
706            searchOperation.returnEntry(baseEntry, new LinkedList<Control>());
707          }
708        }
709        else
710        {
711          // Walk through all entries and send the ones that match.
712          for (Entry e : entryMap.values())
713          {
714            e = e.duplicate(true);
715            if (e.matchesBaseAndScope(baseDN, scope) && filter.matchesEntry(e))
716            {
717              searchOperation.returnEntry(e, new LinkedList<Control>());
718            }
719          }
720        }
721      }
722    
723    
724    
725      /**
726       * {@inheritDoc}
727       */
728      @Override()
729      public HashSet<String> getSupportedControls()
730      {
731        return supportedControls;
732      }
733    
734    
735    
736      /**
737       * {@inheritDoc}
738       */
739      @Override()
740      public HashSet<String> getSupportedFeatures()
741      {
742        return supportedFeatures;
743      }
744    
745    
746    
747      /**
748       * {@inheritDoc}
749       */
750      @Override()
751      public boolean supportsLDIFExport()
752      {
753        return true;
754      }
755    
756    
757    
758      /**
759       * {@inheritDoc}
760       */
761      @Override()
762      public synchronized void exportLDIF(LDIFExportConfig exportConfig)
763             throws DirectoryException
764      {
765        // Create the LDIF writer.
766        LDIFWriter ldifWriter;
767        try
768        {
769          ldifWriter = new LDIFWriter(exportConfig);
770        }
771        catch (Exception e)
772        {
773          if (debugEnabled())
774          {
775            TRACER.debugCaught(DebugLogLevel.ERROR, e);
776          }
777    
778          Message message =
779              ERR_MEMORYBACKEND_CANNOT_CREATE_LDIF_WRITER.get(String.valueOf(e));
780          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
781                                       message, e);
782        }
783    
784    
785        // Walk through all the entries and write them to LDIF.
786        DN entryDN = null;
787        try
788        {
789          for (Entry entry : entryMap.values())
790          {
791            entryDN = entry.getDN();
792            ldifWriter.writeEntry(entry);
793          }
794        }
795        catch (Exception e)
796        {
797          Message message = ERR_MEMORYBACKEND_CANNOT_WRITE_ENTRY_TO_LDIF.get(
798              String.valueOf(entryDN), String.valueOf(e));
799          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
800                                       message, e);
801        }
802        finally
803        {
804          try
805          {
806            ldifWriter.close();
807          }
808          catch (Exception e)
809          {
810            if (debugEnabled())
811            {
812              TRACER.debugCaught(DebugLogLevel.ERROR, e);
813            }
814          }
815        }
816      }
817    
818    
819    
820      /**
821       * {@inheritDoc}
822       */
823      @Override()
824      public boolean supportsLDIFImport()
825      {
826        return true;
827      }
828    
829    
830    
831      /**
832       * {@inheritDoc}
833       */
834      @Override()
835      public synchronized LDIFImportResult importLDIF(LDIFImportConfig importConfig)
836             throws DirectoryException
837      {
838        clearMemoryBackend();
839    
840        LDIFReader reader;
841        try
842        {
843          reader = new LDIFReader(importConfig);
844        }
845        catch (Exception e)
846        {
847          Message message =
848              ERR_MEMORYBACKEND_CANNOT_CREATE_LDIF_READER.get(String.valueOf(e));
849          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
850                                       message, e);
851        }
852    
853    
854        try
855        {
856          while (true)
857          {
858            Entry e = null;
859            try
860            {
861              e = reader.readEntry();
862              if (e == null)
863              {
864                break;
865              }
866            }
867            catch (LDIFException le)
868            {
869              if (! le.canContinueReading())
870              {
871                Message message =
872                    ERR_MEMORYBACKEND_ERROR_READING_LDIF.get(String.valueOf(e));
873                throw new DirectoryException(
874                               DirectoryServer.getServerErrorResultCode(),
875                               message, le);
876              }
877              else
878              {
879                continue;
880              }
881            }
882    
883            try
884            {
885              addEntry(e, null);
886            }
887            catch (DirectoryException de)
888            {
889              reader.rejectLastEntry(de.getMessageObject());
890            }
891          }
892    
893          return new LDIFImportResult(reader.getEntriesRead(),
894                                      reader.getEntriesRejected(),
895                                      reader.getEntriesIgnored());
896        }
897        catch (DirectoryException de)
898        {
899          throw de;
900        }
901        catch (Exception e)
902        {
903          Message message =
904              ERR_MEMORYBACKEND_ERROR_DURING_IMPORT.get(String.valueOf(e));
905          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
906                                       message, e);
907        }
908        finally
909        {
910          reader.close();
911        }
912      }
913    
914    
915    
916      /**
917       * {@inheritDoc}
918       */
919      @Override()
920      public boolean supportsBackup()
921      {
922        // This backend does not provide a backup/restore mechanism.
923        return false;
924      }
925    
926    
927    
928      /**
929       * {@inheritDoc}
930       */
931      @Override()
932      public boolean supportsBackup(BackupConfig backupConfig,
933                                    StringBuilder unsupportedReason)
934      {
935        // This backend does not provide a backup/restore mechanism.
936        return false;
937      }
938    
939    
940    
941      /**
942       * {@inheritDoc}
943       */
944      @Override()
945      public void createBackup(BackupConfig backupConfig)
946             throws DirectoryException
947      {
948        Message message = ERR_MEMORYBACKEND_BACKUP_RESTORE_NOT_SUPPORTED.get();
949        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
950      }
951    
952    
953    
954      /**
955       * {@inheritDoc}
956       */
957      @Override()
958      public void removeBackup(BackupDirectory backupDirectory,
959                               String backupID)
960             throws DirectoryException
961      {
962        Message message = ERR_MEMORYBACKEND_BACKUP_RESTORE_NOT_SUPPORTED.get();
963        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
964      }
965    
966    
967    
968      /**
969       * {@inheritDoc}
970       */
971      @Override()
972      public boolean supportsRestore()
973      {
974        // This backend does not provide a backup/restore mechanism.
975        return false;
976      }
977    
978    
979    
980      /**
981       * {@inheritDoc}
982       */
983      @Override()
984      public void restoreBackup(RestoreConfig restoreConfig)
985             throws DirectoryException
986      {
987        Message message = ERR_MEMORYBACKEND_BACKUP_RESTORE_NOT_SUPPORTED.get();
988        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
989      }
990    
991    
992    
993      /**
994       * {@inheritDoc}
995       */
996      public void preloadEntryCache() throws UnsupportedOperationException {
997        throw new UnsupportedOperationException("Operation not supported.");
998      }
999    }
1000