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