KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ejbca > appserver > jboss > SigningDailyRollingFileAppender


1 package org.ejbca.appserver.jboss;
2
3 import java.io.ByteArrayInputStream JavaDoc;
4 import java.io.ByteArrayOutputStream JavaDoc;
5 import java.io.File JavaDoc;
6 import java.io.FileOutputStream JavaDoc;
7 import java.io.IOException JavaDoc;
8 import java.io.InputStream JavaDoc;
9 import java.math.BigInteger JavaDoc;
10 import java.security.MessageDigest JavaDoc;
11 import java.text.SimpleDateFormat JavaDoc;
12 import java.util.Date JavaDoc;
13 import java.util.Locale JavaDoc;
14 import java.util.Random JavaDoc;
15 import java.util.TimeZone JavaDoc;
16
17 import org.apache.commons.httpclient.HttpClient;
18 import org.apache.commons.httpclient.HttpStatus;
19 import org.apache.commons.httpclient.methods.PostMethod;
20 import org.apache.log4j.FileAppender;
21 import org.apache.log4j.Layout;
22 import org.apache.log4j.helpers.LogLog;
23 import org.apache.log4j.spi.LoggingEvent;
24 import org.bouncycastle.tsp.TSPAlgorithms;
25 import org.bouncycastle.tsp.TSPException;
26 import org.bouncycastle.tsp.TSPValidationException;
27 import org.bouncycastle.tsp.TimeStampRequest;
28 import org.bouncycastle.tsp.TimeStampRequestGenerator;
29 import org.bouncycastle.tsp.TimeStampResponse;
30 import org.bouncycastle.util.encoders.Base64;
31 import org.ejbca.util.CertTools;
32 import org.ejbca.util.FileTools;
33
34 /**
35  * A shameless copy of DailyRollingFileAppender from log4j and merged (also shamelessly)
36  * with DailyRollingFileAppender from jboss.
37  *
38  * This was the only way I could find to implement the desired functionality.
39  *
40  * @author tomas
41  * @version $Id: SigningDailyRollingFileAppender.java,v 1.2 2006/08/14 08:46:16 anatom Exp $
42  */

43 public class SigningDailyRollingFileAppender extends FileAppender {
44
45     private Thread JavaDoc signerThread;
46
47     /**
48          The date pattern. By default, the pattern is set to
49          "'.'yyyy-MM-dd" meaning daily rollover.
50      */

51     private String JavaDoc datePattern = "'.'yyyy-MM-dd";
52
53     /** The method use to create a signature */
54     private String JavaDoc signMethod;
55
56     /** The URL to a TSA server used to create time stamps for rolled over log files */
57     private String JavaDoc tsaUrl;
58     
59     /**
60          The log file will be renamed to the value of the
61          scheduledFilename variable when the next interval is entered. For
62          example, if the rollover period is one hour, the log file will be
63          renamed to the value of "scheduledFilename" at the beginning of
64          the next hour.
65
66          The precise time when a rollover occurs depends on logging
67          activity.
68      */

69     private String JavaDoc scheduledFilename;
70
71     /**
72          The next time we estimate a rollover should occur. */

73     private long nextCheck = System.currentTimeMillis () - 1;
74
75     Date JavaDoc now = new Date JavaDoc();
76
77     SimpleDateFormat JavaDoc sdf;
78
79     RollingCalendar rc = new RollingCalendar();
80
81     int checkPeriod = RollingCalendar.TOP_OF_TROUBLE;
82
83     // The gmtTimeZone is used only in computeCheckPeriod() method.
84
static final TimeZone JavaDoc gmtTimeZone = TimeZone.getTimeZone("GMT");
85
86
87     /**
88          The default constructor does nothing. */

89     public SigningDailyRollingFileAppender() {
90     }
91
92     /**
93         Instantiate a <code>DailyRollingFileAppender</code> and open the
94         file designated by <code>filename</code>. The opened filename will
95         become the ouput destination for this appender.
96
97      */

98     public SigningDailyRollingFileAppender (Layout layout, String JavaDoc filename,
99             String JavaDoc datePattern) throws IOException JavaDoc {
100         super(layout, filename, true);
101         this.datePattern = datePattern;
102         activateOptions();
103     }
104
105     /** This is from org.jboss.logging.appender.DailyRollingFileAppender,
106      * which will make the directory structure for the set log file.
107      */

108     public void setFile(final String JavaDoc filename)
109     {
110         org.jboss.logging.appender.FileAppender.Helper.makePath(filename);
111         super.setFile(filename);
112     }
113
114     /**
115          The <b>DatePattern</b> takes a string in the same format as
116          expected by {@link SimpleDateFormat}. This options determines the
117          rollover schedule.
118      */

119     public void setDatePattern(String JavaDoc pattern) {
120         datePattern = pattern;
121     }
122
123     /** Returns the value of the <b>DatePattern</b> option. */
124     public String JavaDoc getDatePattern() {
125         return datePattern;
126     }
127     
128     public void setSignMethod(String JavaDoc method) {
129         signMethod = method;
130     }
131     public String JavaDoc getSignMethod() {
132         return signMethod;
133     }
134     public void setTsaUrl(String JavaDoc url) {
135         tsaUrl = url;
136     }
137     public String JavaDoc getTsaUrl() {
138         return tsaUrl;
139     }
140
141     public void activateOptions() {
142         super.activateOptions();
143         if(datePattern != null && fileName != null) {
144             now.setTime(System.currentTimeMillis());
145             sdf = new SimpleDateFormat JavaDoc(datePattern);
146             int type = computeCheckPeriod();
147             printPeriodicity(type);
148             rc.setType(type);
149             File JavaDoc file = new File JavaDoc(fileName);
150             scheduledFilename = fileName+sdf.format(new Date JavaDoc(file.lastModified()));
151
152         } else {
153             LogLog.error("Either File or DatePattern options are not set for appender ["+name+"].");
154         }
155         if (signMethod != null) {
156             if (tsaUrl == null) {
157                 LogLog.error("TsaUrl option is not set for appender ["+name+"].");
158             }
159         } else {
160             LogLog.error("SignMethod option is not set for appender ["+name+"].");
161         }
162         CertTools.installBCProvider();
163     }
164
165     void printPeriodicity(int type) {
166         switch(type) {
167         case RollingCalendar.TOP_OF_MINUTE:
168             LogLog.debug("Appender ["+name+"] to be rolled every minute.");
169             break;
170         case RollingCalendar.TOP_OF_HOUR:
171             LogLog.debug("Appender ["+name
172                     +"] to be rolled on top of every hour.");
173             break;
174         case RollingCalendar.HALF_DAY:
175             LogLog.debug("Appender ["+name
176                     +"] to be rolled at midday and midnight.");
177             break;
178         case RollingCalendar.TOP_OF_DAY:
179             LogLog.debug("Appender ["+name
180                     +"] to be rolled at midnight.");
181             break;
182         case RollingCalendar.TOP_OF_WEEK:
183             LogLog.debug("Appender ["+name
184                     +"] to be rolled at start of week.");
185             break;
186         case RollingCalendar.TOP_OF_MONTH:
187             LogLog.debug("Appender ["+name
188                     +"] to be rolled at start of every month.");
189             break;
190         default:
191             LogLog.warn("Unknown periodicity for appender ["+name+"].");
192         }
193     }
194
195
196     // This method computes the roll over period by looping over the
197
// periods, starting with the shortest, and stopping when the r0 is
198
// different from from r1, where r0 is the epoch formatted according
199
// the datePattern (supplied by the user) and r1 is the
200
// epoch+nextMillis(i) formatted according to datePattern. All date
201
// formatting is done in GMT and not local format because the test
202
// logic is based on comparisons relative to 1970-01-01 00:00:00
203
// GMT (the epoch).
204

205     int computeCheckPeriod() {
206         RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone, Locale.ENGLISH);
207         // set sate to 1970-01-01 00:00:00 GMT
208
Date JavaDoc epoch = new Date JavaDoc(0);
209         if(datePattern != null) {
210             for(int i = RollingCalendar.TOP_OF_MINUTE; i <= RollingCalendar.TOP_OF_MONTH; i++) {
211                 SimpleDateFormat JavaDoc simpleDateFormat = new SimpleDateFormat JavaDoc(datePattern);
212                 simpleDateFormat.setTimeZone(gmtTimeZone); // do all date formatting in GMT
213
String JavaDoc r0 = simpleDateFormat.format(epoch);
214                 rollingCalendar.setType(i);
215                 Date JavaDoc next = new Date JavaDoc(rollingCalendar.getNextCheckMillis(epoch));
216                 String JavaDoc r1 = simpleDateFormat.format(next);
217                 //System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1);
218
if(r0 != null && r1 != null && !r0.equals(r1)) {
219                     return i;
220                 }
221             }
222         }
223         return RollingCalendar.TOP_OF_TROUBLE; // Deliberately head for trouble...
224
}
225
226     /**
227          Rollover the current file to a new file.
228      */

229     void rollOver() throws IOException JavaDoc {
230
231         /* Compute filename, but only if datePattern is specified */
232         if (datePattern == null) {
233             errorHandler.error("Missing DatePattern option in rollOver().");
234             return;
235         }
236
237         String JavaDoc datedFilename = fileName+sdf.format(now);
238         // It is too early to roll over because we are still within the
239
// bounds of the current interval. Rollover will occur once the
240
// next interval is reached.
241
if (scheduledFilename.equals(datedFilename)) {
242             return;
243         }
244
245         // close current file, and rename it to datedFilename
246
this.closeFile();
247
248         File JavaDoc target = new File JavaDoc(scheduledFilename);
249         if (target.exists()) {
250             target.delete();
251         }
252
253         File JavaDoc file = new File JavaDoc(fileName);
254         boolean result = file.renameTo(target);
255         if(result) {
256             LogLog.debug(fileName +" -> "+ scheduledFilename);
257         } else {
258             LogLog.error("Failed to rename ["+fileName+"] to ["+scheduledFilename+"].");
259         }
260
261         try {
262             // This will also close the file. This is OK since multiple
263
// close operations are safe.
264
this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
265         }
266         catch(IOException JavaDoc e) {
267             errorHandler.error("setFile("+fileName+", false) call failed.");
268         }
269         if (signMethod.equalsIgnoreCase("tsa")) {
270             if (tsaUrl != null) {
271                 // Now do the actual signing
272
// Check first if an old instance of the thread is blocking
273
if ( (signerThread != null) && signerThread.isAlive() ) {
274                     System.out.println("Stopping old hanging signerthread");
275                     signerThread.interrupt();
276                 }
277                 signerThread = new Thread JavaDoc(new SignerThread(tsaUrl, scheduledFilename, scheduledFilename+".tsp"));
278                 signerThread.start();
279             } else {
280                 System.out.println("No TsaUrl set, can not sign logs!");
281             }
282         }
283
284         scheduledFilename = datedFilename;
285     }
286
287     /**
288      * This method differentiates DailyRollingFileAppender from its
289      * super class.
290      *
291      * <p>Before actually logging, this method will check whether it is
292      * time to do a rollover. If it is, it will schedule the next
293      * rollover time and then rollover.
294      * */

295     protected void subAppend(LoggingEvent event) {
296         long n = System.currentTimeMillis();
297         if (n >= nextCheck) {
298             now.setTime(n);
299             nextCheck = rc.getNextCheckMillis(now);
300             try {
301                 rollOver();
302             }
303             catch(IOException JavaDoc ioe) {
304                 LogLog.error("rollOver() failed.", ioe);
305             }
306         }
307         super.subAppend(event);
308     }
309
310 }
311
312 class SignerThread implements Runnable JavaDoc {
313     private String JavaDoc urlstr;
314     private String JavaDoc infile;
315     private String JavaDoc outfile;
316     public SignerThread(String JavaDoc urlstr, String JavaDoc infile, String JavaDoc outfile) {
317         this.urlstr = urlstr;
318         this.infile = infile;
319         this.outfile = outfile;
320     }
321     public void run() {
322         
323         try {
324             boolean base64 = true;
325             TimeStampRequestGenerator timeStampRequestGenerator = new TimeStampRequestGenerator();
326
327             Random JavaDoc rand = new Random JavaDoc();
328             int nonce = rand.nextInt();
329             byte[] digestBytes = new byte[20];
330             if (infile != null) {
331                 digestBytes = FileTools.readFiletoBuffer(infile);
332             }
333             MessageDigest JavaDoc dig = MessageDigest.getInstance(TSPAlgorithms.SHA1, "BC");
334             dig.update(digestBytes);
335             byte[] digest = dig.digest();
336             TimeStampRequest timeStampRequest = timeStampRequestGenerator.generate(TSPAlgorithms.SHA1, digest, BigInteger.valueOf(nonce));
337
338             // create a singular HttpClient object
339
HttpClient client = new HttpClient();
340
341             //establish a connection within 5 seconds
342
client.setConnectionTimeout(5000);
343             //client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
344
PostMethod method = new PostMethod(urlstr);
345             method.setParameter("http.socket.timeout", "5000");
346             method.setRequestHeader("Content-Type", "application/timestamp-query");
347             method.setRequestBody(new ByteArrayInputStream JavaDoc(timeStampRequest.getEncoded()));
348             //method.setRequestEntity(new InputStreamRequestEntity(new ByteArrayInputStream(timeStampRequest.getEncoded())));
349
//method.setContentChunked(true);
350
InputStream JavaDoc input = null;
351             ByteArrayOutputStream JavaDoc baos = null;
352             byte[] replyBytes = null;
353             try {
354                 client.executeMethod(method);
355                 if (method.getStatusCode() == HttpStatus.SC_OK) {
356                     replyBytes = method.getResponseBody();
357                 }
358             } catch (Exception JavaDoc e) {
359                 e.printStackTrace();
360             } finally {
361                 method.releaseConnection();
362                 if (input != null) input.close();
363                 if (baos != null) baos.close();
364             }
365
366             if ( (outfile != null) && (replyBytes != null) ) {
367                 // Store request
368
byte[] outBytes;
369                 if (base64) {
370                     outBytes=Base64.encode(replyBytes);
371                 } else {
372                     outBytes = replyBytes;
373                 }
374                 FileOutputStream JavaDoc fos = null;
375                 try {
376                     fos = new FileOutputStream JavaDoc(outfile);
377                     fos.write(outBytes);
378                 } finally {
379                     if (fos != null) fos.close();
380                 }
381             }
382
383             if (replyBytes != null) {
384                 try {
385                     TimeStampResponse timeStampResponse = new TimeStampResponse(replyBytes);
386                     timeStampResponse.validate(timeStampRequest);
387                 } catch (TSPValidationException e) {
388                     LogLog.error("TimeStampResponse validation failed.", e);
389                     e.printStackTrace();
390                 } catch (TSPException e) {
391                     LogLog.error("TimeStampResponse failed.", e);
392                     e.printStackTrace();
393                 }
394             } else {
395                 LogLog.error("No reply bytes received, is TSA down?");
396                 System.out.println("SigningDailyRollingFileAppender: No reply bytes received, is TSA down?");
397             }
398         } catch (Exception JavaDoc e) {
399             LogLog.error("Exception caught while signing log: ", e);
400             e.printStackTrace();
401         }
402         
403     }
404     
405 }
406
407
Popular Tags