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<DN,Entry></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<ByteString,Set<DN>></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