1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.mortbay.util;
16
17 import java.io.File;
18 import java.io.FileOutputStream;
19 import java.io.FilterOutputStream;
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.lang.ref.WeakReference;
23 import java.text.SimpleDateFormat;
24 import java.util.ArrayList;
25 import java.util.Calendar;
26 import java.util.Date;
27 import java.util.GregorianCalendar;
28 import java.util.ListIterator;
29 import java.util.StringTokenizer;
30 import java.util.TimeZone;
31 import java.util.Timer;
32 import java.util.TimerTask;
33
34
35
36
37
38
39
40
41
42
43
44
45 public class RolloverFileOutputStream extends FilterOutputStream
46 {
47 private static Timer __rollover;
48
49 final static String YYYY_MM_DD="yyyy_mm_dd";
50
51 private RollTask _rollTask;
52 private SimpleDateFormat _fileBackupFormat;
53 private SimpleDateFormat _fileDateFormat;
54
55 private String _filename;
56 private File _file;
57 private boolean _append;
58 private int _retainDays;
59
60
61
62
63
64
65
66 public RolloverFileOutputStream(String filename)
67 throws IOException
68 {
69 this(filename,true,Integer.getInteger("ROLLOVERFILE_RETAIN_DAYS",31).intValue());
70 }
71
72
73
74
75
76
77
78
79 public RolloverFileOutputStream(String filename, boolean append)
80 throws IOException
81 {
82 this(filename,append,Integer.getInteger("ROLLOVERFILE_RETAIN_DAYS",31).intValue());
83 }
84
85
86
87
88
89
90
91
92
93 public RolloverFileOutputStream(String filename,
94 boolean append,
95 int retainDays)
96 throws IOException
97 {
98 this(filename,append,retainDays,TimeZone.getDefault());
99 }
100
101
102
103
104
105
106
107
108
109 public RolloverFileOutputStream(String filename,
110 boolean append,
111 int retainDays,
112 TimeZone zone)
113 throws IOException
114 {
115
116 this(filename,append,retainDays,zone,null,null);
117 }
118
119
120
121
122
123
124
125
126
127
128
129
130
131 public RolloverFileOutputStream(String filename,
132 boolean append,
133 int retainDays,
134 TimeZone zone,
135 String dateFormat,
136 String backupFormat)
137 throws IOException
138 {
139 super(null);
140
141 if (dateFormat==null)
142 dateFormat=System.getProperty("ROLLOVERFILE_DATE_FORMAT","yyyy_MM_dd");
143 _fileDateFormat = new SimpleDateFormat(dateFormat);
144
145 if (backupFormat==null)
146 backupFormat=System.getProperty("ROLLOVERFILE_BACKUP_FORMAT","HHmmssSSS");
147 _fileBackupFormat = new SimpleDateFormat(backupFormat);
148
149 _fileBackupFormat.setTimeZone(zone);
150 _fileDateFormat.setTimeZone(zone);
151
152 if (filename!=null)
153 {
154 filename=filename.trim();
155 if (filename.length()==0)
156 filename=null;
157 }
158 if (filename==null)
159 throw new IllegalArgumentException("Invalid filename");
160
161 _filename=filename;
162 _append=append;
163 _retainDays=retainDays;
164 setFile();
165
166 synchronized(RolloverFileOutputStream.class)
167 {
168 if (__rollover==null)
169 __rollover=new Timer(true);
170
171 _rollTask=new RollTask();
172
173 Calendar now = Calendar.getInstance();
174 now.setTimeZone(zone);
175
176 GregorianCalendar midnight =
177 new GregorianCalendar(now.get(Calendar.YEAR),
178 now.get(Calendar.MONTH),
179 now.get(Calendar.DAY_OF_MONTH),
180 23,0);
181 midnight.setTimeZone(zone);
182 midnight.add(Calendar.HOUR,1);
183 __rollover.scheduleAtFixedRate(_rollTask,midnight.getTime(),1000L*60*60*24);
184 }
185 }
186
187
188 public String getFilename()
189 {
190 return _filename;
191 }
192
193
194 public String getDatedFilename()
195 {
196 if (_file==null)
197 return null;
198 return _file.toString();
199 }
200
201
202 public int getRetainDays()
203 {
204 return _retainDays;
205 }
206
207
208 private synchronized void setFile()
209 throws IOException
210 {
211
212 File file = new File(_filename);
213 _filename=file.getCanonicalPath();
214 file=new File(_filename);
215 File dir= new File(file.getParent());
216 if (!dir.isDirectory() || !dir.canWrite())
217 throw new IOException("Cannot write log directory "+dir);
218
219 Date now=new Date();
220
221
222 String filename=file.getName();
223 int i=filename.toLowerCase().indexOf(YYYY_MM_DD);
224 if (i>=0)
225 {
226 file=new File(dir,
227 filename.substring(0,i)+
228 _fileDateFormat.format(now)+
229 filename.substring(i+YYYY_MM_DD.length()));
230 }
231
232 if (file.exists()&&!file.canWrite())
233 throw new IOException("Cannot write log file "+file);
234
235
236 if (out==null || !file.equals(_file))
237 {
238
239 _file=file;
240 if (!_append && file.exists())
241 file.renameTo(new File(file.toString()+"."+_fileBackupFormat.format(now)));
242 OutputStream oldOut=out;
243 out=new FileOutputStream(file.toString(),_append);
244 if (oldOut!=null)
245 oldOut.close();
246
247 }
248 }
249
250
251 private void removeOldFiles()
252 {
253 if (_retainDays>0)
254 {
255 long now = System.currentTimeMillis();
256
257 File file= new File(_filename);
258 File dir = new File(file.getParent());
259 String fn=file.getName();
260 int s=fn.toLowerCase().indexOf(YYYY_MM_DD);
261 if (s<0)
262 return;
263 String prefix=fn.substring(0,s);
264 String suffix=fn.substring(s+YYYY_MM_DD.length());
265
266 String[] logList=dir.list();
267 for (int i=0;i<logList.length;i++)
268 {
269 fn = logList[i];
270 if(fn.startsWith(prefix)&&fn.indexOf(suffix,prefix.length())>=0)
271 {
272 File f = new File(dir,fn);
273 long date = f.lastModified();
274 if ( ((now-date)/(1000*60*60*24))>_retainDays)
275 f.delete();
276 }
277 }
278 }
279 }
280
281
282 public void write (byte[] buf)
283 throws IOException
284 {
285 out.write (buf);
286 }
287
288
289 public void write (byte[] buf, int off, int len)
290 throws IOException
291 {
292 out.write (buf, off, len);
293 }
294
295
296
297
298 public void close()
299 throws IOException
300 {
301 synchronized(RolloverFileOutputStream.class)
302 {
303 try{super.close();}
304 finally
305 {
306 out=null;
307 _file=null;
308 }
309
310 _rollTask.cancel();
311 }
312 }
313
314
315
316
317 private class RollTask extends TimerTask
318 {
319 public void run()
320 {
321 try
322 {
323 RolloverFileOutputStream.this.setFile();
324 RolloverFileOutputStream.this.removeOldFiles();
325
326 }
327 catch(IOException e)
328 {
329 e.printStackTrace();
330 }
331 }
332 }
333 }