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