001    /*
002     * Copyright 2009-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2015 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.ldap.sdk.unboundidds.tasks;
022    
023    
024    
025    import java.util.Arrays;
026    import java.util.Collections;
027    import java.util.Date;
028    import java.util.LinkedHashMap;
029    import java.util.LinkedList;
030    import java.util.List;
031    import java.util.Map;
032    
033    import com.unboundid.ldap.sdk.Attribute;
034    import com.unboundid.ldap.sdk.Entry;
035    import com.unboundid.ldap.sdk.Filter;
036    import com.unboundid.ldap.sdk.LDAPException;
037    import com.unboundid.ldap.sdk.SearchScope;
038    import com.unboundid.util.NotMutable;
039    import com.unboundid.util.ThreadSafety;
040    import com.unboundid.util.ThreadSafetyLevel;
041    
042    import static com.unboundid.ldap.sdk.unboundidds.tasks.TaskMessages.*;
043    import static com.unboundid.util.Debug.*;
044    import static com.unboundid.util.StaticUtils.*;
045    import static com.unboundid.util.Validator.*;
046    
047    
048    
049    /**
050     * <BLOCKQUOTE>
051     *   <B>NOTE:</B>  This class is part of the Commercial Edition of the UnboundID
052     *   LDAP SDK for Java.  It is not available for use in applications that
053     *   include only the Standard Edition of the LDAP SDK, and is not supported for
054     *   use in conjunction with non-UnboundID products.
055     * </BLOCKQUOTE>
056     * This class defines a Directory Server task that can be used to perform an
057     * internal search within the server and write the contents to an LDIF file.
058     * The properties that are available for use with this type of task include:
059     * <UL>
060     *   <LI>The base DN to use for the search.  This is required.</LI>
061     *   <LI>The scope to use for the search.  This is required.</LI>
062     *   <LI>The filter to use for the search.  This is required.</LI>
063     *   <LI>The attributes to return.  This is optional and multivalued.</LI>
064     *   <LI>The authorization DN to use for the search.  This is optional.</LI>
065     *   <LI>The path to the output file to use.  This is required.</LI>
066     * </UL>
067     */
068    @NotMutable()
069    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
070    public final class SearchTask
071           extends Task
072    {
073      /**
074       * The fully-qualified name of the Java class that is used for the search
075       * task.
076       */
077      static final String SEARCH_TASK_CLASS =
078           "com.unboundid.directory.server.tasks.SearchTask";
079    
080    
081    
082      /**
083       * The name of the attribute used to specify the search base DN.
084       */
085      private static final String ATTR_BASE_DN = "ds-task-search-base-dn";
086    
087    
088    
089      /**
090       * The name of the attribute used to specify the search scope.
091       */
092      private static final String ATTR_SCOPE = "ds-task-search-scope";
093    
094    
095    
096      /**
097       * The name of the attribute used to specify the search filter.
098       */
099      private static final String ATTR_FILTER = "ds-task-search-filter";
100    
101    
102    
103      /**
104       * The name of the attribute used to specify the attribute(s) to return.
105       */
106      private static final String ATTR_RETURN_ATTR =
107           "ds-task-search-return-attribute";
108    
109    
110    
111      /**
112       * The name of the attribute used to specify the authorization DN.
113       */
114      private static final String ATTR_AUTHZ_DN = "ds-task-search-authz-dn";
115    
116    
117    
118      /**
119       * The name of the attribute used to specify the output file.
120       */
121      private static final String ATTR_OUTPUT_FILE = "ds-task-search-output-file";
122    
123    
124    
125      /**
126       * The name of the object class used in search task entries.
127       */
128      private static final String OC_SEARCH_TASK = "ds-task-search";
129    
130    
131    
132      /**
133       * The task property that will be used for the base DN.
134       */
135      private static final TaskProperty PROPERTY_BASE_DN =
136           new TaskProperty(ATTR_BASE_DN,
137                INFO_SEARCH_TASK_DISPLAY_NAME_BASE_DN.get(),
138                INFO_SEARCH_TASK_DESCRIPTION_BASE_DN.get(), String.class, true,
139                false, false);
140    
141    
142    
143      /**
144       * The allowed values for the scope property.
145       */
146      private static final Object[] ALLOWED_SCOPE_VALUES =
147      {
148        "base", "baseobject", "0",
149        "one", "onelevel", "singlelevel", "1",
150        "sub", "subtree", "wholesubtree", "2",
151        "subord", "subordinate", "subordinatesubtree", "3"
152      };
153    
154    
155    
156      /**
157       * The task property that will be used for the scope.
158       */
159      private static final TaskProperty PROPERTY_SCOPE =
160           new TaskProperty(ATTR_SCOPE,
161                INFO_SEARCH_TASK_DISPLAY_NAME_SCOPE.get(),
162                INFO_SEARCH_TASK_DESCRIPTION_SCOPE.get(), String.class, true,
163                false, false, ALLOWED_SCOPE_VALUES);
164    
165    
166    
167      /**
168       * The task property that will be used for the filter.
169       */
170      private static final TaskProperty PROPERTY_FILTER =
171           new TaskProperty(ATTR_FILTER,
172                INFO_SEARCH_TASK_DISPLAY_NAME_FILTER.get(),
173                INFO_SEARCH_TASK_DESCRIPTION_FILTER.get(), String.class, true,
174                false, false);
175    
176    
177    
178      /**
179       * The task property that will be used for the requested attributes.
180       */
181      private static final TaskProperty PROPERTY_REQUESTED_ATTR =
182           new TaskProperty(ATTR_RETURN_ATTR,
183                INFO_SEARCH_TASK_DISPLAY_NAME_RETURN_ATTR.get(),
184                INFO_SEARCH_TASK_DESCRIPTION_RETURN_ATTR.get(), String.class, false,
185                true, false);
186    
187    
188    
189      /**
190       * The task property that will be used for the authorization DN.
191       */
192      private static final TaskProperty PROPERTY_AUTHZ_DN =
193           new TaskProperty(ATTR_AUTHZ_DN,
194                INFO_SEARCH_TASK_DISPLAY_NAME_AUTHZ_DN.get(),
195                INFO_SEARCH_TASK_DESCRIPTION_AUTHZ_DN.get(), String.class, false,
196                false, true);
197    
198    
199    
200      /**
201       * The task property that will be used for the output file.
202       */
203      private static final TaskProperty PROPERTY_OUTPUT_FILE =
204           new TaskProperty(ATTR_OUTPUT_FILE,
205                INFO_SEARCH_TASK_DISPLAY_NAME_OUTPUT_FILE.get(),
206                INFO_SEARCH_TASK_DESCRIPTION_NAME_OUTPUT_FILE.get(), String.class,
207                true, false, false);
208    
209    
210    
211      /**
212       * The serial version UID for this serializable class.
213       */
214      private static final long serialVersionUID = -1742374271508548328L;
215    
216    
217    
218      // The search filter.
219      private final Filter filter;
220    
221      // The list of attributes to return.
222      private final List<String> attributes;
223    
224      // The search scope.
225      private final SearchScope scope;
226    
227      // The authorization DN.
228      private final String authzDN;
229    
230      // The search base DN.
231      private final String baseDN;
232    
233      // The output file path.
234      private final String outputFile;
235    
236    
237    
238      /**
239       * Creates a new uninitialized search task instance which should only be used
240       * for obtaining general information about this task, including the task name,
241       * description, and supported properties.  Attempts to use a task created with
242       * this constructor for any other reason will likely fail.
243       */
244      public SearchTask()
245      {
246        filter     = null;
247        attributes = null;
248        scope      = null;
249        authzDN    = null;
250        baseDN     = null;
251        outputFile = null;
252      }
253    
254    
255    
256      /**
257       * Creates a new search task with the provided information.
258       *
259       * @param  taskID      The task ID to use for this task.  If it is
260       *                     {@code null} then a UUID will be generated for use as
261       *                     the task ID.
262       * @param  baseDN      The base DN to use for the search.  It must not be
263       *                     {@code null}.
264       * @param  scope       The scope to use for the search.  It must not be
265       *                     {@code null}.
266       * @param  filter      The filter to use for the search.  It must not be
267       *                     {@code null}.
268       * @param  attributes  The list of attributes to include in matching entries.
269       *                     If it is {@code null} or empty, then all user
270       *                     attributes will be selected.
271       * @param  outputFile  The path to the file (on the server filesystem) to
272       *                     which the results should be written.  It must not be
273       *                     {@code null}.
274       */
275      public SearchTask(final String taskID, final String baseDN,
276                        final SearchScope scope, final Filter filter,
277                        final List<String> attributes, final String outputFile)
278      {
279        this(taskID, baseDN, scope, filter, attributes, outputFile, null, null,
280             null, null, null, null);
281      }
282    
283    
284    
285      /**
286       * Creates a new search task with the provided information.
287       *
288       * @param  taskID      The task ID to use for this task.  If it is
289       *                     {@code null} then a UUID will be generated for use as
290       *                     the task ID.
291       * @param  baseDN      The base DN to use for the search.  It must not be
292       *                     {@code null}.
293       * @param  scope       The scope to use for the search.  It must not be
294       *                     {@code null}.
295       * @param  filter      The filter to use for the search.  It must not be
296       *                     {@code null}.
297       * @param  attributes  The list of attributes to include in matching entries.
298       *                     If it is {@code null} or empty, then all user
299       *                     attributes will be selected.
300       * @param  outputFile  The path to the file (on the server filesystem) to
301       *                     which the results should be written.  It must not be
302       *                     {@code null}.
303       * @param  authzDN     The DN of the user as whom the search should be
304       *                     processed.  If this is {@code null}, then it will be
305       *                     processed as an internal root user.
306       */
307      public SearchTask(final String taskID, final String baseDN,
308                        final SearchScope scope, final Filter filter,
309                        final List<String> attributes, final String outputFile,
310                        final String authzDN)
311      {
312        this(taskID, baseDN, scope, filter, attributes, outputFile, authzDN, null,
313             null, null, null, null);
314      }
315    
316    
317    
318      /**
319       * Creates a new search task with the provided information.
320       *
321       * @param  taskID                  The task ID to use for this task.  If it is
322       *                                 {@code null} then a UUID will be generated
323       *                                 for use as the task ID.
324       * @param  baseDN                  The base DN to use for the search.  It must
325       *                                 not be {@code null}.
326       * @param  scope                   The scope to use for the search.  It must
327       *                                 not be {@code null}.
328       * @param  filter                  The filter to use for the search.  It must
329       *                                 not be {@code null}.
330       * @param  attributes              The list of attributes to include in
331       *                                 matching entries.  If it is {@code null} or
332       *                                 empty, then all user attributes will be
333       *                                 selected.
334       * @param  outputFile              The path to the file (on the server
335       *                                 filesystem) to which the results should be
336       *                                 written.  It must not be {@code null}.
337       * @param  authzDN                 The DN of the user as whom the search
338       *                                 should be processed.  If this is
339       *                                 {@code null}, then it will be processed as
340       *                                 an internal root user.
341       * @param  scheduledStartTime      The time that this task should start
342       *                                 running.
343       * @param  dependencyIDs           The list of task IDs that will be required
344       *                                 to complete before this task will be
345       *                                 eligible to start.
346       * @param  failedDependencyAction  Indicates what action should be taken if
347       *                                 any of the dependencies for this task do
348       *                                 not complete successfully.
349       * @param  notifyOnCompletion      The list of e-mail addresses of individuals
350       *                                 that should be notified when this task
351       *                                 completes.
352       * @param  notifyOnError           The list of e-mail addresses of individuals
353       *                                 that should be notified if this task does
354       *                                 not complete successfully.
355       */
356      public SearchTask(final String taskID, final String baseDN,
357                        final SearchScope scope, final Filter filter,
358                        final List<String> attributes, final String outputFile,
359                        final String authzDN,  final Date scheduledStartTime,
360                        final List<String> dependencyIDs,
361                        final FailedDependencyAction failedDependencyAction,
362                        final List<String> notifyOnCompletion,
363                        final List<String> notifyOnError)
364      {
365        super(taskID, SEARCH_TASK_CLASS, scheduledStartTime, dependencyIDs,
366             failedDependencyAction, notifyOnCompletion, notifyOnError);
367    
368        ensureNotNull(baseDN, scope, filter, outputFile);
369    
370        this.baseDN     = baseDN;
371        this.scope      = scope;
372        this.filter     = filter;
373        this.outputFile = outputFile;
374        this.authzDN    = authzDN;
375    
376        if (attributes == null)
377        {
378          this.attributes = Collections.emptyList();
379        }
380        else
381        {
382          this.attributes = Collections.unmodifiableList(attributes);
383        }
384      }
385    
386    
387    
388      /**
389       * Creates a new search task from the provided entry.
390       *
391       * @param  entry  The entry to use to create this search task.
392       *
393       * @throws  TaskException  If the provided entry cannot be parsed as a search
394       *                         task entry.
395       */
396      public SearchTask(final Entry entry)
397             throws TaskException
398      {
399        super(entry);
400    
401    
402        // Get the base DN.  It must be present.
403        baseDN = entry.getAttributeValue(ATTR_BASE_DN);
404        if (baseDN == null)
405        {
406          throw new TaskException(ERR_SEARCH_TASK_ENTRY_NO_BASE_DN.get(
407               entry.getDN()));
408        }
409    
410    
411        // Get the scope.  It must be present.
412        final String scopeStr = toLowerCase(entry.getAttributeValue(ATTR_SCOPE));
413        if (scopeStr == null)
414        {
415          throw new TaskException(ERR_SEARCH_TASK_ENTRY_NO_SCOPE.get(
416               entry.getDN()));
417        }
418    
419        if (scopeStr.equals("base") || scopeStr.equals("baseobject") ||
420            scopeStr.equals("0"))
421        {
422          scope = SearchScope.BASE;
423        }
424        else if (scopeStr.equals("one") || scopeStr.equals("onelevel") ||
425                 scopeStr.equals("singlelevel") || scopeStr.equals("1"))
426        {
427          scope = SearchScope.ONE;
428        }
429        else if (scopeStr.equals("sub") || scopeStr.equals("subtree") ||
430                 scopeStr.equals("wholesubtree") || scopeStr.equals("2"))
431        {
432          scope = SearchScope.SUB;
433        }
434        else if (scopeStr.equals("subord") || scopeStr.equals("subordinate") ||
435                 scopeStr.equals("subordinatesubtree") || scopeStr.equals("3"))
436        {
437          scope = SearchScope.SUBORDINATE_SUBTREE;
438        }
439        else
440        {
441          throw new TaskException(ERR_SEARCH_TASK_ENTRY_INVALID_SCOPE.get(
442               entry.getDN(), scopeStr));
443        }
444    
445    
446        // Get the filter.  It must be present.
447        final String filterStr = entry.getAttributeValue(ATTR_FILTER);
448        if (filterStr == null)
449        {
450          throw new TaskException(ERR_SEARCH_TASK_ENTRY_NO_FILTER.get(
451               entry.getDN()));
452        }
453        try
454        {
455          filter = Filter.create(filterStr);
456        }
457        catch (final LDAPException le)
458        {
459          debugException(le);
460          throw new TaskException(ERR_SEARCH_TASK_ENTRY_INVALID_FILTER.get(
461               entry.getDN(), filterStr), le);
462        }
463    
464    
465        // Get the list of requested attributes.  It is optional.
466        final String[] attrs = entry.getAttributeValues(ATTR_RETURN_ATTR);
467        if (attrs == null)
468        {
469          attributes = Collections.emptyList();
470        }
471        else
472        {
473          attributes = Collections.unmodifiableList(Arrays.asList(attrs));
474        }
475    
476    
477        // Get the authorization DN.  It is optional.
478        authzDN = entry.getAttributeValue(ATTR_AUTHZ_DN);
479    
480    
481        // Get the path to the output file.  It must be present.
482        outputFile = entry.getAttributeValue(ATTR_OUTPUT_FILE);
483        if (outputFile == null)
484        {
485          throw new TaskException(ERR_SEARCH_TASK_ENTRY_NO_OUTPUT_FILE.get(
486               entry.getDN()));
487        }
488      }
489    
490    
491    
492      /**
493       * Creates a new search task from the provided set of task properties.
494       *
495       * @param  properties  The set of task properties and their corresponding
496       *                     values to use for the task.  It must not be
497       *                     {@code null}.
498       *
499       * @throws  TaskException  If the provided set of properties cannot be used to
500       *                         create a valid add schema file task.
501       */
502      public SearchTask(final Map<TaskProperty,List<Object>> properties)
503             throws TaskException
504      {
505        super(SEARCH_TASK_CLASS, properties);
506    
507        Filter      tmpFilter  = null;
508        SearchScope tmpScope   = null;
509        String      tmpAuthzDN = null;
510        String      tmpBaseDN  = null;
511        String      tmpFile    = null;
512        String[]    tmpAttrs   = null;
513    
514        for (final Map.Entry<TaskProperty,List<Object>> entry :
515             properties.entrySet())
516        {
517          final TaskProperty p = entry.getKey();
518          final String attrName = toLowerCase(p.getAttributeName());
519          final List<Object> values = entry.getValue();
520    
521          if (attrName.equals(ATTR_BASE_DN))
522          {
523            tmpBaseDN = parseString(p, values, null);
524          }
525          else if (attrName.equals(ATTR_SCOPE))
526          {
527            final String scopeStr = toLowerCase(parseString(p, values, null));
528            if (scopeStr != null)
529            {
530              if (scopeStr.equals("base") || scopeStr.equals("baseobject") ||
531                   scopeStr.equals("0"))
532              {
533                tmpScope = SearchScope.BASE;
534              }
535              else if (scopeStr.equals("one") || scopeStr.equals("onelevel") ||
536                   scopeStr.equals("singlelevel") || scopeStr.equals("1"))
537              {
538                tmpScope = SearchScope.ONE;
539              }
540              else if (scopeStr.equals("sub") || scopeStr.equals("subtree") ||
541                       scopeStr.equals("wholesubtree") || scopeStr.equals("2"))
542              {
543                tmpScope = SearchScope.SUB;
544              }
545              else if (scopeStr.equals("subord") ||
546                       scopeStr.equals("subordinate") ||
547                       scopeStr.equals("subordinatesubtree") ||
548                       scopeStr.equals("3"))
549              {
550                tmpScope = SearchScope.SUBORDINATE_SUBTREE;
551              }
552              else
553              {
554                throw new TaskException(ERR_SEARCH_TASK_INVALID_SCOPE_PROPERTY.get(
555                     scopeStr));
556              }
557            }
558          }
559          else if (attrName.equals(ATTR_FILTER))
560          {
561            final String filterStr = parseString(p, values, null);
562            if (filterStr != null)
563            {
564              try
565              {
566                tmpFilter = Filter.create(filterStr);
567              }
568              catch (final LDAPException le)
569              {
570                debugException(le);
571                throw new TaskException(ERR_SEARCH_TASK_INVALID_FILTER_PROPERTY.get(
572                     filterStr), le);
573              }
574            }
575          }
576          else if (attrName.equals(ATTR_RETURN_ATTR))
577          {
578            tmpAttrs = parseStrings(p, values, null);
579          }
580          else if (attrName.equals(ATTR_OUTPUT_FILE))
581          {
582            tmpFile = parseString(p, values, null);
583          }
584          else if (attrName.equals(ATTR_AUTHZ_DN))
585          {
586            tmpAuthzDN = parseString(p, values, null);
587          }
588        }
589    
590        baseDN = tmpBaseDN;
591        if (baseDN == null)
592        {
593          throw new TaskException(ERR_SEARCH_TASK_NO_BASE_PROPERTY.get());
594        }
595    
596        scope = tmpScope;
597        if (scope == null)
598        {
599          throw new TaskException(ERR_SEARCH_TASK_NO_SCOPE_PROPERTY.get());
600        }
601    
602        filter = tmpFilter;
603        if (filter == null)
604        {
605          throw new TaskException(ERR_SEARCH_TASK_NO_FILTER_PROPERTY.get());
606        }
607    
608        outputFile = tmpFile;
609        if (outputFile == null)
610        {
611          throw new TaskException(ERR_SEARCH_TASK_NO_OUTPUT_FILE_PROPERTY.get());
612        }
613    
614    
615        if (tmpAttrs == null)
616        {
617          attributes = Collections.emptyList();
618        }
619        else
620        {
621          attributes = Collections.unmodifiableList(Arrays.asList(tmpAttrs));
622        }
623    
624        authzDN = tmpAuthzDN;
625      }
626    
627    
628    
629      /**
630       * {@inheritDoc}
631       */
632      @Override()
633      public String getTaskName()
634      {
635        return INFO_TASK_NAME_SEARCH.get();
636      }
637    
638    
639    
640      /**
641       * {@inheritDoc}
642       */
643      @Override()
644      public String getTaskDescription()
645      {
646        return INFO_TASK_DESCRIPTION_SEARCH.get();
647      }
648    
649    
650    
651      /**
652       * Retrieves the base DN for the search.
653       *
654       * @return  The base DN for the search.
655       */
656      public String getBaseDN()
657      {
658        return baseDN;
659      }
660    
661    
662    
663      /**
664       * Retrieves the scope for the search.
665       *
666       * @return  The scope for the search.
667       */
668      public SearchScope getScope()
669      {
670        return scope;
671      }
672    
673    
674    
675      /**
676       * Retrieves the filter for the search.
677       *
678       * @return  The filter for the search.
679       */
680      public Filter getFilter()
681      {
682        return filter;
683      }
684    
685    
686    
687      /**
688       * Retrieves the list of attributes to include in matching entries.
689       *
690       * @return  The list of attributes to include in matching entries, or an
691       *          empty list of all user attributes should be requested.
692       */
693      public List<String> getAttributes()
694      {
695        return attributes;
696      }
697    
698    
699    
700      /**
701       * Retrieves the DN of the user as whom the request should be processed.
702       *
703       * @return  The DN of the user as whom the request should be processed, or
704       *          {@code null} if it should be processed as an internal root user.
705       */
706      public String getAuthzDN()
707      {
708        return authzDN;
709      }
710    
711    
712    
713      /**
714       * Retrieves the path to the file on the server filesystem to which the
715       * results should be written.
716       *
717       * @return  The path to the file on the server filesystem to which the results
718       *          should be written.
719       */
720      public String getOutputFile()
721      {
722        return outputFile;
723      }
724    
725    
726    
727      /**
728       * {@inheritDoc}
729       */
730      @Override()
731      protected List<String> getAdditionalObjectClasses()
732      {
733        return Arrays.asList(OC_SEARCH_TASK);
734      }
735    
736    
737    
738      /**
739       * {@inheritDoc}
740       */
741      @Override()
742      protected List<Attribute> getAdditionalAttributes()
743      {
744        final LinkedList<Attribute> attrs = new LinkedList<Attribute>();
745    
746        attrs.add(new Attribute(ATTR_BASE_DN, baseDN));
747        attrs.add(new Attribute(ATTR_SCOPE, String.valueOf(scope.intValue())));
748        attrs.add(new Attribute(ATTR_FILTER, filter.toString()));
749        attrs.add(new Attribute(ATTR_OUTPUT_FILE, outputFile));
750    
751        if ((attributes != null) && (! attributes.isEmpty()))
752        {
753          attrs.add(new Attribute(ATTR_RETURN_ATTR, attributes));
754        }
755    
756        if (authzDN != null)
757        {
758          attrs.add(new Attribute(ATTR_AUTHZ_DN, authzDN));
759        }
760    
761        return Collections.unmodifiableList(attrs);
762      }
763    
764    
765    
766      /**
767       * {@inheritDoc}
768       */
769      @Override()
770      public List<TaskProperty> getTaskSpecificProperties()
771      {
772        final LinkedList<TaskProperty> props = new LinkedList<TaskProperty>();
773    
774        props.add(PROPERTY_BASE_DN);
775        props.add(PROPERTY_SCOPE);
776        props.add(PROPERTY_FILTER);
777        props.add(PROPERTY_REQUESTED_ATTR);
778        props.add(PROPERTY_AUTHZ_DN);
779        props.add(PROPERTY_OUTPUT_FILE);
780    
781        return Collections.unmodifiableList(props);
782      }
783    
784    
785    
786      /**
787       * {@inheritDoc}
788       */
789      @Override()
790      public Map<TaskProperty,List<Object>> getTaskPropertyValues()
791      {
792        final LinkedHashMap<TaskProperty,List<Object>> props =
793             new LinkedHashMap<TaskProperty,List<Object>>(6);
794    
795        props.put(PROPERTY_BASE_DN,
796             Collections.<Object>unmodifiableList(Arrays.asList(baseDN)));
797    
798        props.put(PROPERTY_SCOPE, Collections.<Object>unmodifiableList(
799             Arrays.asList(String.valueOf(scope.intValue()))));
800    
801        props.put(PROPERTY_FILTER, Collections.<Object>unmodifiableList(
802             Arrays.asList(filter.toString())));
803    
804        if ((attributes != null) && (! attributes.isEmpty()))
805        {
806          final LinkedList<Object> attrObjects = new LinkedList<Object>();
807          attrObjects.addAll(attributes);
808    
809          props.put(PROPERTY_REQUESTED_ATTR,
810               Collections.unmodifiableList(attrObjects));
811        }
812    
813        if (authzDN != null)
814        {
815          props.put(PROPERTY_AUTHZ_DN,
816               Collections.<Object>unmodifiableList(Arrays.asList(authzDN)));
817        }
818    
819        props.put(PROPERTY_OUTPUT_FILE,
820             Collections.<Object>unmodifiableList(Arrays.asList(outputFile)));
821    
822        props.putAll(super.getTaskPropertyValues());
823        return Collections.unmodifiableMap(props);
824      }
825    }