001: package org.ejbca.appserver.jboss;
002:
003: import java.io.File;
004: import java.io.IOException;
005: import java.text.SimpleDateFormat;
006: import java.util.Date;
007: import java.util.Locale;
008: import java.util.TimeZone;
009:
010: import org.apache.log4j.FileAppender;
011: import org.apache.log4j.Layout;
012: import org.apache.log4j.helpers.LogLog;
013: import org.apache.log4j.spi.LoggingEvent;
014:
015: /**
016: * A shameless copy of DailyRollingFileAppender from log4j and merged (also shamelessly)
017: * with DailyRollingFileAppender from jboss.
018: *
019: * This was the only way I could find to implement the desired functionality.
020: *
021: * @author tomas
022: * @version $Id: ScriptrunningDailyRollingFileAppender.java,v 1.2 2006/12/04 12:44:38 anatom Exp $
023: */
024: public class ScriptrunningDailyRollingFileAppender extends FileAppender {
025:
026: // The code assumes that the following constants are in a increasing
027: // sequence.
028: static final int TOP_OF_TROUBLE = -1;
029: static final int TOP_OF_MINUTE = 0;
030: static final int TOP_OF_HOUR = 1;
031: static final int HALF_DAY = 2;
032: static final int TOP_OF_DAY = 3;
033: static final int TOP_OF_WEEK = 4;
034: static final int TOP_OF_MONTH = 5;
035:
036: private Thread scriptThread;
037:
038: /**
039: The date pattern. By default, the pattern is set to
040: "'.'yyyy-MM-dd" meaning daily rollover.
041: */
042: private String datePattern = "'.'yyyy-MM-dd";
043:
044: /** The script to run after rotating log */
045: private String script;
046:
047: /**
048: The log file will be renamed to the value of the
049: scheduledFilename variable when the next interval is entered. For
050: example, if the rollover period is one hour, the log file will be
051: renamed to the value of "scheduledFilename" at the beginning of
052: the next hour.
053:
054: The precise time when a rollover occurs depends on logging
055: activity.
056: */
057: private String scheduledFilename;
058:
059: /**
060: The next time we estimate a rollover should occur. */
061: private long nextCheck = System.currentTimeMillis() - 1;
062:
063: Date now = new Date();
064:
065: SimpleDateFormat sdf;
066:
067: RollingCalendar rc = new RollingCalendar();
068:
069: int checkPeriod = TOP_OF_TROUBLE;
070:
071: // The gmtTimeZone is used only in computeCheckPeriod() method.
072: static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");
073:
074: /**
075: The default constructor does nothing. */
076: public ScriptrunningDailyRollingFileAppender() {
077: }
078:
079: /**
080: Instantiate a <code>DailyRollingFileAppender</code> and open the
081: file designated by <code>filename</code>. The opened filename will
082: become the ouput destination for this appender.
083:
084: */
085: public ScriptrunningDailyRollingFileAppender(Layout layout,
086: String filename, String datePattern) throws IOException {
087: super (layout, filename, true);
088: this .datePattern = datePattern;
089: activateOptions();
090: }
091:
092: /** This is from org.jboss.logging.appender.DailyRollingFileAppender,
093: * which will make the directory structure for the set log file.
094: */
095: public void setFile(final String filename) {
096: org.jboss.logging.appender.FileAppender.Helper
097: .makePath(filename);
098: super .setFile(filename);
099: }
100:
101: /**
102: The <b>DatePattern</b> takes a string in the same format as
103: expected by {@link SimpleDateFormat}. This options determines the
104: rollover schedule.
105: */
106: public void setDatePattern(String pattern) {
107: datePattern = pattern;
108: }
109:
110: /** Returns the value of the <b>DatePattern</b> option. */
111: public String getDatePattern() {
112: return datePattern;
113: }
114:
115: public void activateOptions() {
116: super .activateOptions();
117: if (datePattern != null && fileName != null) {
118: now.setTime(System.currentTimeMillis());
119: sdf = new SimpleDateFormat(datePattern);
120: int type = computeCheckPeriod();
121: printPeriodicity(type);
122: rc.setType(type);
123: File file = new File(fileName);
124: scheduledFilename = fileName
125: + sdf.format(new Date(file.lastModified()));
126:
127: } else {
128: LogLog
129: .error("Either File or DatePattern options are not set for appender ["
130: + name + "].");
131: }
132: if (script == null) {
133: LogLog.error("Script option is not set for appender ["
134: + name + "].");
135: }
136: }
137:
138: void printPeriodicity(int type) {
139: switch (type) {
140: case TOP_OF_MINUTE:
141: LogLog.debug("Appender [" + name
142: + "] to be rolled every minute.");
143: break;
144: case TOP_OF_HOUR:
145: LogLog.debug("Appender [" + name
146: + "] to be rolled on top of every hour.");
147: break;
148: case HALF_DAY:
149: LogLog.debug("Appender [" + name
150: + "] to be rolled at midday and midnight.");
151: break;
152: case TOP_OF_DAY:
153: LogLog.debug("Appender [" + name
154: + "] to be rolled at midnight.");
155: break;
156: case TOP_OF_WEEK:
157: LogLog.debug("Appender [" + name
158: + "] to be rolled at start of week.");
159: break;
160: case TOP_OF_MONTH:
161: LogLog.debug("Appender [" + name
162: + "] to be rolled at start of every month.");
163: break;
164: default:
165: LogLog.warn("Unknown periodicity for appender [" + name
166: + "].");
167: }
168: }
169:
170: // This method computes the roll over period by looping over the
171: // periods, starting with the shortest, and stopping when the r0 is
172: // different from from r1, where r0 is the epoch formatted according
173: // the datePattern (supplied by the user) and r1 is the
174: // epoch+nextMillis(i) formatted according to datePattern. All date
175: // formatting is done in GMT and not local format because the test
176: // logic is based on comparisons relative to 1970-01-01 00:00:00
177: // GMT (the epoch).
178:
179: int computeCheckPeriod() {
180: RollingCalendar rollingCalendar = new RollingCalendar(
181: gmtTimeZone, Locale.ENGLISH);
182: // set sate to 1970-01-01 00:00:00 GMT
183: Date epoch = new Date(0);
184: if (datePattern != null) {
185: for (int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) {
186: SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
187: datePattern);
188: simpleDateFormat.setTimeZone(gmtTimeZone); // do all date formatting in GMT
189: String r0 = simpleDateFormat.format(epoch);
190: rollingCalendar.setType(i);
191: Date next = new Date(rollingCalendar
192: .getNextCheckMillis(epoch));
193: String r1 = simpleDateFormat.format(next);
194: if (r0 != null && r1 != null && !r0.equals(r1)) {
195: return i;
196: }
197: }
198: }
199: return TOP_OF_TROUBLE; // Deliberately head for trouble...
200: }
201:
202: /**
203: Rollover the current file to a new file.
204: */
205: void rollOver() throws IOException {
206:
207: /* Compute filename, but only if datePattern is specified */
208: if (datePattern == null) {
209: errorHandler
210: .error("Missing DatePattern option in rollOver().");
211: return;
212: }
213:
214: String datedFilename = fileName + sdf.format(now);
215: // It is too early to roll over because we are still within the
216: // bounds of the current interval. Rollover will occur once the
217: // next interval is reached.
218: if (scheduledFilename.equals(datedFilename)) {
219: return;
220: }
221:
222: // close current file, and rename it to datedFilename
223: this .closeFile();
224:
225: File target = new File(scheduledFilename);
226: if (target.exists()) {
227: target.delete();
228: }
229:
230: File file = new File(fileName);
231: boolean result = file.renameTo(target);
232: if (result) {
233: LogLog.debug(fileName + " -> " + scheduledFilename);
234: } else {
235: LogLog.error("Failed to rename [" + fileName + "] to ["
236: + scheduledFilename + "].");
237: }
238:
239: try {
240: // This will also close the file. This is OK since multiple
241: // close operations are safe.
242: this .setFile(fileName, false, this .bufferedIO,
243: this .bufferSize);
244: } catch (IOException e) {
245: errorHandler.error("setFile(" + fileName
246: + ", false) call failed.");
247: }
248: if ((script != null) && (script.length() > 0)) {
249: // Now call the script
250: // Check first if an old instance of the thread is blocking
251: if ((scriptThread != null) && scriptThread.isAlive()) {
252: System.out.println("Stopping old hanging signerthread");
253: scriptThread.interrupt();
254: }
255: scriptThread = new Thread(new ScriptThread(script,
256: scheduledFilename));
257: scriptThread.start();
258: }
259:
260: scheduledFilename = datedFilename;
261: }
262:
263: /**
264: * This method differentiates DailyRollingFileAppender from its
265: * super class.
266: *
267: * <p>Before actually logging, this method will check whether it is
268: * time to do a rollover. If it is, it will schedule the next
269: * rollover time and then rollover.
270: * */
271: protected void subAppend(LoggingEvent event) {
272: long n = System.currentTimeMillis();
273: if (n >= nextCheck) {
274: now.setTime(n);
275: nextCheck = rc.getNextCheckMillis(now);
276: try {
277: rollOver();
278: } catch (IOException ioe) {
279: LogLog.error("rollOver() failed.", ioe);
280: }
281: }
282: super .subAppend(event);
283: }
284:
285: public String getScript() {
286: return script;
287: }
288:
289: public void setScript(String script) {
290: this .script = script;
291: }
292:
293: }
294:
295: class ScriptThread implements Runnable {
296: private String script;
297: private String infile;
298:
299: public ScriptThread(String script, String infile) {
300: this .script = script;
301: this .infile = infile;
302: }
303:
304: public void run() {
305:
306: try {
307: Runtime rt = Runtime.getRuntime();
308: String s = script + " " + infile;
309: rt.exec(s);
310: /*
311: Process p = rt.exec(s);
312: try {
313: int e = p.exitValue();
314: if (e != 0) {
315: LogLog.error("Script did not terminate with 0 return value.");
316: System.out.println("Script did not terminate with 0 return value.");
317: }
318: } catch (IllegalThreadStateException e) {
319: LogLog.error("Script did not terminate within the timeout, 60 seconds.");
320: System.out.println("Script did not terminate within the timeout, 60 seconds.");
321: p.destroy();
322: }
323: */
324: } catch (Exception e) {
325: LogLog.error("Exception caught while running script: ", e);
326: e.printStackTrace();
327: }
328:
329: }
330:
331: }
|