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 }