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.setSafePermissions(f, 0600); 274 } 275 catch (Exception e) 276 { 277 // The file could not be created with the correct permissions. 278 LocalizableMessage message = WARN_EXPORT_LDIF_SET_PERMISSION_FAILED 279 .get(f, stackTraceToSingleLineString(e)); 280 throw new IOException(message.toString()); 281 } 282 } 283 } 284 285 286 // See if we should compress the output. 287 OutputStream outputStream; 288 if (compressData) 289 { 290 outputStream = new GZIPOutputStream(ldifOutputStream); 291 } 292 else 293 { 294 outputStream = ldifOutputStream; 295 } 296 297 298 // See if we should encrypt the output. 299 if (encryptData) 300 { 301 // FIXME -- Implement this. 302 } 303 304 305 // Create the writer. 306 writer = new BufferedWriter(new OutputStreamWriter(outputStream)); 307 } 308 309 return writer; 310 } 311 312 313 314 /** 315 * Indicates whether the LDIF export plugins should be invoked for 316 * entries as they are exported. 317 * 318 * @return <CODE>true</CODE> if LDIF export plugins should be 319 * invoked for entries as they are exported, or 320 * <CODE>false</CODE> if not. 321 */ 322 public boolean invokeExportPlugins() 323 { 324 return invokeExportPlugins; 325 } 326 327 328 329 /** 330 * Specifies whether the LDIF export plugins should be invoked for 331 * entries as they are exported. 332 * 333 * @param invokeExportPlugins Specifies whether the LDIF export 334 * plugins should be invoked for 335 * entries as they are exported. 336 */ 337 public void setInvokeExportPlugins(boolean invokeExportPlugins) 338 { 339 this.invokeExportPlugins = invokeExportPlugins; 340 } 341 342 343 344 /** 345 * Indicates whether the LDIF data should be compressed as it is 346 * written. 347 * 348 * @return <CODE>true</CODE> if the LDIF data should be compressed 349 * as it is written, or <CODE>false</CODE> if not. 350 */ 351 public boolean compressData() 352 { 353 return compressData; 354 } 355 356 357 358 /** 359 * Specifies whether the LDIF data should be compressed as it is 360 * written. If compression should be used, then this must be set 361 * before calling <CODE>getWriter</CODE> for the first time. 362 * 363 * @param compressData Indicates whether the LDIF data should be 364 * compressed as it is written. 365 */ 366 public void setCompressData(boolean compressData) 367 { 368 this.compressData = compressData; 369 } 370 371 372 373 /** 374 * Indicates whether the LDIF data should be encrypted as it is 375 * written. 376 * 377 * @return <CODE>true</CODE> if the LDIF data should be encrypted 378 * as it is written, or <CODE>false</CODE> if not. 379 */ 380 public boolean encryptData() 381 { 382 return encryptData; 383 } 384 385 386 387 /** 388 * Specifies whether the LDIF data should be encrypted as it is 389 * written. If encryption should be used, then this must be set 390 * before calling <CODE>getWriter</CODE> for the first time. 391 * 392 * @param encryptData Indicates whether the LDIF data should be 393 * encrypted as it is written. 394 */ 395 public void setEncryptData(boolean encryptData) 396 { 397 this.encryptData = encryptData; 398 } 399 400 401 402 /** 403 * Indicates whether to generate a cryptographic hash of the data 404 * that is written. 405 * 406 * @return <CODE>true</CODE> if a hash should be generated as the 407 * data is written, or <CODE>false</CODE> if not. 408 */ 409 public boolean hashData() 410 { 411 return hashData; 412 } 413 414 415 416 /** 417 * Specifies whether to generate a cryptographic hash of the data 418 * that is written. If hashing is to be used, then this must be set 419 * before calling <CODE>getWriter</CODE> for the first time. 420 * 421 * @param hashData Indicates whether to generate a hash of the 422 * data as it is written. 423 */ 424 public void setHashData(boolean hashData) 425 { 426 this.hashData = hashData; 427 } 428 429 430 431 /** 432 * Indicates whether to sign the cryptographic hash of the data that 433 * is written when the export is complete. 434 * 435 * @return <CODE>true</CODE> if the hash should be signed when the 436 * export is complete, or <CODE>false</CODE> if not. 437 */ 438 public boolean signHash() 439 { 440 return signHash; 441 } 442 443 444 445 /** 446 * Specifies whether to sign the cryptographic hash of the data that 447 * is written when the export is complete. If the export is not 448 * configured to generate a hash, then this will be ignored. If 449 * hashing is to be used and the hash should be signed, then this 450 * must be set before calling <CODE>getWriter</CODE> for the first 451 * time. 452 * 453 * @param signHash Indicates whether to generate a hash of the 454 * data as it is written. 455 */ 456 public void setSignHash(boolean signHash) 457 { 458 this.signHash = signHash; 459 } 460 461 462 463 /** 464 * Indicates whether the LDIF generated should include attribute 465 * types (i.e., attribute names) only or both attribute types and 466 * values. 467 * 468 * @return <CODE>true</CODE> if only attribute types should be 469 * included in the resulting LDIF, or <CODE>false</CODE> if 470 * both types and values should be included. 471 */ 472 public boolean typesOnly() 473 { 474 return typesOnly; 475 } 476 477 478 479 /** 480 * Specifies whether the LDIF generated should include attribute 481 * types (i.e., attribute names) only or both attribute types and 482 * values. 483 * 484 * @param typesOnly Specifies whether the LDIF generated should 485 * include attribute types only or both attribute 486 * types and values. 487 */ 488 public void setTypesOnly(boolean typesOnly) 489 { 490 this.typesOnly = typesOnly; 491 } 492 493 494 495 /** 496 * Retrieves the column at which long lines should be wrapped. 497 * 498 * @return The column at which long lines should be wrapped, or a 499 * value less than or equal to zero to indicate that no 500 * wrapping should be performed. 501 */ 502 public int getWrapColumn() 503 { 504 return wrapColumn; 505 } 506 507 508 509 /** 510 * Specifies the column at which long lines should be wrapped. A 511 * value less than or equal to zero indicates that no wrapping 512 * should be performed. 513 * 514 * @param wrapColumn The column at which long lines should be 515 * wrapped. 516 */ 517 public void setWrapColumn(int wrapColumn) 518 { 519 this.wrapColumn = wrapColumn; 520 } 521 522 523 524 /** 525 * Retrieves the set of base DNs that specify the set of entries to 526 * exclude from the export. The list that is returned may be 527 * altered by the caller. 528 * 529 * @return The set of base DNs that specify the set of entries to 530 * exclude from the export. 531 */ 532 public List<DN> getExcludeBranches() 533 { 534 return excludeBranches; 535 } 536 537 538 539 /** 540 * Specifies the set of base DNs that specify the set of entries to 541 * exclude from the export. 542 * 543 * @param excludeBranches The set of base DNs that specify the set 544 * of entries to exclude from the export. 545 */ 546 public void setExcludeBranches(List<DN> excludeBranches) 547 { 548 if (excludeBranches == null) 549 { 550 this.excludeBranches = new ArrayList<>(0); 551 } 552 else 553 { 554 this.excludeBranches = excludeBranches; 555 } 556 } 557 558 559 560 /** 561 * Retrieves the set of base DNs that specify the set of entries to 562 * include in the export. The list that is returned may be altered 563 * by the caller. 564 * 565 * @return The set of base DNs that specify the set of entries to 566 * include in the export. 567 */ 568 public List<DN> getIncludeBranches() 569 { 570 return includeBranches; 571 } 572 573 574 575 /** 576 * Specifies the set of base DNs that specify the set of entries to 577 * include in the export. 578 * 579 * @param includeBranches The set of base DNs that specify the set 580 * of entries to include in the export. 581 */ 582 public void setIncludeBranches(List<DN> includeBranches) 583 { 584 if (includeBranches == null) 585 { 586 this.includeBranches = new ArrayList<>(0); 587 } 588 else 589 { 590 this.includeBranches = includeBranches; 591 } 592 } 593 594 595 596 /** 597 * Indicates whether the set of objectclasses should be included in 598 * the entries written to LDIF. 599 * 600 * @return <CODE>true</CODE> if the set of objectclasses should be 601 * included in the entries written to LDIF, or 602 * <CODE>false</CODE> if not. 603 */ 604 public boolean includeObjectClasses() 605 { 606 return includeObjectClasses; 607 } 608 609 610 611 /** 612 * Indicates whether the set of operational attributes should be 613 * included in the export. 614 * 615 * @return <CODE>true</CODE> if the set of operational attributes 616 * should be included in the export. 617 */ 618 public boolean includeOperationalAttributes() 619 { 620 return includeOperationalAttributes; 621 } 622 623 624 625 /** 626 * Specifies whether the objectclasss attribute should be 627 * included in the export. 628 * 629 * @param includeObjectClasses Specifies whether the 630 * objectclass attribute 631 * should be included in the 632 * export. 633 */ 634 public void setIncludeObjectClasses(boolean includeObjectClasses) 635 { 636 this.includeObjectClasses = includeObjectClasses; 637 } 638 639 /** 640 * Specifies whether the set of operational attributes should be 641 * included in the export. 642 * 643 * @param includeOperationalAttributes Specifies whether the set 644 * of operational attributes 645 * should be included in the 646 * export. 647 */ 648 public void setIncludeOperationalAttributes( 649 boolean includeOperationalAttributes) 650 { 651 this.includeOperationalAttributes = includeOperationalAttributes; 652 } 653 654 655 656 /** 657 * Indicates whether virtual attributes should be included in the 658 * export. 659 * 660 * @return {@code true} if virtual attributes should be included in 661 * the export, or {@code false} if not. 662 */ 663 public boolean includeVirtualAttributes() 664 { 665 return includeVirtualAttributes; 666 } 667 668 669 670 /** 671 * Specifies whether virtual attributes should be included in the 672 * export. 673 * 674 * @param includeVirtualAttributes Specifies whether virtual 675 * attributes should be included 676 * in the export. 677 */ 678 public void setIncludeVirtualAttributes( 679 boolean includeVirtualAttributes) 680 { 681 this.includeVirtualAttributes = includeVirtualAttributes; 682 } 683 684 685 686 /** 687 * Retrieves the set of attributes that should be excluded from the 688 * entries written to LDIF. The set that is returned may be altered 689 * by the caller. 690 * 691 * @return The set of attributes that should be excluded from the 692 * entries written to LDIF. 693 */ 694 public Set<AttributeType> getExcludeAttributes() 695 { 696 return excludeAttributes; 697 } 698 699 700 701 /** 702 * Specifies the set of attributes that should be excluded from the 703 * entries written to LDIF. 704 * 705 * @param excludeAttributes The set of attributes that should be 706 * excluded from the entries written to 707 * LDIF. 708 */ 709 public void setExcludeAttributes( 710 Set<AttributeType> excludeAttributes) 711 { 712 if (excludeAttributes == null) 713 { 714 this.excludeAttributes = new HashSet<>(0); 715 } 716 else 717 { 718 this.excludeAttributes = excludeAttributes; 719 } 720 } 721 722 723 724 /** 725 * Retrieves the set of attributes that should be included in the 726 * entries written to LDIF. The set that is returned may be altered 727 * by the caller. 728 * 729 * @return The set of attributes that should be included in the 730 * entries written to LDIF. 731 */ 732 public Set<AttributeType> getIncludeAttributes() 733 { 734 return includeAttributes; 735 } 736 737 738 739 /** 740 * Specifies the set of attributes that should be included in the 741 * entries written to LDIF. 742 * 743 * @param includeAttributes The set of attributes that should be 744 * included in the entries written to 745 * LDIF. 746 */ 747 public void setIncludeAttributes(Set<AttributeType> includeAttributes) 748 { 749 if (includeAttributes == null) 750 { 751 this.includeAttributes = new HashSet<>(0); 752 } 753 else 754 { 755 this.includeAttributes = includeAttributes; 756 } 757 } 758 759 760 761 /** 762 * Indicates whether the specified attribute should be included in 763 * the entries written to LDIF. 764 * 765 * @param attributeType The attribute type for which to make the 766 * determination. 767 * 768 * @return <CODE>true</CODE> if the specified attribute should be 769 * included in the entries written to LDIF, or 770 * <CODE>false</CODE> if not. 771 */ 772 public boolean includeAttribute(AttributeType attributeType) 773 { 774 return (excludeAttributes.isEmpty() 775 || !excludeAttributes.contains(attributeType)) 776 && (includeAttributes.isEmpty() 777 || includeAttributes.contains(attributeType)); 778 } 779 780 781 782 /** 783 * Retrieves the set of search filters that should be used to 784 * determine which entries to exclude from the LDIF. The list that 785 * is returned may be altered by the caller. 786 * 787 * @return The set of search filters that should be used to 788 * determine which entries to exclude from the LDIF. 789 */ 790 public List<SearchFilter> getExcludeFilters() 791 { 792 return excludeFilters; 793 } 794 795 796 797 /** 798 * Specifies the set of search filters that should be used to 799 * determine which entries to exclude from the LDIF. 800 * 801 * @param excludeFilters The set of search filters that should be 802 * used to determine which entries to 803 * exclude from the LDIF. 804 */ 805 public void setExcludeFilters(List<SearchFilter> excludeFilters) 806 { 807 if (excludeFilters == null) 808 { 809 this.excludeFilters = new ArrayList<>(0); 810 } 811 else 812 { 813 this.excludeFilters = excludeFilters; 814 } 815 } 816 817 818 819 /** 820 * Retrieves the set of search filters that should be used to 821 * determine which entries to include in the LDIF. The list that is 822 * returned may be altered by the caller. 823 * 824 * @return The set of search filters that should be used to 825 * determine which entries to include in the LDIF. 826 */ 827 public List<SearchFilter> getIncludeFilters() 828 { 829 return includeFilters; 830 } 831 832 833 834 /** 835 * Specifies the set of search filters that should be used to 836 * determine which entries to include in the LDIF. 837 * 838 * @param includeFilters The set of search filters that should be 839 * used to determine which entries to 840 * include in the LDIF. 841 */ 842 public void setIncludeFilters(List<SearchFilter> includeFilters) 843 { 844 if (includeFilters == null) 845 { 846 this.includeFilters = new ArrayList<>(0); 847 } 848 else 849 { 850 this.includeFilters = includeFilters; 851 } 852 } 853 854 855 856 /** 857 * Indicates whether the specified entry should be included in the 858 * export based on the configured set of include and exclude 859 * filters. 860 * 861 * @param entry The entry for which to make the determination. 862 * 863 * @return <CODE>true</CODE> if the specified entry should be 864 * included in the export, or <CODE>false</CODE> if not. 865 * 866 * @throws DirectoryException If there is a problem with any of 867 * the search filters used to make the 868 * determination. 869 */ 870 public boolean includeEntry(Entry entry) 871 throws DirectoryException 872 { 873 DN dn = entry.getName(); 874 if (! excludeBranches.isEmpty()) 875 { 876 for (DN excludeBranch : excludeBranches) 877 { 878 if (excludeBranch.isAncestorOf(dn)) 879 { 880 return false; 881 } 882 } 883 } 884 885 checkIncludeBranches: if (! includeBranches.isEmpty()) 886 { 887 for (DN includeBranch : includeBranches) 888 { 889 if (includeBranch.isAncestorOf(dn)) 890 { 891 break checkIncludeBranches; 892 } 893 } 894 895 return false; 896 } 897 898 if (! excludeFilters.isEmpty()) 899 { 900 for (SearchFilter filter : excludeFilters) 901 { 902 if (filter.matchesEntry(entry)) 903 { 904 return false; 905 } 906 } 907 } 908 909 if (! includeFilters.isEmpty()) 910 { 911 for (SearchFilter filter : includeFilters) 912 { 913 if (filter.matchesEntry(entry)) 914 { 915 return true; 916 } 917 } 918 return false; 919 } 920 921 return true; 922 } 923 924 925 926 /** 927 * Closes any resources that this export config might have open. 928 */ 929 @Override 930 public void close() 931 { 932 // FIXME -- Need to add code to generate a signed hash of the LDIF content. 933 StaticUtils.close(writer); 934 } 935}