// ======================================================================== // $Id: RolloverFileOutputStream.java,v 1.14 2005/08/13 00:01:28 gregwilkins Exp $ // Copyright 1997-2004 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. // ======================================================================== package net.lightbody.bmp.proxy.jetty.util; import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; import java.io.*; import java.lang.ref.WeakReference; import java.text.SimpleDateFormat; import java.util.*; /* ------------------------------------------------------------ */ /** A File OutputStream that rolls overs. * If the passed filename contains the string "yyyy_mm_dd" on daily intervals. * @version $Id: RolloverFileOutputStream.java,v 1.14 2005/08/13 00:01:28 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ public class RolloverFileOutputStream extends FilterOutputStream { static Log log = LogFactory.getLog(RolloverFileOutputStream.class); static Rollover __rollover; final static String YYYY_MM_DD="yyyy_mm_dd"; final static ArrayList __rollovers = new ArrayList(); 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; private WeakReference _ref; /* ------------------------------------------------------------ */ 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 { super(null); 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; _ref=new WeakReference(this); setFile(); synchronized(__rollovers) { if (__rollover==null) { __rollover=new Rollover(); __rollover.start(); } __rollovers.add(_ref); } } /* ------------------------------------------------------------ */ 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(__rollovers) { __rollovers.remove(_ref); _ref=null; try{super.close();} finally { out=null; _file=null; } // this will kill the thread when the last stream is removed. if ( __rollovers.size() == 0 ) { __rollover.timeToStop(); __rollover.interrupt(); __rollover = null; } } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ private class Rollover extends Thread { private boolean timeToStop = false; Rollover() { setName("Rollover"); setDaemon(true); } synchronized void timeToStop() { timeToStop = true; } public void run() { while(!timeToStop) { try { // Sleep until midnight Calendar now = Calendar.getInstance(); GregorianCalendar midnight = new GregorianCalendar(now.get(Calendar.YEAR), now.get(Calendar.MONTH), now.get(Calendar.DAY_OF_MONTH), 23,0); midnight.add(Calendar.HOUR,1); long sleeptime= midnight.getTime().getTime()- now.getTime().getTime(); if(log.isDebugEnabled())log.debug("Rollover sleep until "+midnight.getTime()); Thread.sleep(sleeptime); } catch(InterruptedException e) { if (!timeToStop) e.printStackTrace(); } synchronized(__rollovers) { ListIterator iter = __rollovers.listIterator(); while (iter.hasNext()) { WeakReference ref = (WeakReference)iter.next(); RolloverFileOutputStream rfos = (RolloverFileOutputStream)ref.get(); if (rfos==null) iter.remove(); else { try { rfos.setFile(); rfos.removeOldFiles(); } catch(IOException e) { if (!timeToStop) e.printStackTrace(); } } } } } } } }