]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/util/RolloverFileOutputStream.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / util / RolloverFileOutputStream.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18
19 package org.eclipse.jetty.util; 
20
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.FilterOutputStream;
24 import java.io.IOException;
25 import java.io.OutputStream;
26 import java.text.SimpleDateFormat;
27 import java.util.Calendar;
28 import java.util.Date;
29 import java.util.GregorianCalendar;
30 import java.util.Locale;
31 import java.util.TimeZone;
32 import java.util.Timer;
33 import java.util.TimerTask;
34
35 /** 
36  * RolloverFileOutputStream
37  * 
38  * This output stream puts content in a file that is rolled over every 24 hours.
39  * The filename must include the string "yyyy_mm_dd", which is replaced with the 
40  * actual date when creating and rolling over the file.
41  * 
42  * Old files are retained for a number of days before being deleted.
43  * 
44  * 
45  */
46 public class RolloverFileOutputStream extends FilterOutputStream
47 {
48     private static Timer __rollover;
49     
50     final static String YYYY_MM_DD="yyyy_mm_dd";
51     final static String ROLLOVER_FILE_DATE_FORMAT = "yyyy_MM_dd";
52     final static String ROLLOVER_FILE_BACKUP_FORMAT = "HHmmssSSS";
53     final static int ROLLOVER_FILE_RETAIN_DAYS = 31;
54
55     private RollTask _rollTask;
56     private SimpleDateFormat _fileBackupFormat;
57     private SimpleDateFormat _fileDateFormat;
58
59     private String _filename;
60     private File _file;
61     private boolean _append;
62     private int _retainDays;
63     
64     /* ------------------------------------------------------------ */
65     /**
66      * @param filename The filename must include the string "yyyy_mm_dd", 
67      * which is replaced with the actual date when creating and rolling over the file.
68      * @throws IOException
69      */
70     public RolloverFileOutputStream(String filename)
71         throws IOException
72     {
73         this(filename,true,ROLLOVER_FILE_RETAIN_DAYS);
74     }
75     
76     /* ------------------------------------------------------------ */
77     /**
78      * @param filename The filename must include the string "yyyy_mm_dd", 
79      * which is replaced with the actual date when creating and rolling over the file.
80      * @param append If true, existing files will be appended to.
81      * @throws IOException
82      */
83     public RolloverFileOutputStream(String filename, boolean append)
84         throws IOException
85     {
86         this(filename,append,ROLLOVER_FILE_RETAIN_DAYS);
87     }
88
89     /* ------------------------------------------------------------ */
90     /**
91      * @param filename The filename must include the string "yyyy_mm_dd", 
92      * which is replaced with the actual date when creating and rolling over the file.
93      * @param append If true, existing files will be appended to.
94      * @param retainDays The number of days to retain files before deleting them.  0 to retain forever.
95      * @throws IOException
96      */
97     public RolloverFileOutputStream(String filename,
98                                     boolean append,
99                                     int retainDays)
100         throws IOException
101     {
102         this(filename,append,retainDays,TimeZone.getDefault());
103     }
104
105     /* ------------------------------------------------------------ */
106     /**
107      * @param filename The filename must include the string "yyyy_mm_dd", 
108      * which is replaced with the actual date when creating and rolling over the file.
109      * @param append If true, existing files will be appended to.
110      * @param retainDays The number of days to retain files before deleting them. 0 to retain forever.
111      * @throws IOException
112      */
113     public RolloverFileOutputStream(String filename,
114                                     boolean append,
115                                     int retainDays,
116                                     TimeZone zone)
117         throws IOException
118     {
119
120          this(filename,append,retainDays,zone,null,null);
121     }
122      
123     /* ------------------------------------------------------------ */
124     /**
125      * @param filename The filename must include the string "yyyy_mm_dd", 
126      * which is replaced with the actual date when creating and rolling over the file.
127      * @param append If true, existing files will be appended to.
128      * @param retainDays The number of days to retain files before deleting them. 0 to retain forever.
129      * @param dateFormat The format for the date file substitution. The default is "yyyy_MM_dd". 
130      * @param backupFormat The format for the file extension of backup files. The default is "HHmmssSSS". 
131      * @throws IOException
132      */
133     public RolloverFileOutputStream(String filename,
134                                     boolean append,
135                                     int retainDays,
136                                     TimeZone zone,
137                                     String dateFormat,
138                                     String backupFormat)
139         throws IOException
140     {
141         super(null);
142
143         if (dateFormat==null)
144             dateFormat=ROLLOVER_FILE_DATE_FORMAT;
145         _fileDateFormat = new SimpleDateFormat(dateFormat);
146         
147         if (backupFormat==null)
148             backupFormat=ROLLOVER_FILE_BACKUP_FORMAT;
149         _fileBackupFormat = new SimpleDateFormat(backupFormat);
150         
151         _fileBackupFormat.setTimeZone(zone);
152         _fileDateFormat.setTimeZone(zone);
153         
154         if (filename!=null)
155         {
156             filename=filename.trim();
157             if (filename.length()==0)
158                 filename=null;
159         }
160         if (filename==null)
161             throw new IllegalArgumentException("Invalid filename");
162
163         _filename=filename;
164         _append=append;
165         _retainDays=retainDays;
166         setFile();
167         
168         synchronized(RolloverFileOutputStream.class)
169         {
170             if (__rollover==null)
171                 __rollover=new Timer(RolloverFileOutputStream.class.getName(),true);
172             
173             _rollTask=new RollTask();
174
175              Calendar now = Calendar.getInstance();
176              now.setTimeZone(zone);
177
178              GregorianCalendar midnight =
179                  new GregorianCalendar(now.get(Calendar.YEAR),
180                          now.get(Calendar.MONTH),
181                          now.get(Calendar.DAY_OF_MONTH),
182                          23,0);
183              midnight.setTimeZone(zone);
184              midnight.add(Calendar.HOUR,1);
185              __rollover.scheduleAtFixedRate(_rollTask,midnight.getTime(),1000L*60*60*24);
186         }
187     }
188
189     /* ------------------------------------------------------------ */
190     public String getFilename()
191     {
192         return _filename;
193     }
194     
195     /* ------------------------------------------------------------ */
196     public String getDatedFilename()
197     {
198         if (_file==null)
199             return null;
200         return _file.toString();
201     }
202     
203     /* ------------------------------------------------------------ */
204     public int getRetainDays()
205     {
206         return _retainDays;
207     }
208
209     /* ------------------------------------------------------------ */
210     private synchronized void setFile()
211         throws IOException
212     {
213         // Check directory
214         File file = new File(_filename);
215         _filename=file.getCanonicalPath();
216         file=new File(_filename);
217         File dir= new File(file.getParent());
218         if (!dir.isDirectory() || !dir.canWrite())
219             throw new IOException("Cannot write log directory "+dir);
220             
221         Date now=new Date();
222         
223         // Is this a rollover file?
224         String filename=file.getName();
225         int i=filename.toLowerCase(Locale.ENGLISH).indexOf(YYYY_MM_DD);
226         if (i>=0)
227         {
228             file=new File(dir,
229                           filename.substring(0,i)+
230                           _fileDateFormat.format(now)+
231                           filename.substring(i+YYYY_MM_DD.length()));
232         }
233             
234         if (file.exists()&&!file.canWrite())
235             throw new IOException("Cannot write log file "+file);
236
237         // Do we need to change the output stream?
238         if (out==null || !file.equals(_file))
239         {
240             // Yep
241             _file=file;
242             if (!_append && file.exists())
243                 file.renameTo(new File(file.toString()+"."+_fileBackupFormat.format(now)));
244             OutputStream oldOut=out;
245             out=new FileOutputStream(file.toString(),_append);
246             if (oldOut!=null)
247                 oldOut.close();
248             //if(log.isDebugEnabled())log.debug("Opened "+_file);
249         }
250     }
251
252     /* ------------------------------------------------------------ */
253     private void removeOldFiles()
254     {
255         if (_retainDays>0)
256         {
257             long now = System.currentTimeMillis();
258             
259             File file= new File(_filename);
260             File dir = new File(file.getParent());
261             String fn=file.getName();
262             int s=fn.toLowerCase(Locale.ENGLISH).indexOf(YYYY_MM_DD);
263             if (s<0)
264                 return;
265             String prefix=fn.substring(0,s);
266             String suffix=fn.substring(s+YYYY_MM_DD.length());
267
268             String[] logList=dir.list();
269             for (int i=0;i<logList.length;i++)
270             {
271                 fn = logList[i];
272                 if(fn.startsWith(prefix)&&fn.indexOf(suffix,prefix.length())>=0)
273                 {        
274                     File f = new File(dir,fn);
275                     long date = f.lastModified();
276                     if ( ((now-date)/(1000*60*60*24))>_retainDays)
277                         f.delete();   
278                 }
279             }
280         }
281     }
282
283     /* ------------------------------------------------------------ */
284     @Override
285     public void write (byte[] buf)
286             throws IOException
287      {
288             out.write (buf);
289      }
290
291     /* ------------------------------------------------------------ */
292     @Override
293     public void write (byte[] buf, int off, int len)
294             throws IOException
295      {
296             out.write (buf, off, len);
297      }
298     
299     /* ------------------------------------------------------------ */
300     /** 
301      */
302     @Override
303     public void close()
304         throws IOException
305     {
306         synchronized(RolloverFileOutputStream.class)
307         {
308             try{super.close();}
309             finally
310             {
311                 out=null;
312                 _file=null;
313             }
314
315             _rollTask.cancel(); 
316         }
317     }
318     
319     /* ------------------------------------------------------------ */
320     /* ------------------------------------------------------------ */
321     /* ------------------------------------------------------------ */
322     private class RollTask extends TimerTask
323     {
324         @Override
325         public void run()
326         {
327             try
328             {
329                 RolloverFileOutputStream.this.setFile();
330                 RolloverFileOutputStream.this.removeOldFiles();
331
332             }
333             catch(IOException e)
334             {
335                 // Cannot log this exception to a LOG, as RolloverFOS can be used by logging
336                 e.printStackTrace();
337             }
338         }
339     }
340 }