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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2009 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS. 026 */ 027package org.opends.server.types; 028 029import java.io.*; 030import java.util.ArrayList; 031import java.util.HashSet; 032import java.util.List; 033import java.util.Set; 034import java.util.zip.GZIPOutputStream; 035 036import org.forgerock.i18n.LocalizableMessage; 037import org.forgerock.i18n.slf4j.LocalizedLogger; 038import org.opends.server.util.StaticUtils; 039 040import static org.opends.messages.UtilityMessages.*; 041import static org.opends.server.util.StaticUtils.*; 042 043/** 044 * This class defines a data structure for holding configuration 045 * information to use when performing an LDIF export. 046 */ 047@org.opends.server.types.PublicAPI( 048 stability=org.opends.server.types.StabilityLevel.VOLATILE, 049 mayInstantiate=true, 050 mayExtend=false, 051 mayInvoke=true) 052public final class LDIFExportConfig extends OperationConfig 053 implements Closeable 054{ 055 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 056 057 /** Indicates whether the data should be compressed as it is written. */ 058 private boolean compressData; 059 060 /** Indicates whether the data should be encrypted as it is written. */ 061 private boolean encryptData; 062 063 /** 064 * Indicates whether to generate a cryptographic hash of the data as it is 065 * written. 066 */ 067 private boolean hashData; 068 069 /** 070 * Indicates whether to include the objectclasses in the entries written in 071 * the export. 072 */ 073 private boolean includeObjectClasses; 074 075 /** 076 * Indicates whether to include operational attributes in the export. 077 */ 078 private boolean includeOperationalAttributes; 079 080 /** Indicates whether to include virtual attributes in the export. */ 081 private boolean includeVirtualAttributes; 082 083 /** 084 * Indicates whether to invoke LDIF export plugins on entries being exported. 085 */ 086 private boolean invokeExportPlugins; 087 088 /** 089 * Indicates whether to digitally sign the hash when the export is complete. 090 */ 091 private boolean signHash; 092 093 /** 094 * Indicates whether to include attribute types (i.e., names) only or both 095 * types and values. 096 */ 097 private boolean typesOnly; 098 099 /** The buffered writer to which the LDIF data should be written. */ 100 private BufferedWriter writer; 101 102 /** 103 * The behavior that should be used when writing an LDIF file and a file with 104 * the same name already exists. 105 */ 106 private ExistingFileBehavior existingFileBehavior; 107 108 /** The column number at which long lines should be wrapped. */ 109 private int wrapColumn; 110 111 /** The set of base DNs to exclude from the export. */ 112 private List<DN> excludeBranches; 113 114 /** The set of base DNs to include from the export. */ 115 private List<DN> includeBranches; 116 117 /** The set of search filters for entries to exclude from the export. */ 118 private List<SearchFilter> excludeFilters; 119 120 /** The set of search filters for entries to include in the export. */ 121 private List<SearchFilter> includeFilters; 122 123 /** The output stream to which the LDIF data should be written. */ 124 private OutputStream ldifOutputStream; 125 126 /** 127 * The set of attribute types that should be excluded from the export. 128 */ 129 private Set<AttributeType> excludeAttributes; 130 131 /** The set of attribute types that should be included in the export. */ 132 private Set<AttributeType> includeAttributes; 133 134 /** The path to the LDIF file that should be written. */ 135 private String ldifFile; 136 137 138 139 /** 140 * Creates a new LDIF export configuration that will write to the 141 * specified LDIF file. 142 * 143 * @param ldifFile The path to the LDIF file to 144 * export. 145 * @param existingFileBehavior Indicates how to proceed if the 146 * specified file already exists. 147 */ 148 public LDIFExportConfig(String ldifFile, 149 ExistingFileBehavior existingFileBehavior) 150 { 151 this.ldifFile = ldifFile; 152 this.existingFileBehavior = existingFileBehavior; 153 ldifOutputStream = null; 154 155 excludeBranches = new ArrayList<>(); 156 includeBranches = new ArrayList<>(); 157 excludeFilters = new ArrayList<>(); 158 includeFilters = new ArrayList<>(); 159 compressData = false; 160 encryptData = false; 161 hashData = false; 162 includeObjectClasses = true; 163 includeOperationalAttributes = true; 164 includeVirtualAttributes = false; 165 invokeExportPlugins = false; 166 signHash = false; 167 typesOnly = false; 168 writer = null; 169 excludeAttributes = new HashSet<>(); 170 includeAttributes = new HashSet<>(); 171 wrapColumn = -1; 172 } 173 174 175 176 /** 177 * Creates a new LDIF export configuration that will write to the 178 * provided output stream. 179 * 180 * @param ldifOutputStream The output stream to which the LDIF 181 * data should be written. 182 */ 183 public LDIFExportConfig(OutputStream ldifOutputStream) 184 { 185 this.ldifOutputStream = ldifOutputStream; 186 ldifFile = null; 187 existingFileBehavior = ExistingFileBehavior.FAIL; 188 189 excludeBranches = new ArrayList<>(); 190 includeBranches = new ArrayList<>(); 191 excludeFilters = new ArrayList<>(); 192 includeFilters = new ArrayList<>(); 193 compressData = false; 194 encryptData = false; 195 hashData = false; 196 includeObjectClasses = true; 197 includeOperationalAttributes = true; 198 includeVirtualAttributes = false; 199 invokeExportPlugins = false; 200 signHash = false; 201 typesOnly = false; 202 writer = null; 203 excludeAttributes = new HashSet<>(); 204 includeAttributes = new HashSet<>(); 205 wrapColumn = -1; 206 } 207 208 209 210 /** 211 * Retrieves the writer that should be used to write the LDIF data. 212 * If compression or encryption are to be used, then they must be 213 * enabled before the first call to this method. 214 * 215 * @return The writer that should be used to write the LDIF data. 216 * 217 * @throws IOException If a problem occurs while preparing the 218 * writer. 219 */ 220 public BufferedWriter getWriter() 221 throws IOException 222 { 223 if (writer == null) 224 { 225 if (ldifOutputStream == null) 226 { 227 File f = new File(ldifFile); 228 boolean mustSetPermissions = false; 229 230 switch (existingFileBehavior) 231 { 232 case APPEND: 233 // Create new file if it doesn't exist ensuring that we can 234 // set its permissions. 235 if (!f.exists()) 236 { 237 f.createNewFile(); 238 mustSetPermissions = true; 239 } 240 ldifOutputStream = new FileOutputStream(ldifFile, true); 241 break; 242 case OVERWRITE: 243 // Create new file if it doesn't exist ensuring that we can 244 // set its permissions. 245 if (!f.exists()) 246 { 247 f.createNewFile(); 248 mustSetPermissions = true; 249 } 250 ldifOutputStream = new FileOutputStream(ldifFile, false); 251 break; 252 case FAIL: 253 if (f.exists()) 254 { 255 LocalizableMessage message = ERR_LDIF_FILE_EXISTS.get(ldifFile); 256 throw new IOException(message.toString()); 257 } 258 else 259 { 260 // Create new file ensuring that we can set its permissions. 261 f.createNewFile(); 262 mustSetPermissions = true; 263 ldifOutputStream = new FileOutputStream(ldifFile); 264 } 265 break; 266 } 267 268 if (mustSetPermissions) 269 { 270 try 271 { 272 // Ignore 273 FilePermission.setPermissions(f, 274 new FilePermission(0600)); 275 } 276 catch (Exception e) 277 { 278 // The file could not be created with the correct permissions. 279 LocalizableMessage message = WARN_EXPORT_LDIF_SET_PERMISSION_FAILED 280 .get(f, stackTraceToSingleLineString(e)); 281 throw new IOException(message.toString()); 282 } 283 } 284 } 285 286 287 // See if we should compress the output. 288 OutputStream outputStream; 289 if (compressData) 290 { 291 outputStream = new GZIPOutputStream(ldifOutputStream); 292 } 293 else 294 { 295 outputStream = ldifOutputStream; 296 } 297 298 299 // See if we should encrypt the output. 300 if (encryptData) 301 { 302 // FIXME -- Implement this. 303 } 304 305 306 // Create the writer. 307 writer = new BufferedWriter(new OutputStreamWriter(outputStream)); 308 } 309 310 return writer; 311 } 312 313 314 315 /** 316 * Indicates whether the LDIF export plugins should be invoked for 317 * entries as they are exported. 318 * 319 * @return <CODE>true</CODE> if LDIF export plugins should be 320 * invoked for entries as they are exported, or 321 * <CODE>false</CODE> if not. 322 */ 323 public boolean invokeExportPlugins() 324 { 325 return invokeExportPlugins; 326 } 327 328 329 330 /** 331 * Specifies whether the LDIF export plugins should be invoked for 332 * entries as they are exported. 333 * 334 * @param invokeExportPlugins Specifies whether the LDIF export 335 * plugins should be invoked for 336 * entries as they are exported. 337 */ 338 public void setInvokeExportPlugins(boolean invokeExportPlugins) 339 { 340 this.invokeExportPlugins = invokeExportPlugins; 341 } 342 343 344 345 /** 346 * Indicates whether the LDIF data should be compressed as it is 347 * written. 348 * 349 * @return <CODE>true</CODE> if the LDIF data should be compressed 350 * as it is written, or <CODE>false</CODE> if not. 351 */ 352 public boolean compressData() 353 { 354 return compressData; 355 } 356 357 358 359 /** 360 * Specifies whether the LDIF data should be compressed as it is 361 * written. If compression should be used, then this must be set 362 * before calling <CODE>getWriter</CODE> for the first time. 363 * 364 * @param compressData Indicates whether the LDIF data should be 365 * compressed as it is written. 366 */ 367 public void setCompressData(boolean compressData) 368 { 369 this.compressData = compressData; 370 } 371 372 373 374 /** 375 * Indicates whether the LDIF data should be encrypted as it is 376 * written. 377 * 378 * @return <CODE>true</CODE> if the LDIF data should be encrypted 379 * as it is written, or <CODE>false</CODE> if not. 380 */ 381 public boolean encryptData() 382 { 383 return encryptData; 384 } 385 386 387 388 /** 389 * Specifies whether the LDIF data should be encrypted as it is 390 * written. If encryption should be used, then this must be set 391 * before calling <CODE>getWriter</CODE> for the first time. 392 * 393 * @param encryptData Indicates whether the LDIF data should be 394 * encrypted as it is written. 395 */ 396 public void setEncryptData(boolean encryptData) 397 { 398 this.encryptData = encryptData; 399 } 400 401 402 403 /** 404 * Indicates whether to generate a cryptographic hash of the data 405 * that is written. 406 * 407 * @return <CODE>true</CODE> if a hash should be generated as the 408 * data is written, or <CODE>false</CODE> if not. 409 */ 410 public boolean hashData() 411 { 412 return hashData; 413 } 414 415 416 417 /** 418 * Specifies whether to generate a cryptographic hash of the data 419 * that is written. If hashing is to be used, then this must be set 420 * before calling <CODE>getWriter</CODE> for the first time. 421 * 422 * @param hashData Indicates whether to generate a hash of the 423 * data as it is written. 424 */ 425 public void setHashData(boolean hashData) 426 { 427 this.hashData = hashData; 428 } 429 430 431 432 /** 433 * Indicates whether to sign the cryptographic hash of the data that 434 * is written when the export is complete. 435 * 436 * @return <CODE>true</CODE> if the hash should be signed when the 437 * export is complete, or <CODE>false</CODE> if not. 438 */ 439 public boolean signHash() 440 { 441 return signHash; 442 } 443 444 445 446 /** 447 * Specifies whether to sign the cryptographic hash of the data that 448 * is written when the export is complete. If the export is not 449 * configured to generate a hash, then this will be ignored. If 450 * hashing is to be used and the hash should be signed, then this 451 * must be set before calling <CODE>getWriter</CODE> for the first 452 * time. 453 * 454 * @param signHash Indicates whether to generate a hash of the 455 * data as it is written. 456 */ 457 public void setSignHash(boolean signHash) 458 { 459 this.signHash = signHash; 460 } 461 462 463 464 /** 465 * Indicates whether the LDIF generated should include attribute 466 * types (i.e., attribute names) only or both attribute types and 467 * values. 468 * 469 * @return <CODE>true</CODE> if only attribute types should be 470 * included in the resulting LDIF, or <CODE>false</CODE> if 471 * both types and values should be included. 472 */ 473 public boolean typesOnly() 474 { 475 return typesOnly; 476 } 477 478 479 480 /** 481 * Specifies whether the LDIF generated should include attribute 482 * types (i.e., attribute names) only or both attribute types and 483 * values. 484 * 485 * @param typesOnly Specifies whether the LDIF generated should 486 * include attribute types only or both attribute 487 * types and values. 488 */ 489 public void setTypesOnly(boolean typesOnly) 490 { 491 this.typesOnly = typesOnly; 492 } 493 494 495 496 /** 497 * Retrieves the column at which long lines should be wrapped. 498 * 499 * @return The column at which long lines should be wrapped, or a 500 * value less than or equal to zero to indicate that no 501 * wrapping should be performed. 502 */ 503 public int getWrapColumn() 504 { 505 return wrapColumn; 506 } 507 508 509 510 /** 511 * Specifies the column at which long lines should be wrapped. A 512 * value less than or equal to zero indicates that no wrapping 513 * should be performed. 514 * 515 * @param wrapColumn The column at which long lines should be 516 * wrapped. 517 */ 518 public void setWrapColumn(int wrapColumn) 519 { 520 this.wrapColumn = wrapColumn; 521 } 522 523 524 525 /** 526 * Retrieves the set of base DNs that specify the set of entries to 527 * exclude from the export. The list that is returned may be 528 * altered by the caller. 529 * 530 * @return The set of base DNs that specify the set of entries to 531 * exclude from the export. 532 */ 533 public List<DN> getExcludeBranches() 534 { 535 return excludeBranches; 536 } 537 538 539 540 /** 541 * Specifies the set of base DNs that specify the set of entries to 542 * exclude from the export. 543 * 544 * @param excludeBranches The set of base DNs that specify the set 545 * of entries to exclude from the export. 546 */ 547 public void setExcludeBranches(List<DN> excludeBranches) 548 { 549 if (excludeBranches == null) 550 { 551 this.excludeBranches = new ArrayList<>(0); 552 } 553 else 554 { 555 this.excludeBranches = excludeBranches; 556 } 557 } 558 559 560 561 /** 562 * Retrieves the set of base DNs that specify the set of entries to 563 * include in the export. The list that is returned may be altered 564 * by the caller. 565 * 566 * @return The set of base DNs that specify the set of entries to 567 * include in the export. 568 */ 569 public List<DN> getIncludeBranches() 570 { 571 return includeBranches; 572 } 573 574 575 576 /** 577 * Specifies the set of base DNs that specify the set of entries to 578 * include in the export. 579 * 580 * @param includeBranches The set of base DNs that specify the set 581 * of entries to include in the export. 582 */ 583 public void setIncludeBranches(List<DN> includeBranches) 584 { 585 if (includeBranches == null) 586 { 587 this.includeBranches = new ArrayList<>(0); 588 } 589 else 590 { 591 this.includeBranches = includeBranches; 592 } 593 } 594 595 596 597 /** 598 * Indicates whether the set of objectclasses should be included in 599 * the entries written to LDIF. 600 * 601 * @return <CODE>true</CODE> if the set of objectclasses should be 602 * included in the entries written to LDIF, or 603 * <CODE>false</CODE> if not. 604 */ 605 public boolean includeObjectClasses() 606 { 607 return includeObjectClasses; 608 } 609 610 611 612 /** 613 * Indicates whether the set of operational attributes should be 614 * included in the export. 615 * 616 * @return <CODE>true</CODE> if the set of operational attributes 617 * should be included in the export. 618 */ 619 public boolean includeOperationalAttributes() 620 { 621 return includeOperationalAttributes; 622 } 623 624 625 626 /** 627 * Specifies whether the objectclasss attribute should be 628 * included in the export. 629 * 630 * @param includeObjectClasses Specifies whether the 631 * objectclass attribute 632 * should be included in the 633 * export. 634 */ 635 public void setIncludeObjectClasses(boolean includeObjectClasses) 636 { 637 this.includeObjectClasses = includeObjectClasses; 638 } 639 640 /** 641 * Specifies whether the set of operational attributes should be 642 * included in the export. 643 * 644 * @param includeOperationalAttributes Specifies whether the set 645 * of operational attributes 646 * should be included in the 647 * export. 648 */ 649 public void setIncludeOperationalAttributes( 650 boolean includeOperationalAttributes) 651 { 652 this.includeOperationalAttributes = includeOperationalAttributes; 653 } 654 655 656 657 /** 658 * Indicates whether virtual attributes should be included in the 659 * export. 660 * 661 * @return {@code true} if virtual attributes should be included in 662 * the export, or {@code false} if not. 663 */ 664 public boolean includeVirtualAttributes() 665 { 666 return includeVirtualAttributes; 667 } 668 669 670 671 /** 672 * Specifies whether virtual attributes should be included in the 673 * export. 674 * 675 * @param includeVirtualAttributes Specifies whether virtual 676 * attributes should be included 677 * in the export. 678 */ 679 public void setIncludeVirtualAttributes( 680 boolean includeVirtualAttributes) 681 { 682 this.includeVirtualAttributes = includeVirtualAttributes; 683 } 684 685 686 687 /** 688 * Retrieves the set of attributes that should be excluded from the 689 * entries written to LDIF. The set that is returned may be altered 690 * by the caller. 691 * 692 * @return The set of attributes that should be excluded from the 693 * entries written to LDIF. 694 */ 695 public Set<AttributeType> getExcludeAttributes() 696 { 697 return excludeAttributes; 698 } 699 700 701 702 /** 703 * Specifies the set of attributes that should be excluded from the 704 * entries written to LDIF. 705 * 706 * @param excludeAttributes The set of attributes that should be 707 * excluded from the entries written to 708 * LDIF. 709 */ 710 public void setExcludeAttributes( 711 Set<AttributeType> excludeAttributes) 712 { 713 if (excludeAttributes == null) 714 { 715 this.excludeAttributes = new HashSet<>(0); 716 } 717 else 718 { 719 this.excludeAttributes = excludeAttributes; 720 } 721 } 722 723 724 725 /** 726 * Retrieves the set of attributes that should be included in the 727 * entries written to LDIF. The set that is returned may be altered 728 * by the caller. 729 * 730 * @return The set of attributes that should be included in the 731 * entries written to LDIF. 732 */ 733 public Set<AttributeType> getIncludeAttributes() 734 { 735 return includeAttributes; 736 } 737 738 739 740 /** 741 * Specifies the set of attributes that should be included in the 742 * entries written to LDIF. 743 * 744 * @param includeAttributes The set of attributes that should be 745 * included in the entries written to 746 * LDIF. 747 */ 748 public void setIncludeAttributes(Set<AttributeType> includeAttributes) 749 { 750 if (includeAttributes == null) 751 { 752 this.includeAttributes = new HashSet<>(0); 753 } 754 else 755 { 756 this.includeAttributes = includeAttributes; 757 } 758 } 759 760 761 762 /** 763 * Indicates whether the specified attribute should be included in 764 * the entries written to LDIF. 765 * 766 * @param attributeType The attribute type for which to make the 767 * determination. 768 * 769 * @return <CODE>true</CODE> if the specified attribute should be 770 * included in the entries written to LDIF, or 771 * <CODE>false</CODE> if not. 772 */ 773 public boolean includeAttribute(AttributeType attributeType) 774 { 775 return (excludeAttributes.isEmpty() 776 || !excludeAttributes.contains(attributeType)) 777 && (includeAttributes.isEmpty() 778 || includeAttributes.contains(attributeType)); 779 } 780 781 782 783 /** 784 * Retrieves the set of search filters that should be used to 785 * determine which entries to exclude from the LDIF. The list that 786 * is returned may be altered by the caller. 787 * 788 * @return The set of search filters that should be used to 789 * determine which entries to exclude from the LDIF. 790 */ 791 public List<SearchFilter> getExcludeFilters() 792 { 793 return excludeFilters; 794 } 795 796 797 798 /** 799 * Specifies the set of search filters that should be used to 800 * determine which entries to exclude from the LDIF. 801 * 802 * @param excludeFilters The set of search filters that should be 803 * used to determine which entries to 804 * exclude from the LDIF. 805 */ 806 public void setExcludeFilters(List<SearchFilter> excludeFilters) 807 { 808 if (excludeFilters == null) 809 { 810 this.excludeFilters = new ArrayList<>(0); 811 } 812 else 813 { 814 this.excludeFilters = excludeFilters; 815 } 816 } 817 818 819 820 /** 821 * Retrieves the set of search filters that should be used to 822 * determine which entries to include in the LDIF. The list that is 823 * returned may be altered by the caller. 824 * 825 * @return The set of search filters that should be used to 826 * determine which entries to include in the LDIF. 827 */ 828 public List<SearchFilter> getIncludeFilters() 829 { 830 return includeFilters; 831 } 832 833 834 835 /** 836 * Specifies the set of search filters that should be used to 837 * determine which entries to include in the LDIF. 838 * 839 * @param includeFilters The set of search filters that should be 840 * used to determine which entries to 841 * include in the LDIF. 842 */ 843 public void setIncludeFilters(List<SearchFilter> includeFilters) 844 { 845 if (includeFilters == null) 846 { 847 this.includeFilters = new ArrayList<>(0); 848 } 849 else 850 { 851 this.includeFilters = includeFilters; 852 } 853 } 854 855 856 857 /** 858 * Indicates whether the specified entry should be included in the 859 * export based on the configured set of include and exclude 860 * filters. 861 * 862 * @param entry The entry for which to make the determination. 863 * 864 * @return <CODE>true</CODE> if the specified entry should be 865 * included in the export, or <CODE>false</CODE> if not. 866 * 867 * @throws DirectoryException If there is a problem with any of 868 * the search filters used to make the 869 * determination. 870 */ 871 public boolean includeEntry(Entry entry) 872 throws DirectoryException 873 { 874 DN dn = entry.getName(); 875 if (! excludeBranches.isEmpty()) 876 { 877 for (DN excludeBranch : excludeBranches) 878 { 879 if (excludeBranch.isAncestorOf(dn)) 880 { 881 return false; 882 } 883 } 884 } 885 886 checkIncludeBranches: if (! includeBranches.isEmpty()) 887 { 888 for (DN includeBranch : includeBranches) 889 { 890 if (includeBranch.isAncestorOf(dn)) 891 { 892 break checkIncludeBranches; 893 } 894 } 895 896 return false; 897 } 898 899 if (! excludeFilters.isEmpty()) 900 { 901 for (SearchFilter filter : excludeFilters) 902 { 903 if (filter.matchesEntry(entry)) 904 { 905 return false; 906 } 907 } 908 } 909 910 if (! includeFilters.isEmpty()) 911 { 912 for (SearchFilter filter : includeFilters) 913 { 914 if (filter.matchesEntry(entry)) 915 { 916 return true; 917 } 918 } 919 return false; 920 } 921 922 return true; 923 } 924 925 926 927 /** 928 * Closes any resources that this export config might have open. 929 */ 930 @Override 931 public void close() 932 { 933 // FIXME -- Need to add code to generate a signed hash of the LDIF content. 934 StaticUtils.close(writer); 935 } 936}