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