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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2008 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS. 026 */ 027package org.opends.server.tools; 028 029import static com.forgerock.opendj.cli.ArgumentConstants.*; 030import static com.forgerock.opendj.cli.Utils.*; 031 032import static org.opends.messages.ToolMessages.*; 033import static org.opends.server.util.CollectionUtils.*; 034import static org.opends.server.protocols.ldap.LDAPResultCode.*; 035import static org.opends.server.util.StaticUtils.*; 036 037import java.io.BufferedReader; 038import java.io.FileReader; 039import java.io.OutputStream; 040import java.io.PrintStream; 041import java.util.ArrayList; 042import java.util.Iterator; 043import java.util.LinkedHashSet; 044import java.util.LinkedList; 045 046import org.forgerock.i18n.LocalizableMessage; 047import org.forgerock.opendj.ldap.SearchScope; 048import org.opends.server.core.DirectoryServer; 049import org.opends.server.core.DirectoryServer.DirectoryServerVersionHandler; 050import org.opends.server.extensions.ConfigFileHandler; 051import org.opends.server.loggers.JDKLogging; 052import org.opends.server.protocols.ldap.LDAPResultCode; 053import org.opends.server.types.*; 054import org.opends.server.util.BuildVersion; 055import org.opends.server.util.LDIFException; 056import org.opends.server.util.LDIFReader; 057import org.opends.server.util.LDIFWriter; 058 059import com.forgerock.opendj.cli.*; 060 061/** 062 * This class provides a program that may be used to search LDIF files. It is 063 * modeled after the LDAPSearch tool, with the primary differencing being that 064 * all of its data comes from LDIF rather than communicating over LDAP. 065 * However, it does have a number of differences that allow it to perform 066 * multiple operations in a single pass rather than requiring multiple passes 067 * through the LDIF. 068 */ 069public class LDIFSearch 070{ 071 /** The fully-qualified name of this class. */ 072 private static final String CLASS_NAME = "org.opends.server.tools.LDIFSearch"; 073 074 /** The search scope string that will be used for baseObject searches. */ 075 private static final String SCOPE_STRING_BASE = "base"; 076 /** The search scope string that will be used for singleLevel searches. */ 077 private static final String SCOPE_STRING_ONE = "one"; 078 /** The search scope string that will be used for wholeSubtree searches. */ 079 private static final String SCOPE_STRING_SUB = "sub"; 080 /** The search scope string that will be used for subordinateSubtree searches. */ 081 private static final String SCOPE_STRING_SUBORDINATE = "subordinate"; 082 083 /** 084 * Provides the command line arguments to the <CODE>mainSearch</CODE> method 085 * so that they can be processed. 086 * 087 * @param args The command line arguments provided to this program. 088 */ 089 public static void main(String[] args) 090 { 091 int exitCode = mainSearch(args, true, System.out, System.err); 092 if (exitCode != 0) 093 { 094 System.exit(filterExitCode(exitCode)); 095 } 096 } 097 098 099 100 /** 101 * Parses the provided command line arguments and performs the appropriate 102 * search operation. 103 * 104 * @param args The command line arguments provided to this 105 * program. 106 * @param initializeServer True if server initialization should be done. 107 * @param outStream The output stream to use for standard output, or 108 * {@code null} if standard output is not needed. 109 * @param errStream The output stream to use for standard error, or 110 * {@code null} if standard error is not needed. 111 * 112 * @return The return code for this operation. A value of zero indicates 113 * that all processing completed successfully. A nonzero value 114 * indicates that some problem occurred during processing. 115 */ 116 public static int mainSearch(String[] args, boolean initializeServer, 117 OutputStream outStream, OutputStream errStream) 118 { 119 PrintStream out = NullOutputStream.wrapOrNullStream(outStream); 120 PrintStream err = NullOutputStream.wrapOrNullStream(errStream); 121 JDKLogging.disableLogging(); 122 123 LinkedHashSet<String> scopeStrings = new LinkedHashSet<>(4); 124 scopeStrings.add(SCOPE_STRING_BASE); 125 scopeStrings.add(SCOPE_STRING_ONE); 126 scopeStrings.add(SCOPE_STRING_SUB); 127 scopeStrings.add(SCOPE_STRING_SUBORDINATE); 128 129 130 BooleanArgument dontWrap; 131 BooleanArgument overwriteExisting; 132 BooleanArgument showUsage; 133 StringArgument filterFile; 134 IntegerArgument sizeLimit; 135 IntegerArgument timeLimit; 136 MultiChoiceArgument<String> scopeString; 137 StringArgument baseDNString; 138 StringArgument configClass; 139 StringArgument configFile; 140 StringArgument ldifFile; 141 StringArgument outputFile; 142 143 144 LocalizableMessage toolDescription = INFO_LDIFSEARCH_TOOL_DESCRIPTION.get(); 145 ArgumentParser argParser = new ArgumentParser(CLASS_NAME, toolDescription, 146 false, true, 0, 0, 147 "[filter] [attributes ...]"); 148 argParser.setShortToolDescription(REF_SHORT_DESC_LDIFSEARCH.get()); 149 argParser.setVersionHandler(new DirectoryServerVersionHandler()); 150 151 try 152 { 153 ldifFile = new StringArgument( 154 "ldiffile", 'l', "ldifFile", false, true, 155 true, INFO_LDIFFILE_PLACEHOLDER.get(), null, null, 156 INFO_LDIFSEARCH_DESCRIPTION_LDIF_FILE.get()); 157 argParser.addArgument(ldifFile); 158 159 baseDNString = new StringArgument( 160 "basedn", OPTION_SHORT_BASEDN, 161 OPTION_LONG_BASEDN, false, true, 162 true, INFO_BASEDN_PLACEHOLDER.get(), "", null, 163 INFO_LDIFSEARCH_DESCRIPTION_BASEDN.get()); 164 argParser.addArgument(baseDNString); 165 166 scopeString = new MultiChoiceArgument<>( 167 "scope", 's', "searchScope", false, false, 168 true, INFO_SCOPE_PLACEHOLDER.get(), SCOPE_STRING_SUB, 169 null, scopeStrings, false, 170 INFO_LDIFSEARCH_DESCRIPTION_SCOPE.get()); 171 argParser.addArgument(scopeString); 172 173 configFile = new StringArgument( 174 "configfile", 'c', "configFile", false, 175 false, true, INFO_CONFIGFILE_PLACEHOLDER.get(), null, null, 176 INFO_DESCRIPTION_CONFIG_FILE.get()); 177 configFile.setHidden(true); 178 argParser.addArgument(configFile); 179 180 configClass = new StringArgument("configclass", OPTION_SHORT_CONFIG_CLASS, 181 OPTION_LONG_CONFIG_CLASS, false, 182 false, true, INFO_CONFIGCLASS_PLACEHOLDER.get(), 183 ConfigFileHandler.class.getName(), null, 184 INFO_DESCRIPTION_CONFIG_CLASS.get()); 185 configClass.setHidden(true); 186 argParser.addArgument(configClass); 187 188 filterFile = new StringArgument("filterfile", 'f', "filterFile", false, 189 false, true, INFO_FILTER_FILE_PLACEHOLDER.get(), null, null, 190 INFO_LDIFSEARCH_DESCRIPTION_FILTER_FILE.get()); 191 argParser.addArgument(filterFile); 192 193 outputFile = new StringArgument( 194 "outputfile", 'o', "outputFile", false, 195 false, true, INFO_OUTPUT_FILE_PLACEHOLDER.get(), null, null, 196 INFO_LDIFSEARCH_DESCRIPTION_OUTPUT_FILE.get()); 197 argParser.addArgument(outputFile); 198 199 overwriteExisting = 200 new BooleanArgument( 201 "overwriteexisting", 'O',"overwriteExisting", 202 INFO_LDIFSEARCH_DESCRIPTION_OVERWRITE_EXISTING.get()); 203 argParser.addArgument(overwriteExisting); 204 205 dontWrap = new BooleanArgument( 206 "dontwrap", 'T', "dontWrap", 207 INFO_LDIFSEARCH_DESCRIPTION_DONT_WRAP.get()); 208 argParser.addArgument(dontWrap); 209 210 sizeLimit = new IntegerArgument( 211 "sizelimit", 'z', "sizeLimit", false, 212 false, true, INFO_SIZE_LIMIT_PLACEHOLDER.get(), 0, null, 213 true, 0, false, 0, 214 INFO_LDIFSEARCH_DESCRIPTION_SIZE_LIMIT.get()); 215 argParser.addArgument(sizeLimit); 216 217 timeLimit = new IntegerArgument( 218 "timelimit", 't', "timeLimit", false, 219 false, true, INFO_TIME_LIMIT_PLACEHOLDER.get(), 0, null, 220 true, 0, false, 0, 221 INFO_LDIFSEARCH_DESCRIPTION_TIME_LIMIT.get()); 222 argParser.addArgument(timeLimit); 223 224 225 showUsage = CommonArguments.getShowUsage(); 226 argParser.addArgument(showUsage); 227 argParser.setUsageArgument(showUsage); 228 } 229 catch (ArgumentException ae) 230 { 231 printWrappedText(err, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage())); 232 return 1; 233 } 234 235 236 // Parse the command-line arguments provided to the program. 237 try 238 { 239 argParser.parseArguments(args); 240 } 241 catch (ArgumentException ae) 242 { 243 argParser.displayMessageAndUsageReference(err, ERR_ERROR_PARSING_ARGS.get(ae.getMessage())); 244 return CLIENT_SIDE_PARAM_ERROR; 245 } 246 247 248 // If we should just display usage or version information, 249 // then print it and exit. 250 if (argParser.usageOrVersionDisplayed()) 251 { 252 return 0; 253 } 254 255 // Checks the version - if upgrade required, the tool is unusable 256 try 257 { 258 BuildVersion.checkVersionMismatch(); 259 } 260 catch (InitializationException e) 261 { 262 printWrappedText(err, e.getMessage()); 263 return 1; 264 } 265 266 // Make sure that at least one filter was provided. Also get the attribute 267 // list at the same time because it may need to be specified in the same 268 // way. 269 boolean allUserAttrs = false; 270 boolean allOperationalAttrs = false; 271 //Return objectclass attribute unless analysis of the arguments determines 272 //otherwise. 273 boolean includeObjectclassAttrs = true; 274 final LinkedList<String> attributeNames = new LinkedList<>(); 275 LinkedList<String> objectClassNames = new LinkedList<>(); 276 LinkedList<String> filterStrings = new LinkedList<>(); 277 if (filterFile.isPresent()) 278 { 279 BufferedReader in = null; 280 try 281 { 282 String fileNameValue = filterFile.getValue(); 283 in = new BufferedReader(new FileReader(fileNameValue)); 284 String line = null; 285 286 while ((line = in.readLine()) != null) 287 { 288 if(line.trim().equals("")) 289 { 290 // ignore empty lines. 291 continue; 292 } 293 filterStrings.add(line); 294 } 295 } catch(Exception e) 296 { 297 printWrappedText(err, e.getMessage()); 298 return 1; 299 } 300 finally 301 { 302 close(in); 303 } 304 305 ArrayList<String> trailingArguments = argParser.getTrailingArguments(); 306 if (trailingArguments != null && !trailingArguments.isEmpty()) 307 { 308 for (String attributeName : trailingArguments) 309 { 310 String lowerName = toLowerCase(attributeName); 311 if (lowerName.equals("*")) 312 { 313 allUserAttrs = true; 314 } 315 else if (lowerName.equals("+")) 316 { 317 allOperationalAttrs = true; 318 } 319 else if (lowerName.startsWith("@")) 320 { 321 objectClassNames.add(lowerName.substring(1)); 322 } 323 else 324 { 325 attributeNames.add(lowerName); 326 } 327 } 328 } 329 } 330 else 331 { 332 ArrayList<String> trailingArguments = argParser.getTrailingArguments(); 333 if (trailingArguments == null || trailingArguments.isEmpty()) 334 { 335 argParser.displayMessageAndUsageReference(err, ERR_LDIFSEARCH_NO_FILTER.get()); 336 return 1; 337 } 338 339 Iterator<String> iterator = trailingArguments.iterator(); 340 filterStrings = newLinkedList(iterator.next()); 341 342 while (iterator.hasNext()) 343 { 344 String lowerName = toLowerCase(iterator.next()); 345 if (lowerName.equals("*")) 346 { 347 allUserAttrs = true; 348 } 349 else if (lowerName.equals("+")) 350 { 351 allOperationalAttrs = true; 352 } 353 else if (lowerName.startsWith("@")) 354 { 355 objectClassNames.add(lowerName.substring(1)); 356 } 357 else 358 { 359 attributeNames.add(lowerName); 360 } 361 } 362 } 363 364 if (attributeNames.isEmpty() 365 && objectClassNames.isEmpty() 366 && !allOperationalAttrs) 367 { 368 // This will be true if no attributes were requested, which is effectively 369 // all user attributes. It will also be true if just "*" was included, 370 // but the net result will be the same. 371 allUserAttrs = true; 372 } 373 374 //Determine if objectclass attribute should be returned. 375 if(!allUserAttrs) { 376 //Single '+', never return objectclass. 377 if(allOperationalAttrs && objectClassNames.isEmpty() && 378 attributeNames.isEmpty()) 379 { 380 includeObjectclassAttrs=false; 381 } 382 //If "objectclass" isn't specified in the attributes to return, then 383 //don't include objectclass attribute. 384 if(!attributeNames.isEmpty() && objectClassNames.isEmpty() && 385 !attributeNames.contains("objectclass")) 386 { 387 includeObjectclassAttrs=false; 388 } 389 } 390 391 392 // Bootstrap the Directory Server configuration for use as a client. 393 DirectoryServer directoryServer = DirectoryServer.getInstance(); 394 395 // If we're to use the configuration then initialize it, along with the 396 // schema. 397 boolean checkSchema = configFile.isPresent(); 398 399 if (initializeServer) 400 { 401 DirectoryServer.bootstrapClient(); 402 403 if (checkSchema) 404 { 405 try 406 { 407 DirectoryServer.initializeJMX(); 408 } 409 catch (Exception e) 410 { 411 printWrappedText(err, ERR_LDIFSEARCH_CANNOT_INITIALIZE_JMX.get(configFile.getValue(), e.getMessage())); 412 return 1; 413 } 414 415 try 416 { 417 directoryServer.initializeConfiguration(configClass.getValue(), configFile.getValue()); 418 } 419 catch (Exception e) 420 { 421 printWrappedText(err, ERR_LDIFSEARCH_CANNOT_INITIALIZE_CONFIG.get(configFile.getValue(), e.getMessage())); 422 return 1; 423 } 424 425 try 426 { 427 directoryServer.initializeSchema(); 428 } 429 catch (Exception e) 430 { 431 printWrappedText(err, ERR_LDIFSEARCH_CANNOT_INITIALIZE_SCHEMA.get(configFile.getValue(), e.getMessage())); 432 return 1; 433 } 434 } 435 } 436 437 // Choose the desired search scope. 438 SearchScope searchScope; 439 if (scopeString.isPresent()) 440 { 441 String scopeStr = toLowerCase(scopeString.getValue()); 442 if (scopeStr.equals(SCOPE_STRING_BASE)) 443 { 444 searchScope = SearchScope.BASE_OBJECT; 445 } 446 else if (scopeStr.equals(SCOPE_STRING_ONE)) 447 { 448 searchScope = SearchScope.SINGLE_LEVEL; 449 } 450 else if (scopeStr.equals(SCOPE_STRING_SUBORDINATE)) 451 { 452 searchScope = SearchScope.SUBORDINATES; 453 } 454 else 455 { 456 searchScope = SearchScope.WHOLE_SUBTREE; 457 } 458 } 459 else 460 { 461 searchScope = SearchScope.WHOLE_SUBTREE; 462 } 463 464 465 // Create the list of filters that will be used to process the searches. 466 LinkedList<SearchFilter> searchFilters = new LinkedList<>(); 467 for (String filterString : filterStrings) 468 { 469 try 470 { 471 searchFilters.add(SearchFilter.createFilterFromString(filterString)); 472 } 473 catch (Exception e) 474 { 475 printWrappedText(err, ERR_LDIFSEARCH_CANNOT_PARSE_FILTER.get(filterString, e.getMessage())); 476 return 1; 477 } 478 } 479 480 481 // Transform the attributes to return from strings to attribute types. 482 LinkedHashSet<AttributeType> userAttributeTypes = new LinkedHashSet<>(); 483 LinkedHashSet<AttributeType> operationalAttributeTypes = new LinkedHashSet<>(); 484 for (String attributeName : attributeNames) 485 { 486 AttributeType t = DirectoryServer.getAttributeTypeOrDefault(attributeName); 487 if (t.isOperational()) 488 { 489 operationalAttributeTypes.add(t); 490 } 491 else 492 { 493 userAttributeTypes.add(t); 494 } 495 } 496 497 for (String objectClassName : objectClassNames) 498 { 499 ObjectClass c = DirectoryServer.getObjectClass(objectClassName, true); 500 for (AttributeType t : c.getRequiredAttributeChain()) 501 { 502 if (t.isOperational()) 503 { 504 operationalAttributeTypes.add(t); 505 } 506 else 507 { 508 userAttributeTypes.add(t); 509 } 510 } 511 512 for (AttributeType t : c.getOptionalAttributeChain()) 513 { 514 if (t.isOperational()) 515 { 516 operationalAttributeTypes.add(t); 517 } 518 else 519 { 520 userAttributeTypes.add(t); 521 } 522 } 523 } 524 525 526 // Set the base DNs for the import config. 527 LinkedList<DN> baseDNs = new LinkedList<>(); 528 if (baseDNString.isPresent()) 529 { 530 for (String dnString : baseDNString.getValues()) 531 { 532 try 533 { 534 baseDNs.add(DN.valueOf(dnString)); 535 } 536 catch (Exception e) 537 { 538 printWrappedText(err, ERR_LDIFSEARCH_CANNOT_PARSE_BASE_DN.get(dnString, e.getMessage())); 539 return 1; 540 } 541 } 542 } 543 else 544 { 545 baseDNs.add(DN.rootDN()); 546 } 547 548 549 // Get the time limit in milliseconds. 550 long timeLimitMillis; 551 try 552 { 553 if (timeLimit.isPresent()) 554 { 555 timeLimitMillis = 1000L * timeLimit.getIntValue(); 556 } 557 else 558 { 559 timeLimitMillis = 0; 560 } 561 } 562 catch (Exception e) 563 { 564 printWrappedText(err, ERR_LDIFSEARCH_CANNOT_PARSE_TIME_LIMIT.get(e)); 565 return 1; 566 } 567 568 569 // Convert the size limit to an integer. 570 int sizeLimitValue; 571 try 572 { 573 if (sizeLimit.isPresent()) 574 { 575 sizeLimitValue = sizeLimit.getIntValue(); 576 } 577 else 578 { 579 sizeLimitValue =0; 580 } 581 } 582 catch (Exception e) 583 { 584 printWrappedText(err, ERR_LDIFSEARCH_CANNOT_PARSE_SIZE_LIMIT.get(e)); 585 return 1; 586 } 587 588 589 // Create the LDIF import configuration that will be used to read the source 590 // data. 591 LDIFImportConfig importConfig; 592 if (ldifFile.isPresent()) 593 { 594 importConfig = new LDIFImportConfig(ldifFile.getValues()); 595 } 596 else 597 { 598 importConfig = new LDIFImportConfig(System.in); 599 } 600 601 602 // Create the LDIF export configuration that will be used to write the 603 // matching entries. 604 LDIFExportConfig exportConfig; 605 if (outputFile.isPresent()) 606 { 607 if (overwriteExisting.isPresent()) 608 { 609 exportConfig = new LDIFExportConfig(outputFile.getValue(), 610 ExistingFileBehavior.OVERWRITE); 611 } 612 else 613 { 614 exportConfig = new LDIFExportConfig(outputFile.getValue(), 615 ExistingFileBehavior.APPEND); 616 } 617 } 618 else 619 { 620 exportConfig = new LDIFExportConfig(out); 621 } 622 623 exportConfig.setIncludeObjectClasses(includeObjectclassAttrs); 624 if (dontWrap.isPresent()) 625 { 626 exportConfig.setWrapColumn(0); 627 } 628 else 629 { 630 exportConfig.setWrapColumn(75); 631 } 632 633 634 // Create the LDIF reader/writer from the import/export config. 635 LDIFReader reader; 636 LDIFWriter writer; 637 try 638 { 639 reader = new LDIFReader(importConfig); 640 } 641 catch (Exception e) 642 { 643 printWrappedText(err, ERR_LDIFSEARCH_CANNOT_CREATE_READER.get(e)); 644 return 1; 645 } 646 647 try 648 { 649 writer = new LDIFWriter(exportConfig); 650 } 651 catch (Exception e) 652 { 653 close(reader); 654 printWrappedText(err, ERR_LDIFSEARCH_CANNOT_CREATE_WRITER.get(e)); 655 return 1; 656 } 657 658 659 // Start reading data from the LDIF reader. 660 long startTime = System.currentTimeMillis(); 661 long stopTime = startTime + timeLimitMillis; 662 long matchCount = 0; 663 int resultCode = LDAPResultCode.SUCCESS; 664 while (true) 665 { 666 // If the time limit has been reached, then stop now. 667 if (timeLimitMillis > 0 && System.currentTimeMillis() > stopTime) 668 { 669 resultCode = LDAPResultCode.TIME_LIMIT_EXCEEDED; 670 671 LocalizableMessage message = WARN_LDIFSEARCH_TIME_LIMIT_EXCEEDED.get(); 672 err.println(message); 673 break; 674 } 675 676 677 try 678 { 679 Entry entry = reader.readEntry(checkSchema); 680 if (entry == null) 681 { 682 break; 683 } 684 685 686 // Check to see if the entry has an acceptable base and scope. 687 boolean matchesBaseAndScope = false; 688 for (DN baseDN : baseDNs) 689 { 690 if (entry.matchesBaseAndScope(baseDN, searchScope)) 691 { 692 matchesBaseAndScope = true; 693 break; 694 } 695 } 696 697 if (! matchesBaseAndScope) 698 { 699 continue; 700 } 701 702 703 // Check to see if the entry matches any of the filters. 704 boolean matchesFilter = false; 705 for (SearchFilter filter : searchFilters) 706 { 707 if (filter.matchesEntry(entry)) 708 { 709 matchesFilter = true; 710 break; 711 } 712 } 713 714 if (! matchesFilter) 715 { 716 continue; 717 } 718 719 720 // Prepare the entry to return to the client. 721 if (! allUserAttrs) 722 { 723 Iterator<AttributeType> iterator = 724 entry.getUserAttributes().keySet().iterator(); 725 while (iterator.hasNext()) 726 { 727 if (! userAttributeTypes.contains(iterator.next())) 728 { 729 iterator.remove(); 730 } 731 } 732 } 733 734 if (! allOperationalAttrs) 735 { 736 Iterator<AttributeType> iterator = 737 entry.getOperationalAttributes().keySet().iterator(); 738 while (iterator.hasNext()) 739 { 740 if (! operationalAttributeTypes.contains(iterator.next())) 741 { 742 iterator.remove(); 743 } 744 } 745 } 746 747 748 // Write the entry to the client and increase the count. 749 // FIXME -- Should we include a comment about which base+filter matched? 750 writer.writeEntry(entry); 751 writer.flush(); 752 753 matchCount++; 754 if (sizeLimitValue > 0 && matchCount >= sizeLimitValue) 755 { 756 resultCode = LDAPResultCode.SIZE_LIMIT_EXCEEDED; 757 758 LocalizableMessage message = WARN_LDIFSEARCH_SIZE_LIMIT_EXCEEDED.get(); 759 err.println(message); 760 break; 761 } 762 } 763 catch (LDIFException le) 764 { 765 if (le.canContinueReading()) 766 { 767 LocalizableMessage message = ERR_LDIFSEARCH_CANNOT_READ_ENTRY_RECOVERABLE.get( 768 le.getMessage()); 769 err.println(message); 770 } 771 else 772 { 773 LocalizableMessage message = ERR_LDIFSEARCH_CANNOT_READ_ENTRY_FATAL.get( 774 le.getMessage()); 775 err.println(message); 776 resultCode = LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR; 777 break; 778 } 779 } 780 catch (Exception e) 781 { 782 err.println(ERR_LDIFSEARCH_ERROR_DURING_PROCESSING.get(e)); 783 resultCode = LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR; 784 break; 785 } 786 } 787 788 close(reader, writer); 789 790 return resultCode; 791 } 792} 793