Rollover FileOutputStream : FilterOutputStream « File « Java Tutorial






//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();
      }
    }
  }
}








11.21.FilterOutputStream
11.21.1.extends FilterOutputStream for printable characters
11.21.2.Capture System.out into a JFrame
11.21.3.extends FilterOutputStream
11.21.4.Count the number of bytes written to the output stream.
11.21.5.Rollover FileOutputStream
11.21.6.Provide a debug trace of the stuff thats being written out into the DataOutputStream
11.21.7.Adds extra dot if dot occurs in message body at beginning of line (according to RFC1939)
11.21.8.Apply a ASCII Hex encoding to the stream