Java Tutorial/File/FilterOutputStream

Материал из Java эксперт
Перейти к: навигация, поиск

Adds extra dot if dot occurs in message body at beginning of line (according to RFC1939)

   <source lang="java">

/****************************************************************

* Licensed to the Apache Software Foundation (ASF) under one   *
* or more contributor license agreements.  See the NOTICE file *
* distributed with this work for additional information        *
* regarding copyright ownership.  The ASF licenses this file   *
* to you 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.                                           *
****************************************************************/

import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; /**

* Adds extra dot if dot occurs in message body at beginning of line (according to RFC1939)
* Compare also org.apache.james.smtpserver.SMTPInputStream
*/

public class ExtraDotOutputStream extends FilterOutputStream {

   /*
   static public void main(String[] args) throws IOException
   {
       String data = ".This is a test\r\nof the thing.\r\nWe should not have much trouble.\r\n.doubled?\r\nor not?\n.doubled\nor not?\r\n\r\n\n\n\r\r\r\n";
       OutputStream os = new ExtraDotOutputStream(System.out);
       os.write(data.getBytes());
   }
   */
   /**
    * Counter for number of last (0A or 0D).
    */
   protected int countLast0A0D;
   /**
    * Constructor that wraps an OutputStream.
    *
    * @param out the OutputStream to be wrapped
    */
   public ExtraDotOutputStream(OutputStream out) {
       super(out);
       countLast0A0D = 2; // we already assume a CRLF at beginning (otherwise TOP would not work correctly !)
   }
   /**
    * Writes a byte to the stream, adding dots where appropriate.
    * Also fixes any naked CR or LF to the RFC 2821 mandated CFLF
    * pairing.
    *
    * @param b the byte to write
    *
    * @throws IOException if an error occurs writing the byte
    */
   public void write(int b) throws IOException {
       switch (b) {
           case ".":
               if (countLast0A0D == 2) {
                   // add extra dot (the first of the pair)
                   out.write(".");
               }
               countLast0A0D = 0;
               break;
           case "\r":
               if (countLast0A0D == 1) out.write("\n"); // two CR in a row, so insert an LF first
               countLast0A0D = 1;
               break;
           case "\n":
               /* RFC 2821 #2.3.7 mandates that line termination is
                * CRLF, and that CR and LF must not be transmitted
                * except in that pairing.  If we get a naked LF,
                * convert to CRLF.
                */
               if (countLast0A0D != 1) out.write("\r");
               countLast0A0D = 2;
               break;
           default:
               // we"re  no longer at the start of a line
               countLast0A0D = 0;
               break;
       }
       out.write(b);
   }
   
   /**
    * Ensure that the stream is CRLF terminated.
    * 
    * @throws IOException  if an error occurs writing the byte
    */
   public void checkCRLFTerminator() throws IOException {
       if (countLast0A0D != 2) {
           write("\n");
       }
   }

} ////////////////////////////// /****************************************************************

* Licensed to the Apache Software Foundation (ASF) under one   *
* or more contributor license agreements.  See the NOTICE file *
* distributed with this work for additional information        *
* regarding copyright ownership.  The ASF licenses this file   *
* to you 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.                                           *
****************************************************************/

import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import junit.framework.TestCase; /**

* Tests for the ExtraDotOutputStream
*/

public class ExtraDotOutputStreamTest extends TestCase {

   public void testMain() throws IOException {
       String data = ".This is a test\r\nof the thing.\r\nWe should not have much trouble.\r\n.doubled?\r\nor not?\n.doubled\nor not?\r\n\r\n\n\n\r\r\r\n";
       ByteArrayOutputStream bOut = new ByteArrayOutputStream();
       OutputStream os = new ExtraDotOutputStream(bOut);
       os.write(data.getBytes());
       os.flush();
       String expected = "..This is a test\r\nof the thing.\r\nWe should not have much trouble.\r\n..doubled?\r\nor not?\r\n..doubled\r\nor not?\r\n\r\n\r\n\r\n\r\n\r\n\r\n";
       assertEquals(expected,bOut.toString());
   }
   /*
    * Test method for "org.apache.james.util.ExtraDotOutputStream.checkCRLFTerminator()"
    */
   public void testCheckCRLFTerminator() throws IOException {
       ByteArrayOutputStream bOut = new ByteArrayOutputStream();
       ExtraDotOutputStream os = new ExtraDotOutputStream(bOut);
       // if the stream is empty then we should not add the CRLF.
       os.checkCRLFTerminator();
       os.flush();
       assertEquals("",bOut.toString());
       os.write("Test".getBytes());
       os.flush();
       assertEquals("Test",bOut.toString());
       os.checkCRLFTerminator();
       os.flush();
       assertEquals("Test\r\n",bOut.toString());
       // if the stream ends with \r we should simply add the \n
       os.write("A line with incomplete ending\r".getBytes());
       os.flush();
       assertEquals("Test\r\nA line with incomplete ending\r",bOut.toString());
       os.checkCRLFTerminator();
       os.flush();
       assertEquals("Test\r\nA line with incomplete ending\r\n",bOut.toString());
       // already correctly terminated, should leave the output untouched
       os.checkCRLFTerminator();
       os.flush();
       assertEquals("Test\r\nA line with incomplete ending\r\n",bOut.toString());
   }

}</source>





Apply a ASCII Hex encoding to the stream

   <source lang="java">

/*

* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/

/* $Id: ASCIIHexOutputStream.java 426584 2006-07-28 16:01:47Z jeremias $ */

import java.io.OutputStream; import java.io.FilterOutputStream; import java.io.IOException; /**

* This class applies a ASCII Hex encoding to the stream.
*
* @version $Id: ASCIIHexOutputStream.java 426584 2006-07-28 16:01:47Z jeremias $
*/

public class ASCIIHexOutputStream extends FilterOutputStream

       {
   private static final int EOL   = 0x0A; //"\n"
   private static final int EOD   = 0x3E; //">"
   private static final int ZERO  = 0x30; //"0"
   private static final int NINE  = 0x39; //"9"
   private static final int A     = 0x41; //"A"
   private static final int ADIFF = A - NINE - 1;
   private int posinline = 0;
   /** @see java.io.FilterOutputStream **/
   public ASCIIHexOutputStream(OutputStream out) {
       super(out);
   }
   /** @see java.io.FilterOutputStream **/
   public void write(int b) throws IOException {
       b &= 0xFF;
       int digit1 = ((b & 0xF0) >> 4) + ZERO;
       if (digit1 > NINE) {
           digit1 += ADIFF;
       }
       out.write(digit1);
       int digit2 = (b & 0x0F) + ZERO;
       if (digit2 > NINE) {
           digit2 += ADIFF;
       }
       out.write(digit2);
       posinline++;
       checkLineWrap();
   }
   private void checkLineWrap() throws IOException {
       //Maximum line length is 80 characters
       if (posinline >= 40) {
           out.write(EOL);
           posinline = 0;
       }
   }
   /** @see Finalizable **/
   public void finalizeStream() throws IOException {
       checkLineWrap();
       //Write closing character ">"
       super.write(EOD);
       flush();
  
   }
   /** @see java.io.FilterOutputStream **/
   public void close() throws IOException {
       finalizeStream();
       super.close();
   }

}</source>





Capture System.out into a JFrame

   <source lang="java">

import java.io.ByteArrayOutputStream; import java.io.FileWriter; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextArea; public class Main extends JFrame {

 JTextArea aTextArea = new JTextArea();
 PrintStream aPrintStream = new PrintStream(new FilteredStream(new ByteArrayOutputStream()));
 public Main() {
   setSize(300, 300);
   add("Center", new JScrollPane(aTextArea));
   setVisible(true);
   System.setOut(aPrintStream); // catches System.out messages
   System.setErr(aPrintStream); // catches error messages
 }
 class FilteredStream extends FilterOutputStream {
   public FilteredStream(OutputStream aStream) {
     super(aStream);
   }
   public void write(byte b[]) throws IOException {
     String aString = new String(b);
     aTextArea.append(aString);
   }
   public void write(byte b[], int off, int len) throws IOException {
     String aString = new String(b, off, len);
     aTextArea.append(aString);
     FileWriter aWriter = new FileWriter("a.log", true);
     aWriter.write(aString);
     aWriter.close();
   }
 }

}</source>





Count the number of bytes written to the output stream.

   <source lang="java">

/*

* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/

//Revised from Apache cocoon import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; /**

* This class is like the {@link java.io.BufferedOutputStream} but it
* extends it with a logic to count the number of bytes written to
* the output stream.
* 
* @version $Id: BufferedOutputStream.java 587751 2007-10-24 02:41:36Z vgritsenko $
* @since   2.1
*/

public final class BufferedOutputStream extends FilterOutputStream {

   protected byte buf[];
   protected int count;
   
   /**
    * Creates a new buffered output stream to write data to the 
    * specified underlying output stream with a default 8192-byte
    * buffer size.
    *
    * @param   out   the underlying output stream.
    */
   public BufferedOutputStream(OutputStream out) {
       this(out, 8192);
   }
   /**
    * Creates a new buffered output stream to write data to the 
    * specified underlying output stream with the specified buffer 
    * size. 
    *
    * @param   out    the underlying output stream.
    * @param   size   the buffer size.
    * @exception IllegalArgumentException if size <= 0.
    */
   public BufferedOutputStream(OutputStream out, int size) {
       super(out);
       if (size <= 0) {
           throw new IllegalArgumentException("Buffer size <= 0");
       }
       this.buf = new byte[size];
   }
   /**
    * Writes the specified byte to this buffered output stream. 
    *
    * @param      b   the byte to be written.
    * @exception  IOException  if an I/O error occurs.
    */
   public void write(int b) throws IOException {
       if (this.count >= this.buf.length) {
           this.incBuffer();
       }
       this.buf[count++] = (byte)b;
   }
   /**
    * Writes len bytes from the specified byte array 
    * starting at offset off to this buffered output stream.
    *
    *  Ordinarily this method stores bytes from the given array into this
    * stream"s buffer, flushing the buffer to the underlying output stream as
    * needed.  If the requested length is at least as large as this stream"s
    * buffer, however, then this method will flush the buffer and write the
    * bytes directly to the underlying output stream.  Thus redundant
    * BufferedOutputStreams will not copy data unnecessarily.
    *
    * @param      b     the data.
    * @param      off   the start offset in the data.
    * @param      len   the number of bytes to write.
    * @exception  IOException  if an I/O error occurs.
    */
   public void write(byte b[], int off, int len) throws IOException {
       while (len > buf.length - count) {
           this.incBuffer();
       }
       System.arraycopy(b, off, buf, count, len);
       count += len;
   }
   /**
    * Flushes this buffered output stream. 
    * We don"t flush here, flushing is done during closing.
    *
    * @exception  IOException  if an I/O error occurs.
    */
   public void flush() throws IOException {
       // nothing
   }
   /**
    * Closes this buffered output stream.
    * Flush before closing.
    *
    * @exception  IOException  if an I/O error occurs.
    */
   public void close() throws IOException {
       realFlush();
       super.close ();
   }
   /**
    * Flushes this buffered output stream. 
    */
   public void realFlush() throws IOException {
       this.writeBuffer();
       this.out.flush();
   }
   
   /**
    * Write the buffer
    */
   private void writeBuffer() 
   throws IOException {
       if (this.count > 0) {
           this.out.write(this.buf, 0, this.count);
           this.clearBuffer();
       }
   }
   /**
    * Increment the buffer
    */
   private void incBuffer() {
       // currently we double the buffer size
       // this is not so fast but is a very simple logic
       byte[] newBuf = new byte[this.buf.length * 2];
       System.arraycopy(this.buf, 0, newBuf, 0, this.buf.length);
       this.buf = newBuf;
   }
   
   /**
    * Clear/reset the buffer
    */
   public void clearBuffer() {
       this.count = 0;
   }
   /**
    * Return the size of the current buffer
    */
   public int getCount() {
       return this.count;
   }

}</source>





extends FilterOutputStream

   <source lang="java">

import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class MainClass {

 public static void main(String[] args) throws IOException {
   if (args.length != 3) {
     System.out.println("Usage: java TeeCopier infile outfile1 outfile2");
     return;
   }
   FileInputStream fin = new FileInputStream(args[0]);
   FileOutputStream fout1 = new FileOutputStream(args[1]);
   FileOutputStream fout2 = new FileOutputStream(args[2]);
   MyOutputStream tout = new MyOutputStream(fout1, fout2);
   copy(fin, tout);
   fin.close();
   tout.close();
 }
 public static void copy(InputStream in, OutputStream out) throws IOException {
   byte[] buffer = new byte[1024];
   while (true) {
     int bytesRead = in.read(buffer);
     if (bytesRead == -1)
       break;
     out.write(buffer, 0, bytesRead);
   }
 }

} class MyOutputStream extends FilterOutputStream {

 private OutputStream out1;
 private OutputStream out2;
 public MyOutputStream(OutputStream stream1, OutputStream stream2) {
   super(stream1);
   out1 = stream1;
   out2 = stream2;
 }
 public void write(int b) throws IOException {
   out1.write(b);
   out2.write(b);
 }
 public void write(byte[] data, int offset, int length) throws IOException {
   out1.write(data, offset, length);
   out2.write(data, offset, length);
 }
 public void flush() throws IOException {
   out1.flush();
   out2.flush();
 }
 public void close() throws IOException {
   out1.close();
   out2.close();
 }

}</source>





extends FilterOutputStream for printable characters

   <source lang="java">

import java.io.FileInputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class MainClass {

 public static void main(String[] args) throws Exception {
   InputStream in = new FileInputStream("test.txt");
   OutputStream out = System.out;
   PrintableOutputStream pout = new PrintableOutputStream(out);
   for (int c = in.read(); c != -1; c = in.read()) {
     pout.write(c);
   }
   out.close();
 }

} class PrintableOutputStream extends FilterOutputStream {

 public PrintableOutputStream(OutputStream out) {
   super(out);
 }
 public void write(int b) throws IOException {
   // carriage return, linefeed, and tab
   if (b == "\n" || b == "\r" || b == "\t")
     out.write(b);
   // non-printing characters
   else if (b < 32 || b > 126)
     out.write("?");
   // printing, ASCII characters
   else
     out.write(b);
 }
 public void write(byte[] data, int offset, int length) throws IOException {
   for (int i = offset; i < offset + length; i++) {
     this.write(data[i]);
   }
 }

}</source>





Provide a debug trace of the stuff thats being written out into the DataOutputStream

   <source lang="java">

/*

* The contents of this file are subject to the terms 
* of the Common Development and Distribution License 
* (the "License").  You may not use this file except 
* in compliance with the License.
* 
* You can obtain a copy of the license at 
* glassfish/bootstrap/legal/CDDLv1.0.txt or 
* https://glassfish.dev.java.net/public/CDDLv1.0.html. 
* See the License for the specific language governing 
* permissions and limitations under the License.
* 
* When distributing Covered Code, include this CDDL 
* HEADER in each file and include the License file at 
* glassfish/bootstrap/legal/CDDLv1.0.txt.  If applicable, 
* add the following below this CDDL HEADER, with the 
* fields enclosed by brackets "[]" replaced with your 
* own identifying information: Portions Copyright [yyyy] 
* [name of copyright owner]
*/

/*

* @(#)TraceOutputStream.java 1.5 05/08/29
*
* Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
*/

// revised from sun mail util import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; /**

* This class is a subclass of DataOutputStream that copies the
* data being written into the DataOutputStream into another output
* stream. This class is used here to provide a debug trace of the
* stuff thats being written out into the DataOutputStream.
*
* @author John Mani
*/

public class TraceOutputStream extends FilterOutputStream {

   private boolean trace = false;
   private boolean quote = false;
   private OutputStream traceOut;
   /**
    * Creates an output stream filter built on top of the specified
    * underlying output stream.
    *
    * @param   out   the underlying output stream.
    * @param traceOut  the trace stream.
    */
   public TraceOutputStream(OutputStream out, OutputStream traceOut) {
 super(out);
 this.traceOut = traceOut;
   }
   /**
    * Set the trace mode.
    */
   public void setTrace(boolean trace) {
 this.trace = trace;
   }
   /**
    * Set quote mode.
    * @param quote the quote mode
    */
   public void setQuote(boolean quote) {
 this.quote = quote;
   }
   /**
    * Writes the specified byte to this output stream.
    * Writes out the byte into the trace stream if the trace mode
    * is true
    */
   public void write(int b) throws IOException {
 if (trace) {
     if (quote)
   writeByte(b);
     else
   traceOut.write(b);
 }
 out.write(b);
   }
     
   /**
    * Writes b.length bytes to this output stream.
    * Writes out the bytes into the trace stream if the trace
    * mode is true
    */
   public void write(byte b[], int off, int len) throws IOException {
 if (trace) {
     if (quote) {
   for (int i = 0; i < len; i++)
       writeByte(b[off + i]);
     } else
   traceOut.write(b, off, len);
 }
 out.write(b, off, len);
   }
   /**
    * Write a byte in a way that every byte value is printable ASCII.
    */
   private final void writeByte(int b) throws IOException {
 b &= 0xff;
 if (b > 0x7f) {
     traceOut.write("M");
     traceOut.write("-");
     b &= 0x7f;
 }
 if (b == "\r") {
     traceOut.write("\\");
     traceOut.write("r");
 } else if (b == "\n") {
     traceOut.write("\\");
     traceOut.write("n");
     traceOut.write("\n");
 } else if (b == "\t") {
     traceOut.write("\\");
     traceOut.write("t");
 } else if (b < " ") {
     traceOut.write("^");
     traceOut.write("@" + b);
 } else {
     traceOut.write(b);
 }
   }

}</source>





Rollover FileOutputStream

   <source lang="java">

//Copyright 2006 Mort Bay Consulting Pty. Ltd. //------------------------------------------------------------------------ //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. import java.io.File; import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.StringTokenizer; import java.util.TimeZone; import java.util.Timer; import java.util.TimerTask; /**

* RolloverFileOutputStream
* 
* @author Greg Wilkins
*/

public class RolloverFileOutputStream extends FilterOutputStream {

 private static Timer __rollover;
 final static String YYYY_MM_DD = "yyyy_mm_dd";
 private RollTask _rollTask;
 private SimpleDateFormat _fileBackupFormat = new SimpleDateFormat(System.getProperty(
     "ROLLOVERFILE_BACKUP_FORMAT", "HHmmssSSS"));
 private SimpleDateFormat _fileDateFormat = new SimpleDateFormat(System.getProperty(
     "ROLLOVERFILE_DATE_FORMAT", "yyyy_MM_dd"));
 private String _filename;
 private File _file;
 private boolean _append;
 private int _retainDays;
 /* ------------------------------------------------------------ */
 public RolloverFileOutputStream(String filename) throws IOException {
   this(filename, true, Integer.getInteger("ROLLOVERFILE_RETAIN_DAYS", 31).intValue());
 }
 /* ------------------------------------------------------------ */
 public RolloverFileOutputStream(String filename, boolean append) throws IOException {
   this(filename, append, Integer.getInteger("ROLLOVERFILE_RETAIN_DAYS", 31).intValue());
 }
 /* ------------------------------------------------------------ */
 public RolloverFileOutputStream(String filename, boolean append, int retainDays)
     throws IOException {
   this(filename, append, retainDays, TimeZone.getDefault());
 }
 /* ------------------------------------------------------------ */
 public RolloverFileOutputStream(String filename, boolean append, int retainDays, TimeZone zone)
     throws IOException {
   super(null);
   _fileBackupFormat.setTimeZone(zone);
   _fileDateFormat.setTimeZone(zone);
   if (filename != null) {
     filename = filename.trim();
     if (filename.length() == 0)
       filename = null;
   }
   if (filename == null)
     throw new IllegalArgumentException("Invalid filename");
   _filename = filename;
   _append = append;
   _retainDays = retainDays;
   setFile();
   synchronized (RolloverFileOutputStream.class) {
     if (__rollover == null)
       __rollover = new Timer();
     _rollTask = new RollTask();
     Calendar now = Calendar.getInstance();
     now.setTimeZone(zone);
     GregorianCalendar midnight = new GregorianCalendar(now.get(Calendar.YEAR), now
         .get(Calendar.MONTH), now.get(Calendar.DAY_OF_MONTH), 23, 0);
     midnight.setTimeZone(zone);
     midnight.add(Calendar.HOUR, 1);
     __rollover.scheduleAtFixedRate(_rollTask, midnight.getTime(), 1000L * 60 * 60 * 24);
   }
 }
 /* ------------------------------------------------------------ */
 public String getFilename() {
   return _filename;
 }
 /* ------------------------------------------------------------ */
 public String getDatedFilename() {
   if (_file == null)
     return null;
   return _file.toString();
 }
 /* ------------------------------------------------------------ */
 public int getRetainDays() {
   return _retainDays;
 }
 /* ------------------------------------------------------------ */
 private synchronized void setFile() throws IOException {
   // Check directory
   File file = new File(_filename);
   _filename = file.getCanonicalPath();
   file = new File(_filename);
   File dir = new File(file.getParent());
   if (!dir.isDirectory() || !dir.canWrite())
     throw new IOException("Cannot write log directory " + dir);
   Date now = new Date();
   // Is this a rollover file?
   String filename = file.getName();
   int i = filename.toLowerCase().indexOf(YYYY_MM_DD);
   if (i >= 0) {
     file = new File(dir, filename.substring(0, i) + _fileDateFormat.format(now)
         + filename.substring(i + YYYY_MM_DD.length()));
   }
   if (file.exists() && !file.canWrite())
     throw new IOException("Cannot write log file " + file);
   // Do we need to change the output stream?
   if (out == null || !file.equals(_file)) {
     // Yep
     _file = file;
     if (!_append && file.exists())
       file.renameTo(new File(file.toString() + "." + _fileBackupFormat.format(now)));
     OutputStream oldOut = out;
     out = new FileOutputStream(file.toString(), _append);
     if (oldOut != null)
       oldOut.close();
     // if(log.isDebugEnabled())log.debug("Opened "+_file);
   }
 }
 /* ------------------------------------------------------------ */
 private void removeOldFiles() {
   if (_retainDays > 0) {
     Calendar retainDate = Calendar.getInstance();
     retainDate.add(Calendar.DATE, -_retainDays);
     int borderYear = retainDate.get(java.util.Calendar.YEAR);
     int borderMonth = retainDate.get(java.util.Calendar.MONTH) + 1;
     int borderDay = retainDate.get(java.util.Calendar.DAY_OF_MONTH);
     File file = new File(_filename);
     File dir = new File(file.getParent());
     String fn = file.getName();
     int s = fn.toLowerCase().indexOf(YYYY_MM_DD);
     if (s < 0)
       return;
     String prefix = fn.substring(0, s);
     String suffix = fn.substring(s + YYYY_MM_DD.length());
     String[] logList = dir.list();
     for (int i = 0; i < logList.length; i++) {
       fn = logList[i];
       if (fn.startsWith(prefix) && fn.indexOf(suffix, prefix.length()) >= 0) {
         try {
           StringTokenizer st = new StringTokenizer(fn.substring(prefix.length(), prefix.length()
               + YYYY_MM_DD.length()), "_.");
           int nYear = Integer.parseInt(st.nextToken());
           int nMonth = Integer.parseInt(st.nextToken());
           int nDay = Integer.parseInt(st.nextToken());
           if (nYear < borderYear || (nYear == borderYear && nMonth < borderMonth)
               || (nYear == borderYear && nMonth == borderMonth && nDay <= borderDay)) {
             // log.info("Log age "+fn);
             new File(dir, fn).delete();
           }
         } catch (Exception e) {
           // if (log.isDebugEnabled())
           e.printStackTrace();
         }
       }
     }
   }
 }
 /* ------------------------------------------------------------ */
 public void write(byte[] buf) throws IOException {
   out.write(buf);
 }
 /* ------------------------------------------------------------ */
 public void write(byte[] buf, int off, int len) throws IOException {
   out.write(buf, off, len);
 }
 /* ------------------------------------------------------------ */
 /** 
  */
 public void close() throws IOException {
   synchronized (RolloverFileOutputStream.class) {
     try {
       super.close();
     } finally {
       out = null;
       _file = null;
     }
     _rollTask.cancel();
   }
 }
 /* ------------------------------------------------------------ */
 /* ------------------------------------------------------------ */
 /* ------------------------------------------------------------ */
 private class RollTask extends TimerTask {
   public void run() {
     try {
       RolloverFileOutputStream.this.setFile();
       RolloverFileOutputStream.this.removeOldFiles();
     } catch (IOException e) {
       e.printStackTrace();
     }
   }
 }

}</source>