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