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