logging-log4j-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Paul Smith <psm...@aconex.com>
Subject Re: cvs commit: logging-log4j/tests/src/java/org/apache/log4j/rolling/helper CompressTestCase.java FileNamePatternTestCase.java
Date Mon, 23 May 2005 23:58:09 GMT
Hi Curt, I'm just looking at bug 34979, and it says "...suggested  
during the port of those classes to log4jcxx'.

  I don't recall (emphasis on recall) seeing anything about async'ing  
the compression.  Can you provide me some links to refresh my  
memory?  I think this is a good idea, it's just a reasonable chunk of  
work, and async stuff can be complicated.  I was hoping to stabilise  
the Whatever1.3ReleaseIsCalled to a minimum.

cheers,

Paul

On 24/05/2005, at 9:51 AM, carnold@apache.org wrote:

> carnold     2005/05/23 16:51:01
>
>   Modified:    src/java/org/apache/log4j FileAppender.java
>                src/java/org/apache/log4j/rolling
>                         FilterBasedTriggeringPolicy.java
>                         FixedWindowRollingPolicy.java
>                         RollingFileAppender.java RollingPolicy.java
>                         RollingPolicyBase.java
>                         SizeBasedTriggeringPolicy.java
>                         TimeBasedRollingPolicy.java  
> TriggeringPolicy.java
>                tests/src/java/org/apache/log4j/rolling
>                         FilterBasedRollingTest.java
>                         TimeBasedRollingTest.java
>                tests/src/java/org/apache/log4j/rolling/helper
>                         CompressTestCase.java  
> FileNamePatternTestCase.java
>   Added:       src/java/org/apache/log4j/rolling/helper Action.java
>                         CompositeAction.java FileRenameAction.java
>                         GZCompressAction.java ZipCompressAction.java
>   Log:
>   Bug 34979: rolling refactoring, asynchronous compression
>
>   Revision  Changes    Path
>   1.53      +3 -3      logging-log4j/src/java/org/apache/log4j/ 
> FileAppender.java
>
>   Index: FileAppender.java
>   ===================================================================
>   RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/ 
> FileAppender.java,v
>   retrieving revision 1.52
>   retrieving revision 1.53
>   diff -u -r1.52 -r1.53
>   --- FileAppender.java    8 Mar 2005 22:32:56 -0000    1.52
>   +++ FileAppender.java    23 May 2005 23:51:01 -0000    1.53
>   @@ -16,9 +16,8 @@
>
>    package org.apache.log4j;
>
>   -import java.io.BufferedWriter;
>   -import java.io.FileOutputStream;
>   -import java.io.IOException;
>   +import java.io.*;
>   +
>    import org.apache.log4j.helpers.OptionConverter;
>
>
>   @@ -275,4 +274,5 @@
>        writeHeader();
>        getLogger().debug("setFile ended");
>      }
>   +
>    }
>
>
>
>   1.3       +7 -13     logging-log4j/src/java/org/apache/log4j/ 
> rolling/FilterBasedTriggeringPolicy.java
>
>   Index: FilterBasedTriggeringPolicy.java
>   ===================================================================
>   RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/ 
> rolling/FilterBasedTriggeringPolicy.java,v
>   retrieving revision 1.2
>   retrieving revision 1.3
>   diff -u -r1.2 -r1.3
>   --- FilterBasedTriggeringPolicy.java    8 May 2005 03:43:40  
> -0000    1.2
>   +++ FilterBasedTriggeringPolicy.java    23 May 2005 23:51:01  
> -0000    1.3
>   @@ -16,12 +16,10 @@
>
>    package org.apache.log4j.rolling;
>
>   +import org.apache.log4j.Appender;
>    import org.apache.log4j.spi.Filter;
>    import org.apache.log4j.spi.LoggingEvent;
>
>   -import org.apache.log4j.Appender;
>   -import java.io.File;
>   -
>
>    /**
>     * FilterBasedTriggeringPolicy determines if rolling should be  
> triggered
>   @@ -50,17 +48,12 @@
>      }
>
>      /**
>   -   *  Determines if the event should trigger a rollover.
>   -   *  @param appender appender, ignored.
>   -   *  @param fileLength file length in bytes, ignored.
>   -   *  @param event logging event.
>   -   *  @return true if event should trigger a rollover.
>   +   * {@inheritDoc}
>       *
>       */
>   -  public boolean isTriggeringEvent(final Appender appender,
>   -                                   final LoggingEvent event,
>   -                                   final File file,
>   -                                   final long fileLength) {
>   +  public boolean isTriggeringEvent(
>   +    final Appender appender, final LoggingEvent event, final  
> String file,
>   +    final long fileLength) {
>        //
>        //   in the abnormal case of no contained filters
>        //     always return true to avoid each logging event
>   @@ -110,6 +103,7 @@
>
>      /**
>       * Returns the head Filter.
>   +   * @return head of filter chain, may be null.
>       *
>       */
>      public Filter getFilter() {
>   @@ -117,7 +111,7 @@
>      }
>
>      /**
>   -   *  Prepares the instance for use.
>   +   *  {@inheritDoc}
>       */
>      public void activateOptions() {
>        for (Filter f = headFilter; f != null; f = f.getNext()) {
>
>
>
>   1.10      +172 -81   logging-log4j/src/java/org/apache/log4j/ 
> rolling/FixedWindowRollingPolicy.java
>
>   Index: FixedWindowRollingPolicy.java
>   ===================================================================
>   RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/ 
> rolling/FixedWindowRollingPolicy.java,v
>   retrieving revision 1.9
>   retrieving revision 1.10
>   diff -u -r1.9 -r1.10
>   --- FixedWindowRollingPolicy.java    22 May 2005 07:56:23  
> -0000    1.9
>   +++ FixedWindowRollingPolicy.java    23 May 2005 23:51:01  
> -0000    1.10
>   @@ -1,5 +1,5 @@
>    /*
>   - * Copyright 1999,2004 The Apache Software Foundation.
>   + * Copyright 1999,2005 The Apache Software Foundation.
>     *
>     * Licensed under the Apache License, Version 2.0 (the "License");
>     * you may not use this file except in compliance with the License.
>   @@ -16,81 +16,100 @@
>
>    package org.apache.log4j.rolling;
>
>   -import org.apache.log4j.rolling.helper.Compress;
>    import org.apache.log4j.pattern.IntegerPatternConverter;
>   -import org.apache.log4j.rolling.helper.Util;
>    import org.apache.log4j.pattern.PatternConverter;
>   +import org.apache.log4j.rolling.helper.FileRenameAction;
>   +import org.apache.log4j.rolling.helper.GZCompressAction;
>   +import org.apache.log4j.rolling.helper.ZipCompressAction;
>
>    import java.io.File;
>   +import java.io.IOException;
>   +
>   +import java.util.List;
>
>
>    /**
>   - * When rolling over, <code>FixedWindowRollingPolicy</code>  
> renames files
>   - * according to a fixed window algorithm as described below.
>   - *
>   - * <p>The <b>ActiveFileName</b> property, which is required,  
> represents the name
>   - * of the file where current logging output will be written.
>   - * The <b>FileNamePattern</b>  option represents the file name  
> pattern for the
>   - * archived (rolled over) log files. If present, the  
> <b>FileNamePattern</b>
>   - * option must include an integer token, that is the string "%i"  
> somewhere
>   + * When rolling over, <code>FixedWindowRollingPolicy</code>  
> renames files
>   + * according to a fixed window algorithm as described below.
>   + *
>   + * <p>The <b>ActiveFileName</b> property, which is required,  
> represents the name
>   + * of the file where current logging output will be written.
>   + * The <b>FileNamePattern</b>  option represents the file name  
> pattern for the
>   + * archived (rolled over) log files. If present, the  
> <b>FileNamePattern</b>
>   + * option must include an integer token, that is the string "%i"  
> somewhere
>     * within the pattern.
>   - *
>   - * <p>Let <em>max</em> and <em>min</em> represent the values of  
> respectively
>   + *
>   + * <p>Let <em>max</em> and <em>min</em> represent the values of  
> respectively
>     * the <b>MaxIndex</b> and <b>MinIndex</b> options. Let  
> "foo.log" be the value
>   - * of the <b>ActiveFile</b> option and "foo.%i.log" the value of
>   - * <b>FileNamePattern</b>. Then, when rolling over, the file
>   - * <code>foo.<em>max</em>.log</code> will be deleted, the file
>   - * <code>foo.<em>max-1</em>.log</code> will be renamed as
>   - * <code>foo.<em>max</em>.log</code>, the file  
> <code>foo.<em>max-2</em>.log</code>
>   - * renamed as <code>foo.<em>max-1</em>.log</code>, and so on,
>   - * the file <code>foo.<em>min+1</em>.log</code> renamed as
>   + * of the <b>ActiveFile</b> option and "foo.%i.log" the value of
>   + * <b>FileNamePattern</b>. Then, when rolling over, the file
>   + * <code>foo.<em>max</em>.log</code> will be deleted, the file
>   + * <code>foo.<em>max-1</em>.log</code> will be renamed as
>   + * <code>foo.<em>max</em>.log</code>, the file  
> <code>foo.<em>max-2</em>.log</code>
>   + * renamed as <code>foo.<em>max-1</em>.log</code>, and so on,
>   + * the file <code>foo.<em>min+1</em>.log</code> renamed as
>     * <code>foo.<em>min+2</em>.log</code>. Lastly, the active file  
> <code>foo.log</code>
>     * will be renamed as <code>foo.<em>min</em>.log</code> and a  
> new active file name
>     * <code>foo.log</code> will be created.
>   - *
>   - * <p>Given that this rollover algorithm requires as many file  
> renaming
>   + *
>   + * <p>Given that this rollover algorithm requires as many file  
> renaming
>     * operations as the window size, large window sizes are  
> discouraged. The
>     * current implementation will automatically reduce the window  
> size to 12 when
>     * larger values are specified by the user.
>   - *
>   + *
>     *
>     * @author Ceki G&uuml;lc&uuml;
>     * @since 1.3
>     * */
>    public final class FixedWindowRollingPolicy extends  
> RollingPolicyBase {
>   -  static private final String FNP_NOT_SET =
>   +  /**
>   +   * Error message.
>   +   */
>   +  private static final String FNP_NOT_SET =
>        "The FileNamePattern option must be set before using  
> FixedWindowRollingPolicy. ";
>   -  static private final String SEE_FNP_NOT_SET =
>   +
>   +  /**
>   +   * Link for error message.
>   +   */
>   +  private static final String SEE_FNP_NOT_SET =
>        "See also http://logging.apache.org/log4j/ 
> codes.html#tbr_fnp_not_set";
>   +
>   +  /**
>   +   * It's almost always a bad idea to have a large window size,  
> say over 12.
>   +   */
>   +  private static final int MAX_WINDOW_SIZE = 12;
>   +
>   +  /**
>   +   * Index for oldest retained log file.
>   +   */
>      private int maxIndex;
>   +
>   +  /**
>   +   * Index for most recent log file.
>   +   */
>      private int minIndex;
>   -  private final Util util = new Util();
>   -  private final Compress compress = new Compress();
>
>      /**
>   -   * It's almost always a bad idea to have a large window size,  
> say over 12.
>   +   * Constructs a new instance.
>       */
>   -  private static int MAX_WINDOW_SIZE = 12;
>   -
>      public FixedWindowRollingPolicy() {
>        minIndex = 1;
>        maxIndex = 7;
>        activeFileName = null;
>      }
>
>   +  /**
>   +   * {@inheritDoc}
>   +   */
>      public void activateOptions() {
>   -    // set the LR for our utility object
>   -    util.setLoggerRepository(this.repository);
>   -    compress.setLoggerRepository(this.repository);
>   -
>        if (fileNamePatternStr != null) {
>          parseFileNamePattern();
>   -      determineCompressionMode();
>        } else {
>          getLogger().warn(FNP_NOT_SET);
>          getLogger().warn(SEE_FNP_NOT_SET);
>          throw new IllegalStateException(FNP_NOT_SET +  
> SEE_FNP_NOT_SET);
>        }
>   +
>        if (activeFileName == null) {
>          getLogger().warn(
>            "The ActiveFile name option must be set before using  
> this rolling policy.");
>   @@ -106,18 +125,20 @@
>          maxIndex = minIndex;
>        }
>
>   -    if((maxIndex-minIndex) > MAX_WINDOW_SIZE) {
>   +    if ((maxIndex - minIndex) > MAX_WINDOW_SIZE) {
>          getLogger().warn("Large window sizes are not allowed.");
>   -      maxIndex = minIndex +  MAX_WINDOW_SIZE;
>   +      maxIndex = minIndex + MAX_WINDOW_SIZE;
>          getLogger().warn("MaxIndex reduced to {}.", new Integer 
> (maxIndex));
>        }
>
>        PatternConverter itc = null;
>   +
>        for (int i = 0; i < patternConverters.length; i++) {
>   -        if (patternConverters[i] instanceof  
> IntegerPatternConverter) {
>   -            itc = patternConverters[i];
>   -            break;
>   -        }
>   +      if (patternConverters[i] instanceof  
> IntegerPatternConverter) {
>   +        itc = patternConverters[i];
>   +
>   +        break;
>   +      }
>        }
>
>        if (itc == null) {
>   @@ -127,77 +148,147 @@
>        }
>      }
>
>   -  public void rollover() throws RolloverFailure {
>   -    // Inside this method it is guaranteed that the hereto  
> active log fil is closed.
>   -    // If maxIndex <= 0, then there is no file renaming to be done.
>   +  /**
>   +   * {@inheritDoc}
>   +   */
>   +  public boolean rollover(
>   +    final StringBuffer activeFile, List synchronousActions,
>   +    List asynchronousActions) throws IOException {
>        if (maxIndex >= 0) {
>          // Delete the oldest file, to keep Windows happy.
>          StringBuffer buf = new StringBuffer();
>          formatFileName(new Integer(maxIndex), buf);
>   -      File file = new File(buf.toString());
>
>   -      if (file.exists()) {
>   -        file.delete();
>   +      String higherFileName = buf.toString();
>   +      File higherFile = new File(higherFileName);
>   +
>   +      if (higherFile.exists()) {
>   +        if (!higherFile.delete()) {
>   +          throw new IOException("Unable to delete " +  
> higherFileName);
>   +        }
>   +      }
>   +
>   +      int suffixLength = 0;
>   +
>   +      if (higherFileName.endsWith(".gz")) {
>   +        suffixLength = 3;
>   +      } else if (higherFileName.endsWith(".zip")) {
>   +        suffixLength = 4;
>   +      }
>   +
>   +      String higherBaseName = higherFileName;
>   +
>   +      if (suffixLength > 0) {
>   +        higherBaseName =
>   +          higherFileName.substring(0, higherFileName.length() -  
> suffixLength);
>   +
>   +        File baseFile = new File(higherBaseName);
>   +
>   +        if (baseFile.exists()) {
>   +          if (!baseFile.delete()) {
>   +            throw new IOException("Unable to delete " +  
> higherBaseName);
>   +          }
>   +        }
>          }
>
>          // Map {(maxIndex - 1), ..., minIndex} to {maxIndex, ...,  
> minIndex+1}
>          for (int i = maxIndex - 1; i >= minIndex; i--) {
>   -          buf.setLength(0);
>   -          formatFileName(new Integer(i), buf);
>   -      String toRenameStr = buf.toString();
>   -      File toRename = new File(toRenameStr);
>   -      // no point in trying to rename an inexistent file
>   -      if(toRename.exists()) {
>   -          buf.setLength(0);
>   -          formatFileName(new Integer(i + 1), buf);
>   -          util.rename(toRenameStr, buf.toString());
>   -      } else {
>   -          getLogger().info("Skipping rollover for non-existent  
> file {}", toRenameStr);
>   +        buf.setLength(0);
>   +        formatFileName(new Integer(i), buf);
>   +
>   +        String lowerFileName = buf.toString();
>   +        File toRename = new File(lowerFileName);
>   +
>   +        // no point in trying to rename an non-existent file
>   +        if (toRename.exists()) {
>   +          if (!toRename.renameTo(new File(higherFileName))) {
>   +            throw new IOException("Unable to rename " +  
> lowerFileName);
>   +          }
>   +        }
>   +
>   +        if (suffixLength > 0) {
>   +          String lowerBaseName =
>   +            lowerFileName.substring(0, lowerFileName.length() -  
> suffixLength);
>   +          File baseFile = new File(lowerBaseName);
>   +
>   +          if (baseFile.exists()) {
>   +            if (!baseFile.renameTo(new File(higherBaseName))) {
>   +              throw new IOException("Unable to rename " +  
> lowerBaseName);
>   +            }
>              }
>   +
>   +          higherBaseName = lowerBaseName;
>   +        } else {
>   +          higherBaseName = lowerFileName;
>   +        }
>   +
>   +        higherFileName = lowerFileName;
>          }
>
>   -      buf.setLength(0);
>   -      formatFileName(new Integer(minIndex), buf);
>   +      activeFile.setLength(0);
>   +      activeFile.append(activeFileName);
>
>   -      //move active file name to min
>   -      switch (compressionMode) {
>   -      case Compress.NONE:
>   -          util.rename(activeFileName, buf.toString());
>   -          break;
>   -      case Compress.GZ:
>   -          compress.GZCompress(activeFileName, buf.toString());
>   -          break;
>   -      case Compress.ZIP:
>   -      compress.ZIPCompress(activeFileName, buf.toString());
>   -      break;
>   +      File currentFile = new File(activeFileName);
>   +
>   +      if (currentFile.exists()) {
>   +        //
>   +        //    add renaming of active file as something to be done
>   +        //       after closing active file
>   +        //
>   +        synchronousActions.add(
>   +          new FileRenameAction(
>   +            new File(activeFileName), new File(higherBaseName),  
> false));
>   +
>   +        if (suffixLength == 3) {
>   +          asynchronousActions.add(
>   +            new GZCompressAction(
>   +              new File(higherBaseName), new File 
> (higherFileName), true,
>   +              getLogger()));
>   +        }
>   +
>   +        if (suffixLength == 4) {
>   +          asynchronousActions.add(
>   +            new ZipCompressAction(
>   +              new File(higherBaseName), new File 
> (higherFileName), true,
>   +              getLogger()));
>   +        }
>          }
>   +
>   +      return true;
>        }
>   -  }
>
>   -  /**
>   -   * Return the value of the <b>ActiveFile</b> option.
>   -   *
>   -   * @see {@link setActiveFileName}.
>   -  */
>   -  public String getActiveFileName() {
>   -    // TODO This is clearly bogus.
>   -    return activeFileName;
>   +    return false;
>      }
>
>   +  /**
>   +   * Get index of oldest log file to be retained.
>   +   * @return index of oldest log file.
>   +   */
>      public int getMaxIndex() {
>        return maxIndex;
>      }
>
>   +  /**
>   +   * Get index of most recent log file.
>   +   * @return index of oldest log file.
>   +   */
>      public int getMinIndex() {
>        return minIndex;
>      }
>
>   +  /**
>   +   * Set index of oldest log file to be retained.
>   +   * @param maxIndex index of oldest log file to be retained.
>   +   */
>      public void setMaxIndex(int maxIndex) {
>        this.maxIndex = maxIndex;
>      }
>
>   +  /**
>   +   * Set index of most recent log file.
>   +   * @param minIndex Index of most recent log file.
>   +   */
>      public void setMinIndex(int minIndex) {
>        this.minIndex = minIndex;
>      }
>   -
>    }
>
>
>
>   1.26      +363 -163  logging-log4j/src/java/org/apache/log4j/ 
> rolling/RollingFileAppender.java
>
>   Index: RollingFileAppender.java
>   ===================================================================
>   RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/ 
> rolling/RollingFileAppender.java,v
>   retrieving revision 1.25
>   retrieving revision 1.26
>   diff -u -r1.25 -r1.26
>   --- RollingFileAppender.java    8 May 2005 03:43:40 -0000    1.25
>   +++ RollingFileAppender.java    23 May 2005 23:51:01 -0000    1.26
>   @@ -13,15 +13,19 @@
>     * See the License for the specific language governing  
> permissions and
>     * limitations under the License.
>     */
>   +
>    package org.apache.log4j.rolling;
>
>    import org.apache.log4j.FileAppender;
>   +import org.apache.log4j.rolling.helper.Action;
>   +import org.apache.log4j.rolling.helper.CompositeAction;
>    import org.apache.log4j.spi.LoggingEvent;
>
>   -import java.io.File;
>   -import java.io.IOException;
>   -import java.io.OutputStream;
>   -import java.io.OutputStreamWriter;
>   +import java.io.*;
>   +
>   +import java.util.ArrayList;
>   +import java.util.Iterator;
>   +import java.util.List;
>
>
>    /**
>   @@ -70,199 +74,395 @@
>     * @since  1.3
>     * */
>    public final class RollingFileAppender extends FileAppender {
>   -    private File activeFile;
>   -    private TriggeringPolicy triggeringPolicy;
>   -    private RollingPolicy rollingPolicy;
>   -    private long fileLength = 0;
>   +  /**
>   +   * Triggering policy.
>   +   */
>   +  private TriggeringPolicy triggeringPolicy;
>   +
>   +  /**
>   +   * Rolling policy.
>   +   */
>   +  private RollingPolicy rollingPolicy;
>   +
>   +  /**
>   +   * Length of current active log file.
>   +   */
>   +  private long fileLength = 0;
>   +
>   +  /**
>   +   * Thread for any asynchronous actions from last rollover.
>   +   */
>   +  private Thread rollingThread = null;
>   +
>   +  /**
>   +   * Construct a new instance.
>   +   */
>   +  public RollingFileAppender() {
>   +  }
>   +
>   +  /**
>   +   * Prepare instance of use.
>   +   */
>   +  public void activateOptions() {
>   +    if (rollingPolicy == null) {
>   +      getLogger().warn(
>   +        "Please set a rolling policy for the RollingFileAppender  
> named '{}'",
>   +        getName());
>   +    }
>   +
>   +    //
>   +    //  if no explicit triggering policy and rolling policy is  
> both.
>   +    //
>   +    if (
>   +      (triggeringPolicy == null) && rollingPolicy instanceof  
> TriggeringPolicy) {
>   +      triggeringPolicy = (TriggeringPolicy) rollingPolicy;
>   +    }
>   +
>   +    if (triggeringPolicy == null) {
>   +      getLogger().warn(
>   +        "Please set a TriggeringPolicy for the  
> RollingFileAppender named '{}'",
>   +        getName());
>   +
>   +      return;
>   +    }
>   +
>   +    IOException ioException = null;
>   +
>   +    synchronized (this) {
>   +      triggeringPolicy.activateOptions();
>   +      rollingPolicy.activateOptions();
>   +
>   +      StringBuffer activeFileName = new StringBuffer();
>   +      List synchronousActions = new ArrayList();
>   +      List asynchronousActions = new ArrayList();
>   +
>   +      try {
>   +        if (
>   +          rollingPolicy.rollover(
>   +              activeFileName, synchronousActions,  
> asynchronousActions)) {
>   +          performActions(synchronousActions, asynchronousActions);
>   +        }
>
>   -    /**
>   -     * The default constructor simply calls its {@link
>   -     * FileAppender#FileAppender parents constructor}.
>   -     * */
>   -    public RollingFileAppender() {
>   -    }
>   +        String afn = activeFileName.toString();
>   +        setFile(afn);
>
>   -    public void activateOptions() {
>   -        if (triggeringPolicy == null) {
>   -            getLogger().warn("Please set a TriggeringPolicy for  
> the RollingFileAppender named '{}'",
>   -                getName());
>   +        File activeFile = new File(afn);
>
>   -            return;
>   +        if (getAppend()) {
>   +          fileLength = activeFile.length();
>   +        } else {
>   +          fileLength = 0;
>            }
>
>   -        if (rollingPolicy != null) {
>   -            String afn = rollingPolicy.getActiveFileName();
>   -            activeFile = new File(afn);
>   -            getLogger().debug("Active log file name: " + afn);
>   -            setFile(afn);
>   -
>   -            // the activeFile variable is used by the  
> triggeringPolicy.isTriggeringEvent method
>   -            activeFile = new File(afn);
>   -
>   -            if (this.getAppend()) {
>   -                fileLength = activeFile.length();
>   -            } else {
>   -                fileLength = 0;
>   -            }
>   +        super.activateOptions();
>   +      } catch (IOException ex) {
>   +        ioException = ex;
>   +      }
>   +    }
>
>   -            super.activateOptions();
>   -        } else {
>   -            getLogger().warn("Please set a rolling policy");
>   -        }
>   +    if (ioException != null) {
>   +      getLogger().warn(
>   +        "IOException while preparing while initializing  
> RollingFileAppender named '"
>   +        + getName() + "'", ioException);
>        }
>   +  }
>
>   -    /**
>   -       Implements the usual roll over behaviour.
>   +  /**
>   +   * Perform any actions specified by triggering policy.
>   +   * @param synchronousActions list of Action instances to be  
> performed after active file close.
>   +   * @param asynchronousActions list of Action instances to be  
> performed asynchronously after file close
>   +   * and synchronous actions.
>   +   * @return true if all synchronous actions were successful.
>   +   * @throws IOException if IO error during synchronous actions.
>   +   */
>   +  private boolean performActions(
>   +    final List synchronousActions, final List asynchronousActions)
>   +    throws IOException {
>   +    Iterator syncIterator = synchronousActions.iterator();
>
>   -       <p>If <code>MaxBackupIndex</code> is positive, then files
>   -       {<code>File.1</code>, ..., <code>File.MaxBackupIndex -1</ 
> code>}
>   -       are renamed to {<code>File.2</code>, ...,
>   -       <code>File.MaxBackupIndex</code>}. Moreover, <code>File</ 
> code> is
>   -       renamed <code>File.1</code> and closed. A new <code>File</ 
> code> is
>   -       created to receive further log output.
>   -
>   -       <p>If <code>MaxBackupIndex</code> is equal to zero, then the
>   -       <code>File</code> is truncated with no backup files created.
>   -
>   -     */
>   -    public void rollover() {
>   -        // Note: synchronization at this point is unnecessary as  
> the doAppend
>   -        // is already synched
>   -        //
>   -        // make sure to close the hereto active log file!  
> Renaming under windows
>   -        // does not work for open files.
>   -        this.closeWriter();
>   -
>   -        // By default, the newly created file will be created in  
> truncate mode.
>   -        // (See the setFile(fileName,...) call a few lines below.)
>   -        boolean append = false;
>   +    while (syncIterator.hasNext()) {
>   +      if (!((Action) syncIterator.next()).execute()) {
>   +        return false;
>   +      }
>   +    }
>
>   -        try {
>   -            rollingPolicy.rollover();
>   -            fileLength = 0;
>   -        } catch (RolloverFailure rf) {
>   -            getLogger().warn("RolloverFailure occurred.  
> Deferring rollover.");
>   +    if (asynchronousActions.size() > 0) {
>   +      Runnable action = null;
>
>   -            // we failed to rollover, let us not truncate and  
> risk data loss
>   -            append = true;
>   -        }
>   +      if (asynchronousActions.size() > 1) {
>   +        action = new CompositeAction(asynchronousActions, false,  
> getLogger());
>   +      } else {
>   +        action = (Runnable) asynchronousActions.get(0);
>   +      }
>
>   -        // Although not certain, the active file name may change  
> after roll over.
>   -        fileName = rollingPolicy.getActiveFileName();
>   -        getLogger().debug("Active file name is now [{}].",  
> fileName);
>   +      rollingThread = new Thread(action);
>   +      rollingThread.start();
>   +    }
>
>   -        // the activeFile variable is used by the  
> triggeringPolicy.isTriggeringEvent method
>   -        activeFile = new File(fileName);
>   +    return true;
>   +  }
>
>   -        try {
>   -            // This will also close the file. This is OK since  
> multiple
>   -            // close operations are safe.
>   -            this.setFile(fileName, append, bufferedIO, bufferSize);
>   -        } catch (IOException e) {
>   -            getLogger().error("setFile(" + fileName + ", false)  
> call failed.", e);
>   -        }
>   -    }
>   +  /**
>   +     Implements the usual roll over behaviour.
>
>   -    /**
>   -       This method differentiates RollingFileAppender from its  
> super
>   -       class.
>   -    */
>   -    protected void subAppend(final LoggingEvent event) {
>   -        // The rollover check must precede actual writing. This  
> is the
>   -        // only correct behavior for time driven triggers.
>   -        if (triggeringPolicy.isTriggeringEvent(this, event,  
> activeFile,
>   -                    getFileLength())) {
>   -            getLogger().debug("About to rollover");
>   -            rollover();
>   -        }
>   +     <p>If <code>MaxBackupIndex</code> is positive, then files
>   +     {<code>File.1</code>, ..., <code>File.MaxBackupIndex -1</ 
> code>}
>   +     are renamed to {<code>File.2</code>, ...,
>   +     <code>File.MaxBackupIndex</code>}. Moreover, <code>File</ 
> code> is
>   +     renamed <code>File.1</code> and closed. A new <code>File</ 
> code> is
>   +     created to receive further log output.
>
>   -        super.subAppend(event);
>   -    }
>   +     <p>If <code>MaxBackupIndex</code> is equal to zero, then the
>   +     <code>File</code> is truncated with no backup files created.
>
>   -    public RollingPolicy getRollingPolicy() {
>   -        return rollingPolicy;
>   -    }
>   +   */
>   +  public void rollover() {
>   +    if (rollingPolicy != null) {
>   +      Exception exception = null;
>
>   -    public TriggeringPolicy getTriggeringPolicy() {
>   -        return triggeringPolicy;
>   -    }
>   +      synchronized (this) {
>   +        try {
>   +          //
>   +          //  if we have some in-process compression, etc
>   +          //     from the previous rollover, wait till they are  
> finished.
>   +          //
>   +          if (rollingThread != null) {
>   +            rollingThread.join();
>   +            rollingThread = null;
>   +          }
>   +
>   +          StringBuffer activeFileName = new StringBuffer 
> (super.getFile());
>   +          List synchronousActions = new ArrayList();
>   +          List asynchronousActions = new ArrayList();
>   +
>   +          try {
>   +            boolean doRollover =
>   +              rollingPolicy.rollover(
>   +                activeFileName, synchronousActions,  
> asynchronousActions);
>   +
>   +            if (doRollover) {
>   +              String oldFileName = getFile();
>   +              String newFileName = activeFileName.toString();
>   +
>   +              //
>   +              //  if the file names are the same then we
>   +              //     have to close, do actions, then reopen
>   +              if (newFileName.equals(oldFileName)) {
>   +                closeWriter();
>   +
>   +                try {
>   +                  if (!performActions(synchronousActions,  
> asynchronousActions)) {
>   +                    throw new IOException(
>   +                      "Unable to complete action after active  
> file close.");
>   +                  }
>   +                } catch (IOException ex) {
>   +                  setFile(oldFileName, true, bufferedIO,  
> bufferSize);
>   +                  throw ex;
>   +                }
>
>   +                fileLength = 0;
>   +                setFile(newFileName, false, bufferedIO,  
> bufferSize);
>   +              } else {
>   +                //
>   +                //  if not the same, we can try opening new file  
> before
>   +                //     closing old file
>   +                if (bufferedIO) {
>   +                  setImmediateFlush(false);
>   +                }
>   +
>   +                Writer newWriter =
>   +                  createWriter(new FileOutputStream(newFileName,  
> false));
>   +                closeWriter();
>   +
>   +                try {
>   +                  performActions(synchronousActions,  
> asynchronousActions);
>   +                  fileLength = 0;
>   +
>   +                  if (bufferedIO) {
>   +                    this.writer = new BufferedWriter(newWriter,  
> bufferSize);
>   +                  } else {
>   +                    this.writer = newWriter;
>   +                  }
>   +                } catch (IOException ex) {
>   +                  setFile(oldFileName, true, bufferedIO,  
> bufferSize);
>   +                  throw ex;
>   +                }
>   +
>   +                writeHeader();
>   +              }
>   +            }
>   +          } catch (IOException ex) {
>   +            exception = ex;
>   +          }
>   +        } catch (InterruptedException ex) {
>   +          exception = ex;
>   +        }
>   +      }
>   +
>   +      if (exception != null) {
>   +        getLogger().warn(
>   +          "Exception during rollover, rollover deferred.",  
> exception);
>   +      }
>   +    }
>   +  }
>   +
>   +  /**
>   +   * {@inheritDoc}
>   +  */
>   +  protected void subAppend(final LoggingEvent event) {
>   +    // The rollover check must precede actual writing. This is the
>   +    // only correct behavior for time driven triggers.
>   +    if (
>   +      triggeringPolicy.isTriggeringEvent(
>   +          this, event, getFile(), getFileLength())) {
>   +      rollover();
>   +    }
>   +
>   +    super.subAppend(event);
>   +  }
>   +
>   +  /**
>   +   * Get rolling policy.
>   +   * @return rolling policy.
>   +   */
>   +  public RollingPolicy getRollingPolicy() {
>   +    return rollingPolicy;
>   +  }
>   +
>   +  /**
>   +   * Get triggering policy.
>   +   * @return triggering policy.
>   +   */
>   +  public TriggeringPolicy getTriggeringPolicy() {
>   +    return triggeringPolicy;
>   +  }
>   +
>   +  /**
>   +   * Sets the rolling policy.
>   +   * @param policy rolling policy.
>   +   */
>   +  public void setRollingPolicy(final RollingPolicy policy) {
>   +    rollingPolicy = policy;
>   +  }
>   +
>   +  /**
>   +   * Set triggering policy.
>   +   * @param policy triggering policy.
>   +   */
>   +  public void setTriggeringPolicy(final TriggeringPolicy policy) {
>   +    triggeringPolicy = policy;
>   +  }
>   +
>   +  /**
>   +   * Close appender.  Waits for any asynchronous file  
> compression actions to be completed.
>   +   */
>   +  public void close() {
>   +    if (rollingThread != null) {
>   +      try {
>   +        rollingThread.join();
>   +      } catch (InterruptedException ex) {
>   +        getLogger().info(
>   +          "Interrupted while waiting for completion of rollover  
> actions.", ex);
>   +      }
>   +
>   +      rollingThread = null;
>   +    }
>   +
>   +    super.close();
>   +  }
>   +
>   +  /**
>   +     Returns an OutputStreamWriter when passed an OutputStream.   
> The
>   +     encoding used will depend on the value of the
>   +     <code>encoding</code> property.  If the encoding value is
>   +     specified incorrectly the writer will be opened using the  
> default
>   +     system encoding (an error message will be printed to the  
> loglog.
>   +   @param os output stream, may not be null.
>   +   @return new writer.
>   +   */
>   +  protected OutputStreamWriter createWriter(final OutputStream  
> os) {
>   +    return super.createWriter(new CountingOutputStream(os, this));
>   +  }
>   +
>   +  /**
>   +   * Get byte length of current active log file.
>   +   * @return byte length of current active log file.
>   +   */
>   +  public long getFileLength() {
>   +    return fileLength;
>   +  }
>   +
>   +  /**
>   +   * Increments estimated byte length of current active log file.
>   +   * @param increment additional bytes written to log file.
>   +   */
>   +  public void incrementFileLength(int increment) {
>   +    fileLength += increment;
>   +  }
>   +
>   +  /**
>   +   * Wrapper for OutputStream that will report all write
>   +   * operations back to this class for file length calculations.
>   +   */
>   +  private static class CountingOutputStream extends OutputStream {
>        /**
>   -     * Sets the rolling policy. In case the 'policy' argument  
> also implements
>   -     * {@link TriggeringPolicy}, then the triggering policy for  
> this appender
>   -     * is automatically set to be the policy argument.
>   -     * @param policy
>   +     * Wrapped output stream.
>         */
>   -    public void setRollingPolicy(final RollingPolicy policy) {
>   -        rollingPolicy = policy;
>   +    private final OutputStream os;
>
>   -        if (rollingPolicy instanceof TriggeringPolicy) {
>   -            triggeringPolicy = (TriggeringPolicy) policy;
>   -        }
>   -    }
>   -
>   -    public void setTriggeringPolicy(final TriggeringPolicy  
> policy) {
>   -        triggeringPolicy = policy;
>   +    /**
>   +     * Rolling file appender to inform of stream writes.
>   +     */
>   +    private final RollingFileAppender rfa;
>
>   -        if (policy instanceof RollingPolicy) {
>   -            rollingPolicy = (RollingPolicy) policy;
>   -        }
>   +    /**
>   +     * Constructor.
>   +     * @param os output stream to wrap.
>   +     * @param rfa rolling file appender to inform.
>   +     */
>   +    public CountingOutputStream(
>   +      final OutputStream os, final RollingFileAppender rfa) {
>   +      this.os = os;
>   +      this.rfa = rfa;
>        }
>
>        /**
>   -       Returns an OutputStreamWriter when passed an  
> OutputStream.  The
>   -       encoding used will depend on the value of the
>   -       <code>encoding</code> property.  If the encoding value is
>   -       specified incorrectly the writer will be opened using the  
> default
>   -       system encoding (an error message will be printed to the  
> loglog.  */
>   -    protected OutputStreamWriter createWriter(final OutputStream  
> os) {
>   -        return super.createWriter(new CountingOutputStream(os,  
> this));
>   +     * {@inheritDoc}
>   +     */
>   +    public void close() throws IOException {
>   +      os.close();
>        }
>
>   -    public long getFileLength() {
>   -        return fileLength;
>   +    /**
>   +     * {@inheritDoc}
>   +     */
>   +    public void flush() throws IOException {
>   +      os.flush();
>        }
>
>   -    public void incrementFileLength(int increment) {
>   -        fileLength += increment;
>   +    /**
>   +     * {@inheritDoc}
>   +     */
>   +    public void write(final byte[] b) throws IOException {
>   +      os.write(b);
>   +      rfa.incrementFileLength(b.length);
>        }
>
>        /**
>   -     * Wrapper for OutputStream that will report all write
>   -     * operations back to this class for file length calculations.
>   +     * {@inheritDoc}
>         */
>   -    private static class CountingOutputStream extends  
> OutputStream {
>   -        private final OutputStream os;
>   -        private final RollingFileAppender rfa;
>   -
>   -        public CountingOutputStream(final OutputStream os,
>   -            final RollingFileAppender rfa) {
>   -            this.os = os;
>   -            this.rfa = rfa;
>   -        }
>   -
>   -        public void close() throws IOException {
>   -            os.close();
>   -        }
>   -
>   -        public void flush() throws IOException {
>   -            os.flush();
>   -        }
>   -
>   -        public void write(final byte[] b) throws IOException {
>   -            os.write(b);
>   -            rfa.incrementFileLength(b.length);
>   -        }
>   -
>   -        public void write(final byte[] b, final int off, final  
> int len)
>   -            throws IOException {
>   -            os.write(b, off, len);
>   -            rfa.incrementFileLength(len);
>   -        }
>   +    public void write(final byte[] b, final int off, final int len)
>   +      throws IOException {
>   +      os.write(b, off, len);
>   +      rfa.incrementFileLength(len);
>   +    }
>
>   -        public void write(final int b) throws IOException {
>   -            os.write(b);
>   -            rfa.incrementFileLength(1);
>   -        }
>   +    /**
>   +     * {@inheritDoc}
>   +     */
>   +    public void write(final int b) throws IOException {
>   +      os.write(b);
>   +      rfa.incrementFileLength(1);
>        }
>   +  }
>    }
>
>
>
>   1.11      +35 -25    logging-log4j/src/java/org/apache/log4j/ 
> rolling/RollingPolicy.java
>
>   Index: RollingPolicy.java
>   ===================================================================
>   RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/ 
> rolling/RollingPolicy.java,v
>   retrieving revision 1.10
>   retrieving revision 1.11
>   diff -u -r1.10 -r1.11
>   --- RollingPolicy.java    8 May 2005 03:43:40 -0000    1.10
>   +++ RollingPolicy.java    23 May 2005 23:51:01 -0000    1.11
>   @@ -1,12 +1,12 @@
>    /*
>   - * Copyright 1999,2004 The Apache Software Foundation.
>   - *
>   + * Copyright 1999,2005 The Apache Software Foundation.
>   + *
>     * Licensed under the Apache License, Version 2.0 (the "License");
>     * you may not use this file except in compliance with the License.
>     * You may obtain a copy of the License at
>   - *
>   + *
>     *      http://www.apache.org/licenses/LICENSE-2.0
>   - *
>   + *
>     * Unless required by applicable law or agreed to in writing,  
> software
>     * distributed under the License is distributed on an "AS IS"  
> BASIS,
>     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express  
> or implied.
>   @@ -16,33 +16,43 @@
>
>    package org.apache.log4j.rolling;
>
>   -
>    import org.apache.log4j.spi.OptionHandler;
>
>   +import java.io.IOException;
>   +
>   +import java.util.List;
>   +
>   +
>    /**
>   - * A <code>RollingPolicy</code> is responsible for performing the
>   - * rolling over of the active log file. The <code>RollingPolicy</ 
> code>
>   - * is also responsible for providing the <em>active log file</em>,
>   - * that is the live file where logging output will be directed.
>   - *
>   + * A <code>RollingPolicy</code> specifies the actions taken
>   + * on a logging file rollover.
>   + *
>     * @author Ceki G&uuml;lc&uuml;
>   + * @author Curt Arnold
>     * @since 1.3
>     * */
>   -public interface RollingPolicy {
>   -
>   +public interface RollingPolicy extends OptionHandler {
>      /**
>   -   * Rolls over log files according to implementation policy.
>   -   * <p>
>   -   * <p>This method is invoked by {@link RollingFileAppender},  
> usually
>   -   * at the behest of its {@link TriggeringPolicy}.
>   -   *
>   -   * @throws RolloverFailure Thrown if the rollover operation  
> fails for any
>   -   * reason.
>   +   * Prepare for a rollover.  This method is called prior to
>   +   * closing the active log file and performs any necessary
>   +   * preliminary actions.  The rolling file appender will then
>   +   * close the active log file, perform the synchronous actions
>   +   * and dispatch the asynchronous files before returning
>   +   * control to its caller.
>   +   *
>   +   * @param activeFile buffer containing name of the active log  
> file on entry,
>   +   * and name of future active file on exit.
>   +   * @param synchronousActions list to which instances of Runnable
>   +   * are appended for actions to be performed after closing the  
> active file.
>   +   * @param asynchronousActions list to which instances of Runnable
>   +   * are appended for actions to be performed asynchronously
>   +   * after closing the active file and executing the synchronous  
> actions
>   +   * @return true if rollover should proceed.  If false rollover  
> will
>   +   * silently be skipped.
>   +   * @throws IOException on failure to prepare for rollover.   
> Rollover
>   +   * will be suppressed if an exception is thrown.
>       */
>   -  public void rollover() throws RolloverFailure;
>   -
>   -  /**
>   -   * Get the new name of the active log file.
>   -   * */
>   -  public String getActiveFileName();
>   +  public boolean rollover(
>   +    StringBuffer activeFile, List synchronousActions, List  
> asynchronousActions)
>   +    throws IOException;
>    }
>
>
>
>   1.5       +68 -44    logging-log4j/src/java/org/apache/log4j/ 
> rolling/RollingPolicyBase.java
>
>   Index: RollingPolicyBase.java
>   ===================================================================
>   RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/ 
> rolling/RollingPolicyBase.java,v
>   retrieving revision 1.4
>   retrieving revision 1.5
>   diff -u -r1.4 -r1.5
>   --- RollingPolicyBase.java    22 May 2005 07:56:23 -0000    1.4
>   +++ RollingPolicyBase.java    23 May 2005 23:51:01 -0000    1.5
>   @@ -1,5 +1,5 @@
>    /*
>   - * Copyright 1999,2004 The Apache Software Foundation.
>   + * Copyright 1999,2005 The Apache Software Foundation.
>     *
>     * Licensed under the Apache License, Version 2.0 (the "License");
>     * you may not use this file except in compliance with the License.
>   @@ -16,14 +16,14 @@
>
>    package org.apache.log4j.rolling;
>
>   -import org.apache.log4j.rolling.helper.Compress;
>   +import org.apache.log4j.pattern.FormattingInfo;
>   +import org.apache.log4j.pattern.PatternConverter;
>    import org.apache.log4j.pattern.PatternParser;
>    import org.apache.log4j.spi.ComponentBase;
>    import org.apache.log4j.spi.OptionHandler;
>   -import org.apache.log4j.pattern.PatternConverter;
>   -import org.apache.log4j.pattern.FormattingInfo;
>   -import java.util.List;
>   +
>    import java.util.ArrayList;
>   +import java.util.List;
>
>
>    /**
>   @@ -32,45 +32,48 @@
>     * getter/setter.
>     *
>     * @author Ceki G&uuml;lc&uuml;
>   + * @author Curt Arnold
>     * @since 1.3
>     */
>    public abstract class RollingPolicyBase extends ComponentBase
>   -        implements RollingPolicy, OptionHandler {
>   -  protected int compressionMode = Compress.NONE;
>   +  implements RollingPolicy, OptionHandler {
>   +  /**
>   +   * File name pattern converters.
>   +   */
>      protected PatternConverter[] patternConverters;
>   +
>   +  /**
>   +   * File name field specifiers.
>   +   */
>      protected FormattingInfo[] patternFields;
>   +
>   +  /**
>   +   * File name pattern.
>   +   */
>      protected String fileNamePatternStr;
>   +
>   +  /**
>   +   * Active file name may be null.
>   +   */
>      protected String activeFileName;
>
>   -  /*
>   -   * @see org.apache.log4j.spi.OptionHandler#activateOptions()
>   +  /**
>   +   * {@inheritDoc}
>       */
>      public abstract void activateOptions();
>
>      /**
>   -   * Given the FileNamePattern string, this method determines  
> the compression
>   -   * mode depending on last letters of the fileNamePatternStr.  
> Patterns
>   -   * ending with .gz imply GZIP compression, endings with '.zip'  
> imply
>   -   * ZIP compression. Otherwise and by default, there is no  
> compression.
>   -   *
>   +   * Set file name pattern.
>   +   * @param fnp file name pattern.
>       */
>   -  protected void determineCompressionMode() {
>   -     if (fileNamePatternStr.endsWith(".gz")) {
>   -      getLogger().debug("Will use gz compression");
>   -      compressionMode = Compress.GZ;
>   -    } else if (fileNamePatternStr.endsWith(".zip")) {
>   -      getLogger().debug("Will use zip compression");
>   -      compressionMode = Compress.ZIP;
>   -    } else {
>   -      getLogger().debug("No compression will be used");
>   -      compressionMode = Compress.NONE;
>   -    }
>   -  }
>   -
>      public void setFileNamePattern(String fnp) {
>        fileNamePatternStr = fnp;
>      }
>
>   +  /**
>   +   * Get file name pattern.
>   +   * @return file name pattern.
>   +   */
>      public String getFileNamePattern() {
>        return fileNamePatternStr;
>      }
>   @@ -83,25 +86,46 @@
>        activeFileName = afn;
>      }
>
>   +  /**
>   +   * Return the value of the <b>ActiveFile</b> option.
>   +   * @return active file name.
>   +  */
>   +  public String getActiveFileName() {
>   +    return activeFileName;
>   +  }
>   +
>   +  /**
>   +   *   Parse file name pattern.
>   +   */
>      protected final void parseFileNamePattern() {
>   -      List converters = new ArrayList();
>   -      List fields = new ArrayList();
>   +    List converters = new ArrayList();
>   +    List fields = new ArrayList();
>
>   -      PatternParser.parse(fileNamePatternStr, converters,  
> fields, null,
>   -              PatternParser.getFileNamePatternRules(), getLogger 
> ());
>   -      patternConverters = new PatternConverter[converters.size()];
>   -      patternConverters = (PatternConverter[]) converters.toArray 
> (patternConverters);
>   -      patternFields = new FormattingInfo[converters.size()];
>   -      patternFields = (FormattingInfo[]) fields.toArray 
> (patternFields);
>   -  }
>   -
>   -  protected final void formatFileName(final Object obj, final  
> StringBuffer buf) {
>   -      for(int i = 0; i < patternConverters.length; i++) {
>   -          int fieldStart = buf.length();
>   -          patternConverters[i].format(obj, buf);
>   -          if (patternFields[i] != null) {
>   -              patternFields[i].format(fieldStart, buf);
>   -          }
>   +    PatternParser.parse(
>   +      fileNamePatternStr, converters, fields, null,
>   +      PatternParser.getFileNamePatternRules(), getLogger());
>   +    patternConverters = new PatternConverter[converters.size()];
>   +    patternConverters =
>   +      (PatternConverter[]) converters.toArray(patternConverters);
>   +    patternFields = new FormattingInfo[converters.size()];
>   +    patternFields = (FormattingInfo[]) fields.toArray 
> (patternFields);
>   +  }
>   +
>   +  /**
>   +   * Format file name.
>   +   *
>   +   * @param obj object to be evaluted in formatting, may not be  
> null.
>   +   * @param buf string buffer to which formatted file name is  
> appended, may not be null.
>   +   */
>   +  protected final void formatFileName(
>   +    final Object obj, final StringBuffer buf) {
>   +    for (int i = 0; i < patternConverters.length; i++) {
>   +      int fieldStart = buf.length();
>   +      patternConverters[i].format(obj, buf);
>   +
>   +      if (patternFields[i] != null) {
>   +        patternFields[i].format(fieldStart, buf);
>          }
>   +    }
>      }
>    }
>
>
>
>   1.11      +38 -17    logging-log4j/src/java/org/apache/log4j/ 
> rolling/SizeBasedTriggeringPolicy.java
>
>   Index: SizeBasedTriggeringPolicy.java
>   ===================================================================
>   RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/ 
> rolling/SizeBasedTriggeringPolicy.java,v
>   retrieving revision 1.10
>   retrieving revision 1.11
>   diff -u -r1.10 -r1.11
>   --- SizeBasedTriggeringPolicy.java    8 May 2005 03:43:40  
> -0000    1.10
>   +++ SizeBasedTriggeringPolicy.java    23 May 2005 23:51:01  
> -0000    1.11
>   @@ -1,12 +1,12 @@
>    /*
>     * Copyright 1999,2005 The Apache Software Foundation.
>   - *
>   + *
>     * Licensed under the Apache License, Version 2.0 (the "License");
>     * you may not use this file except in compliance with the License.
>     * You may obtain a copy of the License at
>   - *
>   + *
>     *      http://www.apache.org/licenses/LICENSE-2.0
>   - *
>   + *
>     * Unless required by applicable law or agreed to in writing,  
> software
>     * distributed under the License is distributed on an "AS IS"  
> BASIS,
>     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express  
> or implied.
>   @@ -19,45 +19,66 @@
>    import org.apache.log4j.Appender;
>    import org.apache.log4j.spi.LoggingEvent;
>    import org.apache.log4j.spi.OptionHandler;
>   -import java.io.File;
>
>
>    /**
>     * SizeBasedTriggeringPolicy looks at size of the file being
>     * currently written to.
>   - *
>   + *
>     * @author Ceki G&uuml;lc&uuml;
>   + * @author Curt Arnold
>     *
>     */
>   -public class SizeBasedTriggeringPolicy implements  
> TriggeringPolicy, OptionHandler {
>   -  long maxFileSize = 10 * 1024 * 1024; // let 10 MB the default  
> max size
>   -
>   -
>   +public final class SizeBasedTriggeringPolicy implements  
> TriggeringPolicy,
>   +  OptionHandler {
>   +  /**
>   +   * Rollover threshold size in bytes.
>   +   */
>   +  private long maxFileSize = 10 * 1024 * 1024; // let 10 MB the  
> default max size
>   +
>   +  /**
>   +   * Constructs a new instance.
>   +   */
>      public SizeBasedTriggeringPolicy() {
>      }
>
>   +  /**
>   +   * Constructs an new instance.
>   +   * @param maxFileSize rollover threshold size in bytes.
>   +   */
>      public SizeBasedTriggeringPolicy(final long maxFileSize) {
>   -      this.maxFileSize = maxFileSize;
>   +    this.maxFileSize = maxFileSize;
>      }
>
>   -
>   -  public boolean isTriggeringEvent(final Appender appender,
>   -                                   final LoggingEvent event,
>   -                                   final File file,
>   -                                   final long fileLength) {
>   +  /**
>   +   * {@inheritDoc}
>   +   */
>   +  public boolean isTriggeringEvent(
>   +    final Appender appender, final LoggingEvent event, final  
> String file,
>   +    final long fileLength) {
>        //System.out.println("Size"+file.length());
>        return (fileLength >= maxFileSize);
>      }
>
>   +  /**
>   +   * Gets rollover threshold size in bytes.
>   +   * @return rollover threshold size in bytes.
>   +   */
>      public long getMaxFileSize() {
>        return maxFileSize;
>      }
>
>   +  /**
>   +   * Sets rollover threshold size in bytes.
>   +   * @param l new value for rollover threshold size.
>   +   */
>      public void setMaxFileSize(long l) {
>        maxFileSize = l;
>      }
>   -
>   +
>   +  /**
>   +   * Prepares policy for use.
>   +   */
>      public void activateOptions() {
>   -
>      }
>    }
>
>
>
>   1.22      +163 -156  logging-log4j/src/java/org/apache/log4j/ 
> rolling/TimeBasedRollingPolicy.java
>
>   Index: TimeBasedRollingPolicy.java
>   ===================================================================
>   RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/ 
> rolling/TimeBasedRollingPolicy.java,v
>   retrieving revision 1.21
>   retrieving revision 1.22
>   diff -u -r1.21 -r1.22
>   --- TimeBasedRollingPolicy.java    22 May 2005 07:56:23 -0000     
> 1.21
>   +++ TimeBasedRollingPolicy.java    23 May 2005 23:51:01 -0000     
> 1.22
>   @@ -16,28 +16,29 @@
>
>    package org.apache.log4j.rolling;
>
>   -import org.apache.log4j.rolling.helper.Compress;
>   +import org.apache.log4j.Appender;
>    import org.apache.log4j.pattern.DatePatternConverter;
>    import org.apache.log4j.pattern.PatternConverter;
>   -import org.apache.log4j.rolling.helper.Util;
>   +import org.apache.log4j.rolling.helper.*;
>    import org.apache.log4j.spi.LoggingEvent;
>   -import org.apache.log4j.Appender;
>
>   -import java.util.Date;
>    import java.io.File;
>
>   +import java.util.Date;
>   +import java.util.List;
>   +
>
>    /**
>   - * <code>TimeBasedRollingPolicy</code> is both easy to configure  
> and quite
>   - * powerful.
>   - *
>   - * <p>In order to use  <code>TimeBasedRollingPolicy</code>, the
>   - * <b>FileNamePattern</b> option must be set. It basically  
> specifies the name of the
>   - * rolled log files. The value <code>FileNamePattern</code>  
> should consist of
>   - * the name of the file, plus a suitably placed <code>%d</code>  
> conversion
>   - * specifier. The <code>%d</code> conversion specifier may  
> contain a date and
>   - * time pattern as specified by the {@link  
> java.text.SimpleDateFormat} class. If
>   - * the date and time pattern is ommitted, then the default  
> pattern of
>   + * <code>TimeBasedRollingPolicy</code> is both easy to configure  
> and quite
>   + * powerful.
>   + *
>   + * <p>In order to use  <code>TimeBasedRollingPolicy</code>, the
>   + * <b>FileNamePattern</b> option must be set. It basically  
> specifies the name of the
>   + * rolled log files. The value <code>FileNamePattern</code>  
> should consist of
>   + * the name of the file, plus a suitably placed <code>%d</code>  
> conversion
>   + * specifier. The <code>%d</code> conversion specifier may  
> contain a date and
>   + * time pattern as specified by the {@link  
> java.text.SimpleDateFormat} class. If
>   + * the date and time pattern is ommitted, then the default  
> pattern of
>     * "yyyy-MM-dd" is assumed. The following examples should  
> clarify the point.
>     *
>     * <p>
>   @@ -49,29 +50,29 @@
>     *   </tr>
>     *   <tr>
>     *     <td nowrap="true"><code>/wombat/folder/foo.%d</code></td>
>   - *     <td>Daily rollover (at midnight).  Due to the omission of  
> the optional
>   + *     <td>Daily rollover (at midnight).  Due to the omission of  
> the optional
>     *         time and date pattern for the %d token specifier, the  
> default pattern
>     *         of "yyyy-MM-dd" is assumed, which corresponds to  
> daily rollover.
>     *     </td>
>   - *     <td>During November 23rd, 2004, logging output will go to
>   + *     <td>During November 23rd, 2004, logging output will go to
>     *       the file <code>/wombat/foo.2004-11-23</code>. At  
> midnight and for
>   - *       the rest of the 24th, logging output will be directed to
>   - *       <code>/wombat/foo.2004-11-24</code>.
>   + *       the rest of the 24th, logging output will be directed to
>   + *       <code>/wombat/foo.2004-11-24</code>.
>     *     </td>
>     *   </tr>
>     *   <tr>
>     *     <td nowrap="true"><code>/wombat/foo.%d{yyyy-MM}.log</ 
> code></td>
>     *     <td>Rollover at the beginning of each month.</td>
>     *     <td>During the month of October 2004, logging output will  
> go to
>   - *     <code>/wombat/foo.2004-10.log</code>. After midnight of  
> October 31st
>   - *     and for the rest of November, logging output will be  
> directed to
>   + *     <code>/wombat/foo.2004-10.log</code>. After midnight of  
> October 31st
>   + *     and for the rest of November, logging output will be  
> directed to
>     *       <code>/wombat/foo.2004-11.log</code>.
>     *     </td>
>     *   </tr>
>     * </table>
>     * <h2>Automatic file compression</h2>
>   - * <code>TimeBasedRollingPolicy</code> supports automatic file  
> compression.
>   - * This feature is enabled if the value of the  
> <b>FileNamePattern</b> option
>   + * <code>TimeBasedRollingPolicy</code> supports automatic file  
> compression.
>   + * This feature is enabled if the value of the  
> <b>FileNamePattern</b> option
>     * ends with <code>.gz</code> or <code>.zip</code>.
>     * <p>
>     * <table cellspacing="5px" border="1">
>   @@ -82,26 +83,26 @@
>     *   </tr>
>     *   <tr>
>     *     <td nowrap="true"><code>/wombat/foo.%d.gz</code></td>
>   - *     <td>Daily rollover (at midnight) with automatic GZIP  
> compression of the
>   - *      arcived files.</td>
>   - *     <td>During November 23rd, 2004, logging output will go to
>   + *     <td>Daily rollover (at midnight) with automatic GZIP  
> compression of the
>   + *      archived files.</td>
>   + *     <td>During November 23rd, 2004, logging output will go to
>     *       the file <code>/wombat/foo.2004-11-23</code>. However,  
> at midnight that
>     *       file will be compressed to become <code>/wombat/foo. 
> 2004-11-23.gz</code>.
>   - *       For the 24th of November, logging output will be  
> directed to
>   + *       For the 24th of November, logging output will be  
> directed to
>     *       <code>/wombat/folder/foo.2004-11-24</code> until its  
> rolled over at the
>     *       beginning of the next day.
>     *     </td>
>     *   </tr>
>     * </table>
>   - *
>   + *
>     * <h2>Decoupling the location of the active log file and the  
> archived log files</h2>
>   - * <p>The <em>active file</em> is defined as the log file for  
> the current period
>   - * whereas <em>archived files</em> are thos files which have  
> been rolled over
>   + * <p>The <em>active file</em> is defined as the log file for  
> the current period
>   + * whereas <em>archived files</em> are those files which have  
> been rolled over
>     * in previous periods.
>   - *
>   - * <p>By setting the <b>ActiveFileName</b> option you can  
> decouple the location
>   + *
>   + * <p>By setting the <b>ActiveFileName</b> option you can  
> decouple the location
>     * of the active log file and the location of the archived log  
> files.
>   - * <p>
>   + * <p>
>     *  <table cellspacing="5px" border="1">
>     *   <tr>
>     *     <th><code>FileNamePattern</code> value</th>
>   @@ -113,13 +114,13 @@
>     *     <td nowrap="true"><code>/wombat/foo.log.%d</code></td>
>     *     <td nowrap="true"><code>/wombat/foo.log</code></td>
>     *     <td>Daily rollover.</td>
>   - *
>   - *     <td>During November 23rd, 2004, logging output will go to
>   - *       the file <code>/wombat/foo.log</code>. However, at  
> midnight that file
>   + *
>   + *     <td>During November 23rd, 2004, logging output will go to
>   + *       the file <code>/wombat/foo.log</code>. However, at  
> midnight that file
>     *       will archived as <code>/wombat/foo.log.2004-11-23</ 
> code>. For the 24th
>   - *       of November, logging output will be directed to
>   - *       <code>/wombat/folder/foo.log</code> until its archived as
>   - *       <code>/wombat/foo.log.2004-11-24</code> at the  
> beginning of the next
>   + *       of November, logging output will be directed to
>   + *       <code>/wombat/folder/foo.log</code> until its archived as
>   + *       <code>/wombat/foo.log.2004-11-24</code> at the  
> beginning of the next
>     *       day.
>     *     </td>
>     *   </tr>
>   @@ -132,28 +133,51 @@
>     * <code>RollingFileAppender</code>.
>     *
>     * @author Ceki G&uuml;lc&uuml;
>   + * @author Curt Arnold
>     * @since 1.3
>     */
>   -public class TimeBasedRollingPolicy extends RollingPolicyBase  
> implements TriggeringPolicy {
>   -  static final String FNP_NOT_SET =
>   +public final class TimeBasedRollingPolicy extends RollingPolicyBase
>   +  implements TriggeringPolicy {
>   +  /**
>   +   * Error message.
>   +   */
>   +  private static final String FNP_NOT_SET =
>        "The FileNamePattern option must be set before using  
> TimeBasedRollingPolicy. ";
>   -  static final String SEE_FNP_NOT_SET =
>   +
>   +  /**
>   +   *   Reference for error message.
>   +   */
>   +  private static final String SEE_FNP_NOT_SET =
>        "See also http://logging.apache.org/log4j/ 
> codes.html#tbr_fnp_not_set";
>   -  long nextCheck;
>   -  Date lastCheck = new Date();
>   -  String elapsedPeriodsFileName;
>   -  Util util = new Util();
>   -  Compress compress = new Compress();
>   -
>   +
>   +  /**
>   +   * Time for next determination if time for rollover.
>   +   */
>   +  private long nextCheck = 0;
>   +
>   +  /**
>   +   * File name at last rollover.
>   +   */
>   +  private String lastFileName = null;
>   +
>   +  /**
>   +   * Length of any file type suffix (.gz, .zip).
>   +   */
>   +  private int suffixLength = 0;
>   +
>   +  /**
>   +   * Constructs a new instance.
>   +   */
>   +  public TimeBasedRollingPolicy() {
>   +  }
>   +
>   +  /**
>   +   * Prepares instance of use.
>   +   */
>      public void activateOptions() {
>   -    // set the LR for our utility object
>   -    util.setLoggerRepository(this.repository);
>   -    compress.setLoggerRepository(this.repository);
>   -
>        // find out period from the filename pattern
>        if (fileNamePatternStr != null) {
>          parseFileNamePattern();
>   -      determineCompressionMode();
>        } else {
>          getLogger().warn(FNP_NOT_SET);
>          getLogger().warn(SEE_FNP_NOT_SET);
>   @@ -161,11 +185,13 @@
>        }
>
>        PatternConverter dtc = null;
>   +
>        for (int i = 0; i < patternConverters.length; i++) {
>   -        if (patternConverters[i] instanceof DatePatternConverter) {
>   -            dtc = patternConverters[i];
>   -            break;
>   -        }
>   +      if (patternConverters[i] instanceof DatePatternConverter) {
>   +        dtc = patternConverters[i];
>   +
>   +        break;
>   +      }
>        }
>
>        if (dtc == null) {
>   @@ -174,122 +200,103 @@
>            + "] does not contain a valid date format specifier");
>        }
>
>   +    long n = System.currentTimeMillis();
>   +    StringBuffer buf = new StringBuffer();
>   +    formatFileName(new Date(n), buf);
>   +    lastFileName = buf.toString();
>   +
>   +    suffixLength = 0;
>   +
>   +    if (lastFileName.endsWith(".gz")) {
>   +      suffixLength = 3;
>   +    } else if (lastFileName.endsWith(".zip")) {
>   +      suffixLength = 3;
>   +    }
>   +  }
>
>   -
>   +  /**
>   +   * {@inheritDoc}
>   +   */
>   +  public boolean rollover(
>   +    final StringBuffer activeFile, final List synchronousActions,
>   +    final List asynchronousActions) {
>        long n = System.currentTimeMillis();
>   -    lastCheck.setTime(n);
>   -    nextCheck = (n/1000 + 1) * 1000;
>   +    nextCheck = ((n / 1000) + 1) * 1000;
>
>   -    //Date nc = new Date();
>   -    //nc.setTime(nextCheck);
>   -    //getLogger().debug("Next check set to: " + nc);
>   -  }
>   +    StringBuffer buf = new StringBuffer();
>   +    formatFileName(new Date(n), buf);
>
>   -  public void rollover() throws RolloverFailure {
>   -    getLogger().debug("rollover called");
>   -    getLogger().debug("compressionMode: " + compressionMode);
>   +    String newFileName = buf.toString();
>
>   -    if (activeFileName == null) {
>   -      switch (compressionMode) {
>   -      case Compress.NONE:
>   -        // nothing to do;
>   -        break;
>   -      case Compress.GZ:
>   -        getLogger().debug("GZIP compressing [{}]",  
> elapsedPeriodsFileName);
>   -        compress.GZCompress(elapsedPeriodsFileName);
>   -        break;
>   -      case Compress.ZIP:
>   -        getLogger().debug("ZIP compressing [{}]",  
> elapsedPeriodsFileName);
>   -        compress.ZIPCompress(elapsedPeriodsFileName);
>   -        break;
>   -      }
>   -    } else {
>   -      switch (compressionMode) {
>   -      case Compress.NONE:
>   -        util.rename(activeFileName, elapsedPeriodsFileName);
>   -        break;
>   -      case Compress.GZ:
>   -        getLogger().debug("GZIP compressing [[}]",  
> elapsedPeriodsFileName);
>   -        compress.GZCompress(activeFileName,  
> elapsedPeriodsFileName);
>   -        break;
>   -      case Compress.ZIP:
>   -        getLogger().debug("ZIP compressing [[}]",  
> elapsedPeriodsFileName);
>   -        compress.ZIPCompress(activeFileName,  
> elapsedPeriodsFileName);
>   -        break;
>   +    //
>   +    //  if file names haven't changed, no rollover
>   +    //
>   +    if (newFileName.equals(lastFileName)) {
>   +      activeFile.setLength(0);
>   +
>   +      if (activeFileName == null) {
>   +        activeFile.append(
>   +          newFileName.substring(0, newFileName.length() -  
> suffixLength));
>   +      } else {
>   +        activeFile.append(activeFileName);
>          }
>   +
>   +      return false;
>        }
>   -  }
>
>   -  /**
>   -  *
>   -  * The active log file is determined by the value of the  
> activeFileName
>   -  * option if it is set. However, in case the activeFileName is  
> left blank,
>   -  * then, the active log file equals the file name for the  
> current period
>   -  * as computed by the <b>FileNamePattern</b> option.
>   -  *
>   -  */
>   -  public String getActiveFileName() {
>   -    getLogger().debug("getActiveLogFileName called");
>   +    File lastBaseFile =
>   +      new File(
>   +        lastFileName.substring(0, lastFileName.length() -  
> suffixLength));
>   +
>   +    boolean lastFileExists = false;
>   +
>   +    //
>   +    //   if no explicit active file name then
>   +    //      change active file name to new name
>   +    //
>        if (activeFileName == null) {
>   -      return formatActiveFileName(lastCheck);
>   +      lastFileExists = lastBaseFile.exists();
>   +      activeFile.setLength(0);
>   +      activeFile.append(newFileName);
>   +
>   +      if (suffixLength > 0) {
>   +        activeFile.setLength(activeFile.length() - suffixLength);
>   +      }
>        } else {
>   -      return activeFileName;
>   +      activeFile.setLength(0);
>   +      activeFile.append(activeFileName);
>   +
>   +      File currentActiveFile = new File(activeFileName);
>   +      lastFileExists = currentActiveFile.exists();
>   +      synchronousActions.add(
>   +        new FileRenameAction(currentActiveFile, lastBaseFile,  
> true));
>        }
>   -  }
>
>   -  private String formatActiveFileName(Date date) {
>   -      StringBuffer buf = new StringBuffer();
>   -      formatFileName(date, buf);
>   -      switch(compressionMode) {
>   -      case Compress.GZ:
>   -        if (buf.length() > 3) {
>   -           buf.setLength(buf.length() - 3);
>   -        }
>   -        break;
>   +    if ((suffixLength > 0) && lastFileExists) {
>   +      File compressedFile = new File(lastFileName);
>
>   -      case Compress.ZIP:
>   -        if (buf.length() > 4) {
>   -            buf.setLength(buf.length() - 4);
>   -        }
>   -        break;
>   +      if (suffixLength == 3) {
>   +        asynchronousActions.add(
>   +          new GZCompressAction(
>   +            lastBaseFile, compressedFile, true, getLogger()));
>   +      } else if (suffixLength == 4) {
>   +        asynchronousActions.add(
>   +          new ZipCompressAction(
>   +            lastBaseFile, compressedFile, true, getLogger()));
>          }
>   -      return buf.toString();
>   -  }
>   -
>   -  public boolean isTriggeringEvent(final Appender appender,
>   -                                   final LoggingEvent event,
>   -                                   final File file,
>   -                                   final long fileLength) {
>   -    //getLogger().debug("Is triggering event called");
>   -    long n = System.currentTimeMillis();
>   +    }
>
>   -    if (n >= nextCheck) {
>   -      //
>   -      //   next check on next integral second
>   -      nextCheck = (n/1000 + 1) * 1000;
>   -
>   -      StringBuffer buf = new StringBuffer();
>   -      formatFileName(lastCheck, buf);
>   -      String lastName = buf.toString();
>   -      buf.setLength(0);
>   -      formatFileName(new Date(n), buf);
>   -      String newName = buf.toString();
>   -      if (lastName.equals(newName)) return false;
>   -
>   -      getLogger().debug("Time to trigger rollover");
>   -
>   -      // We set the elapsedPeriodsFileName before we set the  
> 'lastCheck' variable
>   -      // The elapsedPeriodsFileName corresponds to the file name  
> of the period
>   -      // that just elapsed.
>   -      elapsedPeriodsFileName = formatActiveFileName(lastCheck);
>   -      getLogger().debug(
>   -        "elapsedPeriodsFileName set to {}",  
> elapsedPeriodsFileName);
>   +    lastFileName = newFileName;
>
>   -      lastCheck.setTime(n);
>   +    return true;
>   +  }
>
>   -      return true;
>   -    } else {
>   -      return false;
>   -    }
>   +  /**
>   +   * {@inheritDoc}
>   +   */
>   +  public boolean isTriggeringEvent(
>   +    final Appender appender, final LoggingEvent event, final  
> String filename,
>   +    final long fileLength) {
>   +    return System.currentTimeMillis() >= nextCheck;
>      }
>    }
>
>
>
>   1.8       +17 -14    logging-log4j/src/java/org/apache/log4j/ 
> rolling/TriggeringPolicy.java
>
>   Index: TriggeringPolicy.java
>   ===================================================================
>   RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/ 
> rolling/TriggeringPolicy.java,v
>   retrieving revision 1.7
>   retrieving revision 1.8
>   diff -u -r1.7 -r1.8
>   --- TriggeringPolicy.java    8 May 2005 03:43:40 -0000    1.7
>   +++ TriggeringPolicy.java    23 May 2005 23:51:01 -0000    1.8
>   @@ -13,14 +13,13 @@
>     * See the License for the specific language governing  
> permissions and
>     * limitations under the License.
>     */
>   +
>    package org.apache.log4j.rolling;
>
>    import org.apache.log4j.Appender;
>    import org.apache.log4j.spi.LoggingEvent;
>    import org.apache.log4j.spi.OptionHandler;
>
>   -import java.io.File;
>   -
>
>    /**
>     * A <code>TriggeringPolicy</code> controls the conditions under  
> which rollover
>   @@ -28,18 +27,22 @@
>     * external event, the log request or a combination thereof.
>     *
>     * @author Ceki G&uuml;lc&uuml;
>   + * @author Curt Arnold
>     * @since 1.3
>     * */
>   -public interface TriggeringPolicy {
>   -    /**
>   -     * Should rolllover be triggered at this time?
>   -     *
>   -     * @param appender A reference to the appender.
>   -     * @param event A reference to the currently event.
>   -     * @param file A reference to the currently active log file.
>   -     * @param fileLength Length of the file in bytes.
>   -     * @return true if a rollover should occur.
>   -     */
>   -    public boolean isTriggeringEvent(final Appender appender,
>   -        final LoggingEvent event, final File file, final long  
> fileLength);
>   +public interface TriggeringPolicy extends OptionHandler {
>   +  /**
>   +   * Determines if a rollover may be appropriate at this time.  If
>   +   * true is returned, RolloverPolicy.rollover will be called  
> but it
>   +   * can determine that a rollover is not warranted.
>   +   *
>   +   * @param appender A reference to the appender.
>   +   * @param event A reference to the currently event.
>   +   * @param filename The filename for the currently active log  
> file.
>   +   * @param fileLength Length of the file in bytes.
>   +   * @return true if a rollover should occur.
>   +   */
>   +  public boolean isTriggeringEvent(
>   +    final Appender appender, final LoggingEvent event, final  
> String filename,
>   +    final long fileLength);
>    }
>
>
>
>   1.1                  logging-log4j/src/java/org/apache/log4j/ 
> rolling/helper/Action.java
>
>   Index: Action.java
>   ===================================================================
>   /*
>    * Copyright 1999,2005 The Apache Software Foundation.
>    *
>    * Licensed under the Apache License, Version 2.0 (the "License");
>    * you may not use this file except in compliance with the License.
>    * You may obtain a copy of the License at
>    *
>    *      http://www.apache.org/licenses/LICENSE-2.0
>    *
>    * Unless required by applicable law or agreed to in writing,  
> software
>    * distributed under the License is distributed on an "AS IS" BASIS,
>    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express  
> or implied.
>    * See the License for the specific language governing  
> permissions and
>    * limitations under the License.
>    */
>
>   package org.apache.log4j.rolling.helper;
>
>   import java.io.IOException;
>
>
>   /**
>    * The Action interface should be implemented by any class that  
> performs
>    * file system actions for RollingFileAppenders after the close of
>    * the active log file.
>    *
>    * @author Curt Arnold
>    * @since 1.3
>    */
>   public interface Action extends Runnable {
>     /**
>      * Perform an action.
>      * @return  true if action was successful.  A return value of  
> false will cause
>      * the rollover to be aborted if possible.
>      * @throws IOException if IO error, a thrown exception will  
> cause the rollover
>      * to be aborted if possible.
>      */
>     boolean execute() throws IOException;
>   }
>
>
>
>   1.1                  logging-log4j/src/java/org/apache/log4j/ 
> rolling/helper/CompositeAction.java
>
>   Index: CompositeAction.java
>   ===================================================================
>   /*
>    * Copyright 1999,2005 The Apache Software Foundation.
>    *
>    * Licensed under the Apache License, Version 2.0 (the "License");
>    * you may not use this file except in compliance with the License.
>    * You may obtain a copy of the License at
>    *
>    *      http://www.apache.org/licenses/LICENSE-2.0
>    *
>    * Unless required by applicable law or agreed to in writing,  
> software
>    * distributed under the License is distributed on an "AS IS" BASIS,
>    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express  
> or implied.
>    * See the License for the specific language governing  
> permissions and
>    * limitations under the License.
>    */
>
>   package org.apache.log4j.rolling.helper;
>
>   import java.io.IOException;
>
>   import java.util.List;
>   import org.apache.log4j.ULogger;
>
>
>   /**
>    * A group of Actions to be executed in sequence.
>    *
>    * @author Curt Arnold
>    * @since 1.3
>    *
>    */
>   public class CompositeAction implements Action {
>     /**
>      * Actions to perform.
>      */
>     private final Action[] actions;
>
>     /**
>      * Stop on error.
>      */
>     private final boolean stopOnError;
>       /**
>        * Logger.
>        */
>     private final ULogger logger;
>
>     /**
>      * Construct a new composite action.
>      * @param actions list of actions, may not be null.
>      * @param stopOnError if true, stop on the first false return  
> value or exception.
>      * @param logger logger, may be null.
>      */
>     public CompositeAction(final List actions,
>                            final boolean stopOnError,
>                            final ULogger logger) {
>       this.actions = new Action[actions.size()];
>       actions.toArray(this.actions);
>       this.stopOnError = stopOnError;
>       this.logger = logger;
>     }
>
>     /**
>      * {@inheritDoc}
>      */
>     public void run() {
>       try {
>         execute();
>       } catch (IOException ex) {
>           if (logger != null) {
>               logger.info("Exception during file rollover.", ex);
>           }
>       }
>     }
>
>     /**
>      * Execute sequence of actions.
>      * @return true if all actions were successful.
>      * @throws IOException on IO error.
>      */
>     public boolean execute() throws IOException {
>       if (stopOnError) {
>         for (int i = 0; i < actions.length; i++) {
>           if (!actions[i].execute()) {
>             return false;
>           }
>         }
>
>         return true;
>       } else {
>         boolean status = true;
>         IOException exception = null;
>
>         for (int i = 0; i < actions.length; i++) {
>           try {
>             status &= actions[i].execute();
>           } catch (IOException ex) {
>             status = false;
>
>             if (exception == null) {
>               exception = ex;
>             }
>           }
>         }
>
>         if (exception != null) {
>           throw exception;
>         }
>
>         return status;
>       }
>     }
>   }
>
>
>
>   1.1                  logging-log4j/src/java/org/apache/log4j/ 
> rolling/helper/FileRenameAction.java
>
>   Index: FileRenameAction.java
>   ===================================================================
>   /*
>    * Copyright 1999,2005 The Apache Software Foundation.
>    *
>    * Licensed under the Apache License, Version 2.0 (the "License");
>    * you may not use this file except in compliance with the License.
>    * You may obtain a copy of the License at
>    *
>    *      http://www.apache.org/licenses/LICENSE-2.0
>    *
>    * Unless required by applicable law or agreed to in writing,  
> software
>    * distributed under the License is distributed on an "AS IS" BASIS,
>    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express  
> or implied.
>    * See the License for the specific language governing  
> permissions and
>    * limitations under the License.
>    */
>
>   package org.apache.log4j.rolling.helper;
>
>   import java.io.File;
>
>
>   /**
>    * File rename action.
>    *
>    * @author Curt Arnold
>    * @since 1.3
>    */
>   public final class FileRenameAction implements Action {
>     /**
>      * Source.
>      */
>     private final File source;
>
>     /**
>      * Destination.
>      */
>     private final File destination;
>
>     /**
>      * If true, rename empty files, otherwise delete empty files.
>      */
>     private final boolean renameEmptyFiles;
>
>     /**
>      * Creates an FileRenameAction.
>      *
>      * @param src current file name.
>      * @param dst new file name.
>      * @param renameEmptyFiles if true, rename file even if empty,  
> otherwise delete empty files.
>      */
>     public FileRenameAction(
>       final File src, final File dst, boolean renameEmptyFiles) {
>       source = src;
>       destination = dst;
>       this.renameEmptyFiles = renameEmptyFiles;
>     }
>
>     /**
>      * Rename file.
>      *
>      * @return true if successfully renamed.
>      */
>     public boolean execute() {
>       return execute(source, destination, renameEmptyFiles);
>     }
>
>     /**
>      * Rename file.
>      * @param source current file name.
>      * @param destination new file name.
>      * @param renameEmptyFiles if true, rename file even if empty,  
> otherwise delete empty files.
>      * @return true if successfully renamed.
>      */
>     public static boolean execute(
>       final File source, final File destination, boolean  
> renameEmptyFiles) {
>       if (renameEmptyFiles || (source.length() > 0)) {
>         return source.renameTo(destination);
>       }
>
>       return source.delete();
>     }
>
>     /**
>      * @{inheritDoc}
>      */
>     public void run() {
>       execute();
>     }
>   }
>
>
>
>   1.1                  logging-log4j/src/java/org/apache/log4j/ 
> rolling/helper/GZCompressAction.java
>
>   Index: GZCompressAction.java
>   ===================================================================
>   /*
>    * Copyright 1999,2005 The Apache Software Foundation.
>    *
>    * Licensed under the Apache License, Version 2.0 (the "License");
>    * you may not use this file except in compliance with the License.
>    * You may obtain a copy of the License at
>    *
>    *      http://www.apache.org/licenses/LICENSE-2.0
>    *
>    * Unless required by applicable law or agreed to in writing,  
> software
>    * distributed under the License is distributed on an "AS IS" BASIS,
>    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express  
> or implied.
>    * See the License for the specific language governing  
> permissions and
>    * limitations under the License.
>    */
>
>   package org.apache.log4j.rolling.helper;
>
>   import org.apache.log4j.ULogger;
>
>   import java.io.File;
>   import java.io.FileInputStream;
>   import java.io.FileOutputStream;
>   import java.io.IOException;
>
>   import java.util.zip.GZIPOutputStream;
>
>
>   /**
>    * Compresses a file using GZ compression.
>    *
>    * @author Curt Arnold
>    * @since 1.3
>    */
>   public final class GZCompressAction implements Action {
>     /**
>      * Source file.
>      */
>     private final File source;
>
>     /**
>      * Destination file.
>      */
>     private final File destination;
>
>     /**
>      * If true, attempt to delete file on completion.
>      */
>     private final boolean deleteSource;
>
>     /**
>      * Logger to receive diagnostic messages.
>      */
>     private final ULogger logger;
>
>     /**
>      * Create new instance of GZCompressAction.
>      *
>      * @param source file to compress, may not be null.
>      * @param destination compressed file, may not be null.
>      * @param deleteSource if true, attempt to delete file on  
> completion.  Failure to delete
>      * does not cause an exception to be thrown or affect return  
> value.
>      * @param logger logger, may be null.
>      */
>     public GZCompressAction(
>       final File source, final File destination, final boolean  
> deleteSource,
>       final ULogger logger) {
>       if (source == null) {
>         throw new NullPointerException("source");
>       }
>
>       if (destination == null) {
>         throw new NullPointerException("destination");
>       }
>
>       this.source = source;
>       this.destination = destination;
>       this.deleteSource = deleteSource;
>       this.logger = logger;
>     }
>
>     /**
>      * Compress.
>      * @return true if successfully compressed.
>      * @throws IOException on IO exception.
>      */
>     public boolean execute() throws IOException {
>       return execute(source, destination, deleteSource, logger);
>     }
>
>     /**
>      * Compress a file.
>      *
>      * @param source file to compress, may not be null.
>      * @param destination compressed file, may not be null.
>      * @param deleteSource if true, attempt to delete file on  
> completion.  Failure to delete
>      * does not cause an exception to be thrown or affect return  
> value.
>      * @param logger logger, may be null.
>      * @return true if source file compressed.
>      * @throws IOException on IO exception.
>      */
>     public static boolean execute(
>       final File source, final File destination, final boolean  
> deleteSource,
>       final ULogger logger) throws IOException {
>       if (source.exists()) {
>         FileInputStream fis = new FileInputStream(source);
>         FileOutputStream fos = new FileOutputStream(destination,  
> false);
>         GZIPOutputStream gzos = new GZIPOutputStream(fos);
>         byte[] inbuf = new byte[8102];
>         int n;
>
>         while ((n = fis.read(inbuf)) != -1) {
>           gzos.write(inbuf, 0, n);
>         }
>
>         gzos.close();
>         fis.close();
>
>         if (deleteSource) {
>           if (!source.delete() && (logger != null)) {
>             logger.info("Unable to delete {}.", source.toString());
>           }
>         }
>
>         return true;
>       }
>
>       return false;
>     }
>
>     /**
>      * {@inheritDoc}
>      */
>     public void run() {
>       try {
>         execute();
>       } catch (IOException ex) {
>         if (logger != null) {
>           logger.info(
>             "Exception while compressing '" + source.toString() +  
> "'.", ex);
>         }
>       }
>     }
>   }
>
>
>
>   1.1                  logging-log4j/src/java/org/apache/log4j/ 
> rolling/helper/ZipCompressAction.java
>
>   Index: ZipCompressAction.java
>   ===================================================================
>   /*
>    * Copyright 1999,2005 The Apache Software Foundation.
>    *
>    * Licensed under the Apache License, Version 2.0 (the "License");
>    * you may not use this file except in compliance with the License.
>    * You may obtain a copy of the License at
>    *
>    *      http://www.apache.org/licenses/LICENSE-2.0
>    *
>    * Unless required by applicable law or agreed to in writing,  
> software
>    * distributed under the License is distributed on an "AS IS" BASIS,
>    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express  
> or implied.
>    * See the License for the specific language governing  
> permissions and
>    * limitations under the License.
>    */
>
>   package org.apache.log4j.rolling.helper;
>
>   import org.apache.log4j.ULogger;
>
>   import java.io.File;
>   import java.io.FileInputStream;
>   import java.io.FileOutputStream;
>   import java.io.IOException;
>
>   import java.util.zip.ZipEntry;
>   import java.util.zip.ZipOutputStream;
>
>
>   /**
>    * Compresses a file using Zip compression.
>    *
>    * @author Curt Arnold
>    * @since 1.3
>    */
>   public final class ZipCompressAction implements Action {
>     /**
>      * Source file.
>      */
>     private final File source;
>
>     /**
>      * Destination file.
>      */
>     private final File destination;
>
>     /**
>      * If true, attempt to delete file on completion.
>      */
>     private final boolean deleteSource;
>
>     /**
>      * Logger to receive diagnostic messages.
>      */
>     private final ULogger logger;
>
>     /**
>      * Create new instance of GZCompressAction.
>      *
>      * @param source file to compress, may not be null.
>      * @param destination compressed file, may not be null.
>      * @param deleteSource if true, attempt to delete file on  
> completion.  Failure to delete
>      * does not cause an exception to be thrown or affect return  
> value.
>      * @param logger logger, may be null.
>      */
>     public ZipCompressAction(
>       final File source, final File destination, final boolean  
> deleteSource,
>       final ULogger logger) {
>       if (source == null) {
>         throw new NullPointerException("source");
>       }
>
>       if (destination == null) {
>         throw new NullPointerException("destination");
>       }
>
>       this.source = source;
>       this.destination = destination;
>       this.deleteSource = deleteSource;
>       this.logger = logger;
>     }
>
>     /**
>      * Compress.
>      * @return true if successfully compressed.
>      * @throws IOException on IO exception.
>      */
>     public boolean execute() throws IOException {
>       return execute(source, destination, deleteSource, logger);
>     }
>
>     /**
>      * Compress a file.
>      *
>      * @param source file to compress, may not be null.
>      * @param destination compressed file, may not be null.
>      * @param deleteSource if true, attempt to delete file on  
> completion.  Failure to delete
>      * does not cause an exception to be thrown or affect return  
> value.
>      * @param logger logger, may be null.
>      * @return true if source file compressed.
>      * @throws IOException on IO exception.
>      */
>     public static boolean execute(
>       final File source, final File destination, final boolean  
> deleteSource,
>       final ULogger logger) throws IOException {
>       if (source.exists()) {
>         FileInputStream fis = new FileInputStream(source);
>         FileOutputStream fos = new FileOutputStream(destination);
>         ZipOutputStream zos = new ZipOutputStream(fos);
>
>         ZipEntry zipEntry = new ZipEntry(source.getName());
>         zos.putNextEntry(zipEntry);
>
>         byte[] inbuf = new byte[8102];
>         int n;
>
>         while ((n = fis.read(inbuf)) != -1) {
>           zos.write(inbuf, 0, n);
>         }
>
>         zos.close();
>         fis.close();
>
>         if (deleteSource) {
>           if (!source.delete() && (logger != null)) {
>             logger.info("Unable to delete {}.", source.toString());
>           }
>         }
>
>         return true;
>       }
>
>       return false;
>     }
>
>     /**
>      * {@inheritDoc}
>      */
>     public void run() {
>       try {
>         execute();
>       } catch (IOException ex) {
>         if (logger != null) {
>           logger.info(
>             "Exception while compressing '" + source.toString() +  
> "'.", ex);
>         }
>       }
>     }
>   }
>
>
>
>   1.4       +6 -5      logging-log4j/tests/src/java/org/apache/ 
> log4j/rolling/FilterBasedRollingTest.java
>
>   Index: FilterBasedRollingTest.java
>   ===================================================================
>   RCS file: /home/cvs/logging-log4j/tests/src/java/org/apache/log4j/ 
> rolling/FilterBasedRollingTest.java,v
>   retrieving revision 1.3
>   retrieving revision 1.4
>   diff -u -r1.3 -r1.4
>   --- FilterBasedRollingTest.java    8 Mar 2005 22:32:58 -0000    1.3
>   +++ FilterBasedRollingTest.java    23 May 2005 23:51:01 -0000    1.4
>   @@ -19,10 +19,7 @@
>    import junit.framework.TestCase;
>
>
>   -import org.apache.log4j.Level;
>   -import org.apache.log4j.LogManager;
>   -import org.apache.log4j.Logger;
>   -import org.apache.log4j.PatternLayout;
>   +import org.apache.log4j.*;
>    import org.apache.log4j.filter.LevelRangeFilter;
>    import org.apache.log4j.joran.JoranConfigurator;
>    import org.apache.log4j.util.Compare;
>   @@ -42,6 +39,9 @@
>      }
>
>      public void setUp() {
>   +      Appender ca = new ConsoleAppender(new PatternLayout("%d % 
> level %c -%m%n"));
>   +      ca.setName("CONSOLE");
>   +      Logger.getRootLogger().addAppender(ca);
>      }
>
>      public void tearDown() {
>   @@ -62,7 +62,7 @@
>
>      /**
>       * Test basic rolling functionality using explicit configuration.
>   -   * @remarks Test fails when run immediately after test1.
>   +   * Test fails when run immediately after test1.
>       */
>      public void xtest2() throws Exception {
>        PatternLayout layout = new PatternLayout("%m\n");
>   @@ -88,6 +88,7 @@
>        rfa.setTriggeringPolicy(fbtp);
>        rfa.activateOptions();
>        Logger.getRootLogger().addAppender(rfa);
>   +    Logger.getRootLogger().setLevel(Level.DEBUG);
>
>        common("output/filterBased-test2");
>      }
>
>
>
>   1.19      +8 -0      logging-log4j/tests/src/java/org/apache/ 
> log4j/rolling/TimeBasedRollingTest.java
>
>   Index: TimeBasedRollingTest.java
>   ===================================================================
>   RCS file: /home/cvs/logging-log4j/tests/src/java/org/apache/log4j/ 
> rolling/TimeBasedRollingTest.java,v
>   retrieving revision 1.18
>   retrieving revision 1.19
>   diff -u -r1.18 -r1.19
>   --- TimeBasedRollingTest.java    8 Mar 2005 22:32:58 -0000    1.18
>   +++ TimeBasedRollingTest.java    23 May 2005 23:51:01 -0000    1.19
>   @@ -170,6 +170,8 @@
>          Thread.sleep(500);
>        }
>
>   +    rfa2.close();
>   +
>        for (int i = 0; i < 4; i++) {
>          assertTrue(Compare.compare(filenames[i], "witness/rolling/ 
> tbr-test2." + i));
>        }
>   @@ -217,6 +219,8 @@
>          //System.out.println(i + " expected filename [" + filenames 
> [i] + "].");
>        }
>
>   +    rfa.close();
>   +
>        for (int i = 0; i < 3; i++) {
>          assertTrue(Compare.gzCompare(filenames[i], "witness/ 
> rolling/tbr-test3." + i + ".gz"));
>        }
>   @@ -282,6 +286,8 @@
>          Thread.sleep(500);
>        }
>
>   +    rfa2.close();
>   +
>        for (int i = 0; i < 4; i++) {
>          assertTrue(Compare.compare(filenames[i], "witness/rolling/ 
> tbr-test4." + i));
>        }
>   @@ -374,6 +380,8 @@
>          //System.out.println(i + " expected filename [" + filenames 
> [i] + "].");
>        }
>
>   +    rfa.close();
>   +
>        for (int i = 0; i < 3; i++) {
>          assertTrue(Compare.gzCompare(filenames[i], "witness/ 
> rolling/tbr-test6." + i + ".gz"));
>        }
>
>
>
>   1.3       +10 -30    logging-log4j/tests/src/java/org/apache/ 
> log4j/rolling/helper/CompressTestCase.java
>
>   Index: CompressTestCase.java
>   ===================================================================
>   RCS file: /home/cvs/logging-log4j/tests/src/java/org/apache/log4j/ 
> rolling/helper/CompressTestCase.java,v
>   retrieving revision 1.2
>   retrieving revision 1.3
>   diff -u -r1.2 -r1.3
>   --- CompressTestCase.java    6 Jan 2005 19:27:03 -0000    1.2
>   +++ CompressTestCase.java    23 May 2005 23:51:01 -0000    1.3
>   @@ -1,5 +1,5 @@
>    /*
>   - * Copyright 1999,2004 The Apache Software Foundation.
>   + * Copyright 1999,2005 The Apache Software Foundation.
>     *
>     * Licensed under the Apache License, Version 2.0 (the "License");
>     * you may not use this file except in compliance with the License.
>   @@ -15,14 +15,10 @@
>     */
>    package org.apache.log4j.rolling.helper;
>
>   -import org.apache.log4j.BasicConfigurator;
>   -import org.apache.log4j.LogManager;
>   -import org.apache.log4j.rolling.helper.Compress;
>    import org.apache.log4j.util.Compare;
>
>   -import junit.framework.Test;
>    import junit.framework.TestCase;
>   -import junit.framework.TestSuite;
>   +import java.io.File;
>
>    /**
>     * @author Ceki
>   @@ -30,8 +26,6 @@
>     */
>    public class CompressTestCase extends TestCase {
>
>   -  Compress compress = new Compress();
>   -
>      /**
>       * Constructor for CompressTestCase.
>       * @param arg0
>   @@ -40,37 +34,23 @@
>        super(arg0);
>      }
>
>   -  public void setUp() {
>   -    BasicConfigurator.configure();;
>   -  }
>   -
>   -  public void tearDown() {
>   -    LogManager.shutdown();
>   -  }
>   -
>      public void test1() throws Exception {
>   -    compress.GZCompress("input/compress1.txt", "output/ 
> compress1.txt.gz");
>   +    GZCompressAction.execute(new File("input/compress1.txt"),  
> new File("output/compress1.txt.gz"), false, null);
>        assertTrue(Compare.gzCompare("output/compress1.txt.gz",
>               "witness/compress1.txt.gz"));
>      }
>
>      public void test2() throws Exception {
>   -     compress.GZCompress("input/compress2.txt", "output/ 
> compress2.txt");
>   +     GZCompressAction.execute(new File("input/compress2.txt"),  
> new File("output/compress2.txt.gz"), false, null);
>         assertTrue(Compare.gzCompare("output/compress2.txt.gz",
>                "witness/compress2.txt.gz"));
>       }
>   -
>   +
>   +  /*  witness file does not exist
>      public void test3() throws Exception {
>   -      compress.ZIPCompress("input/compress3.txt", "output/ 
> compress3.txt");
>   -      //assertTrue(Compare.compare("output/compress3.txt.zip",
>   -        //     "witness/compress3.txt.zip"));
>   -    }
>   -
>   -  public static Test suite() {
>   -      TestSuite suite = new TestSuite();
>   -      suite.addTest(new CompressTestCase("test1"));
>   -      suite.addTest(new CompressTestCase("test2"));
>   -      suite.addTest(new CompressTestCase("test3"));
>   -      return suite;
>   +      ZipCompressAction.execute(new File("input/compress3.txt"),  
> new File("output/compress3.txt.zip"), false, null);
>   +      assertTrue(Compare.compare("output/compress3.txt.zip",
>   +             "witness/compress3.txt.zip"));
>        }
>   +    */
>    }
>
>
>
>   1.4       +5 -1      logging-log4j/tests/src/java/org/apache/ 
> log4j/rolling/helper/FileNamePatternTestCase.java
>
>   Index: FileNamePatternTestCase.java
>   ===================================================================
>   RCS file: /home/cvs/logging-log4j/tests/src/java/org/apache/log4j/ 
> rolling/helper/FileNamePatternTestCase.java,v
>   retrieving revision 1.3
>   retrieving revision 1.4
>   diff -u -r1.3 -r1.4
>   --- FileNamePatternTestCase.java    22 May 2005 07:56:23 -0000     
> 1.3
>   +++ FileNamePatternTestCase.java    23 May 2005 23:51:01 -0000     
> 1.4
>   @@ -21,6 +21,7 @@
>
>    import java.util.Calendar;
>    import org.apache.log4j.ULogger;
>   +import java.util.List;
>
>
>    /**
>   @@ -50,7 +51,10 @@
>            public String getActiveFileName() {
>                return null;
>            }
>   -        public void rollover() {
>   +        public boolean rollover(final StringBuffer activeName,
>   +                                final List synchronousActions,
>   +                                final List asynchronousActions) {
>   +            return false;
>            }
>            public ULogger getLogger() {
>                return null;
>
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
> For additional commands, e-mail: log4j-dev-help@logging.apache.org
>
>
>


---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org


Mime
View raw message