001: //========================================================================
002: //Copyright 2006 Mort Bay Consulting Pty. Ltd.
003: //------------------------------------------------------------------------
004: //Licensed under the Apache License, Version 2.0 (the "License");
005: //you may not use this file except in compliance with the License.
006: //You may obtain a copy of the License at
007: //http://www.apache.org/licenses/LICENSE-2.0
008: //Unless required by applicable law or agreed to in writing, software
009: //distributed under the License is distributed on an "AS IS" BASIS,
010: //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: //See the License for the specific language governing permissions and
012: //limitations under the License.
013: //========================================================================
014:
015: package org.mortbay.util;
016:
017: import java.io.File;
018: import java.io.FileOutputStream;
019: import java.io.FilterOutputStream;
020: import java.io.IOException;
021: import java.io.OutputStream;
022: import java.lang.ref.WeakReference;
023: import java.text.SimpleDateFormat;
024: import java.util.ArrayList;
025: import java.util.Calendar;
026: import java.util.Date;
027: import java.util.GregorianCalendar;
028: import java.util.ListIterator;
029: import java.util.StringTokenizer;
030:
031: /**
032: * RolloverFileOutputStream
033: * @author Greg Wilkins
034: */
035: public class RolloverFileOutputStream extends FilterOutputStream {
036:
037: static Rollover __rollover;
038: final static String YYYY_MM_DD = "yyyy_mm_dd";
039: final static ArrayList __rollovers = new ArrayList();
040:
041: private SimpleDateFormat _fileBackupFormat = new SimpleDateFormat(
042: System.getProperty("ROLLOVERFILE_BACKUP_FORMAT",
043: "HHmmssSSS"));
044: private SimpleDateFormat _fileDateFormat = new SimpleDateFormat(
045: System
046: .getProperty("ROLLOVERFILE_DATE_FORMAT",
047: "yyyy_MM_dd"));
048:
049: private String _filename;
050: private File _file;
051: private boolean _append;
052: private int _retainDays;
053: private WeakReference _ref;
054:
055: /* ------------------------------------------------------------ */
056: public RolloverFileOutputStream(String filename) throws IOException {
057: this (filename, true, Integer.getInteger(
058: "ROLLOVERFILE_RETAIN_DAYS", 31).intValue());
059: }
060:
061: /* ------------------------------------------------------------ */
062: public RolloverFileOutputStream(String filename, boolean append)
063: throws IOException {
064: this (filename, append, Integer.getInteger(
065: "ROLLOVERFILE_RETAIN_DAYS", 31).intValue());
066: }
067:
068: /* ------------------------------------------------------------ */
069: public RolloverFileOutputStream(String filename, boolean append,
070: int retainDays) throws IOException {
071: super (null);
072:
073: if (filename != null) {
074: filename = filename.trim();
075: if (filename.length() == 0)
076: filename = null;
077: }
078: if (filename == null)
079: throw new IllegalArgumentException("Invalid filename");
080:
081: _filename = filename;
082: _append = append;
083: _retainDays = retainDays;
084: _ref = new WeakReference(this );
085: setFile();
086:
087: synchronized (__rollovers) {
088: if (__rollover == null) {
089: __rollover = new Rollover();
090: __rollover.start();
091: }
092: __rollovers.add(_ref);
093: }
094: }
095:
096: /* ------------------------------------------------------------ */
097: public String getFilename() {
098: return _filename;
099: }
100:
101: /* ------------------------------------------------------------ */
102: public String getDatedFilename() {
103: if (_file == null)
104: return null;
105: return _file.toString();
106: }
107:
108: /* ------------------------------------------------------------ */
109: public int getRetainDays() {
110: return _retainDays;
111: }
112:
113: /* ------------------------------------------------------------ */
114: private synchronized void setFile() throws IOException {
115: // Check directory
116: File file = new File(_filename);
117: _filename = file.getCanonicalPath();
118: file = new File(_filename);
119: File dir = new File(file.getParent());
120: if (!dir.isDirectory() || !dir.canWrite())
121: throw new IOException("Cannot write log directory " + dir);
122:
123: Date now = new Date();
124:
125: // Is this a rollover file?
126: String filename = file.getName();
127: int i = filename.toLowerCase().indexOf(YYYY_MM_DD);
128: if (i >= 0) {
129: file = new File(dir, filename.substring(0, i)
130: + _fileDateFormat.format(now)
131: + filename.substring(i + YYYY_MM_DD.length()));
132: }
133:
134: if (file.exists() && !file.canWrite())
135: throw new IOException("Cannot write log file " + file);
136:
137: // Do we need to change the output stream?
138: if (out == null || !file.equals(_file)) {
139: // Yep
140: _file = file;
141: if (!_append && file.exists())
142: file.renameTo(new File(file.toString() + "."
143: + _fileBackupFormat.format(now)));
144: OutputStream oldOut = out;
145: out = new FileOutputStream(file.toString(), _append);
146: if (oldOut != null)
147: oldOut.close();
148: //if(log.isDebugEnabled())log.debug("Opened "+_file);
149: }
150: }
151:
152: /* ------------------------------------------------------------ */
153: private void removeOldFiles() {
154: if (_retainDays > 0) {
155: Calendar retainDate = Calendar.getInstance();
156: retainDate.add(Calendar.DATE, -_retainDays);
157: int borderYear = retainDate.get(java.util.Calendar.YEAR);
158: int borderMonth = retainDate.get(java.util.Calendar.MONTH) + 1;
159: int borderDay = retainDate
160: .get(java.util.Calendar.DAY_OF_MONTH);
161:
162: File file = new File(_filename);
163: File dir = new File(file.getParent());
164: String fn = file.getName();
165: int s = fn.toLowerCase().indexOf(YYYY_MM_DD);
166: if (s < 0)
167: return;
168: String prefix = fn.substring(0, s);
169: String suffix = fn.substring(s + YYYY_MM_DD.length());
170:
171: String[] logList = dir.list();
172: for (int i = 0; i < logList.length; i++) {
173: fn = logList[i];
174: if (fn.startsWith(prefix)
175: && fn.indexOf(suffix, prefix.length()) >= 0) {
176: try {
177: StringTokenizer st = new StringTokenizer(fn
178: .substring(prefix.length(), prefix
179: .length()
180: + YYYY_MM_DD.length()), "_.");
181: int nYear = Integer.parseInt(st.nextToken());
182: int nMonth = Integer.parseInt(st.nextToken());
183: int nDay = Integer.parseInt(st.nextToken());
184:
185: if (nYear < borderYear
186: || (nYear == borderYear && nMonth < borderMonth)
187: || (nYear == borderYear
188: && nMonth == borderMonth && nDay <= borderDay)) {
189: //log.info("Log age "+fn);
190: new File(dir, fn).delete();
191: }
192: } catch (Exception e) {
193: //if (log.isDebugEnabled())
194: e.printStackTrace();
195: }
196: }
197: }
198: }
199: }
200:
201: /* ------------------------------------------------------------ */
202: public void write(byte[] buf) throws IOException {
203: out.write(buf);
204: }
205:
206: /* ------------------------------------------------------------ */
207: public void write(byte[] buf, int off, int len) throws IOException {
208: out.write(buf, off, len);
209: }
210:
211: /* ------------------------------------------------------------ */
212: /**
213: */
214: public void close() throws IOException {
215: synchronized (__rollovers) {
216: __rollovers.remove(_ref);
217: _ref = null;
218: try {
219: super .close();
220: } finally {
221: out = null;
222: _file = null;
223: }
224:
225: // this will kill the thread when the last stream is removed.
226: if (__rollovers.size() == 0) {
227: __rollover.timeToStop();
228: __rollover.interrupt();
229: __rollover = null;
230: }
231: }
232: }
233:
234: /* ------------------------------------------------------------ */
235: /* ------------------------------------------------------------ */
236: /* ------------------------------------------------------------ */
237: private class Rollover extends Thread {
238: private boolean timeToStop = false;
239:
240: Rollover() {
241: setName("Rollover");
242: setDaemon(true);
243: }
244:
245: synchronized void timeToStop() {
246: timeToStop = true;
247: }
248:
249: public void run() {
250: while (!timeToStop) {
251: try {
252: // Sleep until midnight
253: Calendar now = Calendar.getInstance();
254: GregorianCalendar midnight = new GregorianCalendar(
255: now.get(Calendar.YEAR), now
256: .get(Calendar.MONTH), now
257: .get(Calendar.DAY_OF_MONTH), 23, 0);
258: midnight.add(Calendar.HOUR, 1);
259: long sleeptime = midnight.getTime().getTime()
260: - now.getTime().getTime();
261: //if(log.isDebugEnabled())log.debug("Rollover sleep until "+midnight.getTime());
262: Thread.sleep(sleeptime);
263: } catch (InterruptedException e) {
264: if (!timeToStop)
265: e.printStackTrace();
266: }
267:
268: synchronized (__rollovers) {
269: ListIterator iter = __rollovers.listIterator();
270: while (iter.hasNext()) {
271: WeakReference ref = (WeakReference) iter.next();
272: RolloverFileOutputStream rfos = (RolloverFileOutputStream) ref
273: .get();
274:
275: if (rfos == null)
276: iter.remove();
277: else {
278: try {
279: rfos.setFile();
280: rfos.removeOldFiles();
281: } catch (IOException e) {
282: if (!timeToStop)
283: e.printStackTrace();
284: }
285: }
286: }
287: }
288: }
289: }
290: }
291: }
|