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