KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > grlea > log > rollover > RolloverManager


1 package org.grlea.log.rollover;
2
3 // $Id: RolloverManager.java,v 1.7 2006/07/21 11:57:31 grlea Exp $
4
// Copyright (c) 2004-2006 Graham Lea. All rights reserved.
5

6 // Licensed under the Apache License, Version 2.0 (the "License");
7
// you may not use this file except in compliance with the License.
8
// You may obtain a copy of the License at
9
//
10
// http://www.apache.org/licenses/LICENSE-2.0
11
//
12
// Unless required by applicable law or agreed to in writing, software
13
// distributed under the License is distributed on an "AS IS" BASIS,
14
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
// See the License for the specific language governing permissions and
16
// limitations under the License.
17

18 import java.io.BufferedReader JavaDoc;
19 import java.io.File JavaDoc;
20 import java.io.FileOutputStream JavaDoc;
21 import java.io.FileReader JavaDoc;
22 import java.io.FileWriter JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.RandomAccessFile JavaDoc;
25 import java.io.StringWriter JavaDoc;
26 import java.io.Writer JavaDoc;
27 import java.nio.channels.Channels JavaDoc;
28 import java.nio.channels.FileChannel JavaDoc;
29 import java.text.Format JavaDoc;
30 import java.text.MessageFormat JavaDoc;
31 import java.text.ParseException JavaDoc;
32 import java.util.Arrays JavaDoc;
33 import java.util.Collections JavaDoc;
34 import java.util.Date JavaDoc;
35 import java.util.Properties JavaDoc;
36 import java.util.Timer JavaDoc;
37 import java.util.TimerTask JavaDoc;
38
39 /**
40  * <p>The RolloverManager is a {@link Writer} implementation used by Simple Log to enable periodic
41  * log-rolling functionality. It is a writer which writes to an "active" log file and, when told by
42  * its {@link RolloverStrategy} that the file is due to be rolled over, moves the content of the
43  * active log file into a new rollover log file. Sufficient mechanics are employed to ensure that
44  * no log content is lost while the roll over is being conducted.</p>
45  *
46  * @author Graham Lea
47  * @version $Revision: 1.7 $
48  */

49 public class
50 RolloverManager
51 extends Writer JavaDoc
52 {
53    /** The prefix for all special properties keys. */
54    private static final String JavaDoc KEY_PREFIX = "simplelog.";
55
56    /** The property key for the rollover strategy. */
57    private static final String JavaDoc KEY_ROLLOVER_STRATEGY = KEY_PREFIX + "rollover";
58
59    /** The rollover strategy alias for FileSizeRolloverStrategy. */
60    private static final String JavaDoc ROLLOVER_STRATEGY_FILESIZE = "fileSize";
61
62    /** The rollover strategy alias for TimeOfDayRolloverStrategy. */
63    private static final String JavaDoc ROLLOVER_STRATEGY_TIMEOFDAY = "timeOfDay";
64
65    /** The property key for the active log file name. */
66    private static final String JavaDoc KEY_ACTIVE_LOG_FILE = KEY_PREFIX + "logFile";
67
68    /** The property key for rolled log file names. */
69    private static final String JavaDoc KEY_ROLLOVER_LOG_FILE = KEY_PREFIX + "rollover.filename";
70
71    /** The property key for rolled log file names. */
72    private static final String JavaDoc KEY_ROLLOVER_DIRECTORY = KEY_PREFIX + "rollover.directory";
73
74    /** The property key for the log rollover check period. */
75    private static final String JavaDoc KEY_ROLLOVER_PERIOD = KEY_PREFIX + "rollover.period";
76
77    /** The default rollover period. */
78    private static final String JavaDoc DEFAULT_ROLLOVER_PERIOD = "60";
79
80    /**
81     * A semaphore lock which, in concert which {@link #printerCount} is used to monitor the number
82     * of printing threads and to notify a thread waiting to change the writer.
83     */

84    private final Object JavaDoc PRINTERS_SEMAPHORE = new Object JavaDoc();
85
86    /** The number of threads currently executing a printing operation. */
87    private int printerCount = 0;
88
89    /**
90     * Lock object used as a gate to prevent new printing threads from acquiring a printing semaphore
91     * when another thread is waiting to change the writer. Basically, it is used to ensure priority
92     * for the writer-changing thread.
93     */

94    private final Object JavaDoc WRITER_CHANGE_GATE = new Object JavaDoc();
95
96    /** The current writer. */
97    private Writer JavaDoc writer;
98
99    /** The temporary writer, used while the rollover is occurring. */
100    private StringWriter JavaDoc tempWriter = new StringWriter JavaDoc(1024);
101
102    /** The rollover strategy in use. */
103    private RolloverStrategy strategy;
104
105    /** The name used to create the rollover strategy currently in use. */
106    private String JavaDoc currentRolloverStrategyName;
107
108    /** The active log file currently in use. */
109    private File JavaDoc currentActiveLogFile;
110
111    /** The directory of the current active log file. */
112    private File JavaDoc currentActiveLogFileDirectory;
113
114    /** The directory where rolled log files will be stored. */
115    private File JavaDoc rolloverDirectory;
116
117    /** The format used to construct rolled log file names. */
118    private MessageFormat JavaDoc rolloverLogFileFormat;
119
120    /** The read/write stream for the active log file. */
121    private RandomAccessFile JavaDoc fileOut;
122
123    /** Indicates whether the strategy currently in use was set programatically. */
124    private boolean strategySetProgramatically = false;
125
126    /** The time (in seconds) between checks for rollover. */
127    private long rolloverPeriod;
128
129    /** The timer used to prompt rollover to occur. */
130    private Timer JavaDoc timer;
131
132    /** The current unique file ID */
133    private int uniqueFileId = 0;
134
135    /** An object to which errors should be reported. */
136    private ErrorReporter errorReporter;
137
138    /**
139     * Creates a new <code>RolloverManager</code>, configuring it with provided properties. The given
140     * {@link ErrorReporter} object is retained and used to report errors for the life of the new
141     * <code>RolloverManager</code> instance.
142     *
143     * @param properties properties to be used to configure the new <code>RolloverManager</code>
144     *
145     * @param errorReporter and object to which errors that occur while the
146     * <code>RolloverManager</code> is running will be reported. May be <code>null</code>.
147     *
148     * @throws IOException if an error occurs while configuring the <code>RolloverManager</code> or
149     * opening the files it will be logging to.
150     *
151     * @throws IllegalArgumentException if <code>properties</code> is <code>null</code>.
152     */

153    public
154    RolloverManager(Properties JavaDoc properties, ErrorReporter errorReporter)
155    throws IOException JavaDoc
156    {
157       if (properties == null)
158          throw new IllegalArgumentException JavaDoc("properties cannot be null.");
159
160       this.errorReporter = errorReporter;
161       configure(properties);
162    }
163
164    /**
165     * Configures this <code>RolloverManager</code> using the given properties.
166     *
167     * @param properties properties to be used to configure this <code>RolloverManager</code>
168     *
169     * @throws IOException if an error occurs while configuring the <code>RolloverManager</code> or
170     * opening the files it will be logging to.
171     */

172    public void
173    configure(Properties JavaDoc properties)
174    throws IOException JavaDoc
175    {
176       configureStrategy(properties);
177       configureWriter(properties);
178    }
179
180    /**
181     * Configures the strategy of this <code>RolloverManager</code>.
182     */

183    private void
184    configureStrategy(Properties JavaDoc properties)
185    throws IOException JavaDoc
186    {
187       if (strategySetProgramatically)
188       {
189          return;
190       }
191
192       // Get the name.
193
String JavaDoc rolloverStrategyString = properties.getProperty(KEY_ROLLOVER_STRATEGY);
194
195       if (rolloverStrategyString == null)
196       {
197          throw new IOException JavaDoc("RolloverManger created, but rollover property not specified.");
198       }
199
200       rolloverStrategyString = rolloverStrategyString.trim();
201       if (rolloverStrategyString.length() == 0)
202       {
203          throw new IOException JavaDoc("RolloverManger created, but rollover property not specified.");
204       }
205
206       boolean strategyChanged = !rolloverStrategyString.equals(currentRolloverStrategyName);
207       RolloverStrategy newStrategy;
208       if (strategyChanged)
209       {
210          try
211          {
212             if (ROLLOVER_STRATEGY_FILESIZE.equals(rolloverStrategyString))
213             {
214                newStrategy = new FileSizeRolloverStrategy();
215             }
216             else if (ROLLOVER_STRATEGY_TIMEOFDAY.equals(rolloverStrategyString))
217             {
218                newStrategy = new TimeOfDayRolloverStrategy();
219             }
220             else
221             {
222                try
223                {
224                   Class JavaDoc strategyClass = Class.forName(rolloverStrategyString);
225                   Object JavaDoc strategyObject = strategyClass.newInstance();
226                   if (!(strategyObject instanceof RolloverStrategy))
227                   {
228                      throw new IOException JavaDoc(strategyClass.getName() + " is not a RolloverStrategy");
229                   }
230
231                   newStrategy = (RolloverStrategy) strategyObject;
232                }
233                catch (ClassNotFoundException JavaDoc e)
234                {
235                   throw new IOException JavaDoc("Class '" + rolloverStrategyString + "' not found: " + e);
236                }
237                catch (InstantiationException JavaDoc e)
238                {
239                   throw new IOException JavaDoc("Failed to create an instance of " +
240                                         rolloverStrategyString + ": " + e);
241                }
242                catch (IllegalAccessException JavaDoc e)
243                {
244                   throw new IOException JavaDoc("Failed to create an instance of " +
245                                         rolloverStrategyString + ": " + e);
246                }
247             }
248          }
249          catch (IOException JavaDoc e)
250          {
251             throw new IOException JavaDoc("Error creating RolloverStrategy: " + e);
252          }
253       }
254       else
255       {
256          newStrategy = strategy;
257       }
258
259       try
260       {
261          newStrategy.configure(Collections.unmodifiableMap(properties));
262       }
263       catch (IOException JavaDoc e)
264       {
265          throw new IOException JavaDoc("Error configuring RolloverStrategy: " + e);
266       }
267
268       this.strategy = newStrategy;
269       currentRolloverStrategyName = rolloverStrategyString;
270    }
271
272    /**
273     * Configures the writer of this <code>RolloverManager</code>.
274     */

275    private void
276    configureWriter(Properties JavaDoc properties)
277    throws IOException JavaDoc
278    {
279       // Active log file
280
String JavaDoc newActiveLogFileName = properties.getProperty(KEY_ACTIVE_LOG_FILE);
281       if (newActiveLogFileName == null)
282          throw new IOException JavaDoc("RolloverManager created but no active log file name specified.");
283
284       // Create active log file's directory
285
File JavaDoc newActiveLogFile = new File JavaDoc(newActiveLogFileName.trim()).getAbsoluteFile();
286
287       if (newActiveLogFile.isDirectory())
288          throw new IOException JavaDoc("The specified active log file name already exists as a directory.");
289
290       File JavaDoc newActiveLogFileDirectory = newActiveLogFile.getParentFile();
291       if (newActiveLogFileDirectory != null)
292          newActiveLogFileDirectory.mkdirs();
293       else
294          throw new IOException JavaDoc("Caanot access the active log file's parent directory.");
295
296       // Rollover log file name pattern
297
String JavaDoc newRolloverLogFileNamePattern = properties.getProperty(KEY_ROLLOVER_LOG_FILE);
298       if (newRolloverLogFileNamePattern == null)
299          newRolloverLogFileNamePattern = "{1}-" + newActiveLogFile.getName();
300
301       newRolloverLogFileNamePattern = newRolloverLogFileNamePattern.trim();
302
303       // Ensure rollover log fille name pattern compiles
304
MessageFormat JavaDoc newRolloverLogFileNameFormat;
305       try
306       {
307          newRolloverLogFileNameFormat = new MessageFormat JavaDoc(newRolloverLogFileNamePattern);
308       }
309       catch (IllegalArgumentException JavaDoc e)
310       {
311          throw new IOException JavaDoc("Illegal pattern provided for rollover log file name: " +
312                                e.getMessage());
313       }
314
315       // Rollover log file directory
316
String JavaDoc newRolloverDirectoryName = properties.getProperty(KEY_ROLLOVER_DIRECTORY);
317       File JavaDoc newRolloverDirectory;
318       if (newRolloverDirectoryName != null)
319          newRolloverDirectory = new File JavaDoc(newRolloverDirectoryName.trim()).getAbsoluteFile();
320       else
321          newRolloverDirectory = newActiveLogFileDirectory;
322
323       if (newRolloverDirectory.exists() && !newRolloverDirectory.isDirectory())
324       {
325          throw new IOException JavaDoc(
326             "The location specified for storing rolled log files is not a directory: " +
327             newRolloverDirectory);
328       }
329
330       // Rollover period
331
String JavaDoc rolloverPeriodString = (String JavaDoc) properties.get(KEY_ROLLOVER_PERIOD);
332       if (rolloverPeriodString == null)
333          rolloverPeriodString = DEFAULT_ROLLOVER_PERIOD;
334
335       rolloverPeriodString = rolloverPeriodString.trim();
336       if (rolloverPeriodString.length() == 0)
337          rolloverPeriodString = DEFAULT_ROLLOVER_PERIOD;
338
339       long newRolloverPeriod;
340       try
341       {
342          newRolloverPeriod = Long.parseLong(rolloverPeriodString);
343          if (newRolloverPeriod < 1)
344             throw new NumberFormatException JavaDoc("Must be greater than 0");
345       }
346       catch (NumberFormatException JavaDoc e)
347       {
348          throw new IOException JavaDoc("Invalid rollover period specified: " + rolloverPeriodString + " (" +
349                                e.getMessage() + ")");
350       }
351
352       // Determine the current unique ID if one is required
353
Format JavaDoc[] formats = newRolloverLogFileNameFormat.getFormatsByArgumentIndex();
354       boolean uniqueIdUsedInPattern = formats.length > 1;
355       if (uniqueIdUsedInPattern)
356       {
357          String JavaDoc[] filenames = newRolloverDirectory.list();
358          int maximumFileId = 0;
359          for (int i = 0; filenames != null && i < filenames.length; i++)
360          {
361             String JavaDoc filename = filenames[i];
362             try
363             {
364                Object JavaDoc[] parameters = newRolloverLogFileNameFormat.parse(filename);
365                if (parameters.length > 1 && parameters[1] != null)
366                {
367                   String JavaDoc uniqueFileIdString = (String JavaDoc) parameters[1];
368                   int parsedFileId = Integer.parseInt(uniqueFileIdString, 16);
369                   if (parsedFileId > maximumFileId)
370                      maximumFileId = parsedFileId;
371                }
372             }
373             catch (NumberFormatException JavaDoc e)
374             {
375                // This will happen for any filenames that appear to match the pattern but don't
376
// e.g. 'old-application.log' matches {1}-application.log, but 'old' isn't a number
377
}
378             catch (ParseException JavaDoc e)
379             {
380                // This will happen for any filenames that don't match the pattern
381
}
382          }
383          uniqueFileId = maximumFileId + 1;
384       }
385
386       rolloverDirectory = newRolloverDirectory;
387       rolloverLogFileFormat = newRolloverLogFileNameFormat;
388
389       // Create/Open standard log file
390
if (!newActiveLogFile.equals(currentActiveLogFile))
391       {
392          openWriter(newActiveLogFile, newActiveLogFileDirectory);
393          currentActiveLogFile = newActiveLogFile;
394          currentActiveLogFileDirectory = newActiveLogFileDirectory;
395       }
396
397       // Setup the timer
398
if (timer == null || newRolloverPeriod != rolloverPeriod)
399       {
400          if (timer != null)
401             timer.cancel();
402
403          timer = new Timer JavaDoc(true);
404          timer.schedule(new RolloverTask(), 0, newRolloverPeriod * 1000L);
405          rolloverPeriod = newRolloverPeriod;
406       }
407    }
408
409    /**
410     * Opens the specified file as an active log file for this <code>RolloverManager</code>. This
411     * will also result in the creation of a "creation date" file if necessary.
412     *
413     * @param newActiveLogFile the file that will be opened as the active log file
414     *
415     * @param activeLogFileDirectory the directory in which the active log file resides
416     */

417    private void
418    openWriter(File JavaDoc newActiveLogFile, File JavaDoc activeLogFileDirectory)
419    throws IOException JavaDoc
420    {
421       synchronized (WRITER_CHANGE_GATE)
422       {
423          synchronized (PRINTERS_SEMAPHORE)
424          {
425             if (printerCount != 0)
426             {
427                try
428                {
429                   PRINTERS_SEMAPHORE.wait();
430                }
431                catch (InterruptedException JavaDoc e)
432                {
433                   throw new IllegalStateException JavaDoc("Interrupted while attempting to configure writer");
434                }
435             }
436
437             if (writer != tempWriter)
438             {
439                if (writer != null)
440                {
441                   try
442                   {
443                      writer.close();
444                   }
445                   catch (IOException JavaDoc e)
446                   {
447                      throw new IOException JavaDoc("Failed to close open file: " + e);
448                   }
449                }
450
451                RandomAccessFile JavaDoc newFileOut = new RandomAccessFile JavaDoc(newActiveLogFile, "rws");
452                FileChannel JavaDoc channel = newFileOut.getChannel();
453                long initialChannelSize = channel.size();
454                newFileOut.seek(initialChannelSize);
455                Writer JavaDoc newFileWriter = Channels.newWriter(newFileOut.getChannel(), "UTF-8");
456                // Record the time the file was created if it is new
457
storeFileCreationTimeIfNecessary(activeLogFileDirectory, newActiveLogFile);
458                fileOut = newFileOut;
459                writer = newFileWriter;
460             }
461          }
462       }
463    }
464
465    /**
466     * Writes the creation time (which is the current time) of the active log file to a new file if
467     * necessary (i.e. if the creation file doesn't already exist).
468     *
469     * @param newActiveLogFile the file that will be opened as the active log file
470     *
471     * @param activeLogFileDirectory the directory in which the active log file resides
472     */

473    private void
474    storeFileCreationTimeIfNecessary(File JavaDoc activeLogFileDirectory, File JavaDoc newActiveLogFile)
475    throws IOException JavaDoc
476    {
477       long creationTime = System.currentTimeMillis();
478
479       try
480       {
481          File JavaDoc creationTimeFile = getCreationTimeFile(activeLogFileDirectory, newActiveLogFile);
482
483          // The active log file being new is determined by whether the creation time file exists
484
boolean recordCreationTime = !creationTimeFile.exists();
485          if (recordCreationTime)
486          {
487             FileWriter JavaDoc creationTimeFileOut = new FileWriter JavaDoc(creationTimeFile);
488             creationTimeFileOut.write(String.valueOf(creationTime));
489             creationTimeFileOut.close();
490          }
491       }
492       catch (IOException JavaDoc e)
493       {
494          throw new IOException JavaDoc("Failed to write file creation time: " + e);
495       }
496    }
497
498    /**
499     * Reads and returns the time (in milliseconds since the 1970 epoch) that the active log file was
500     * created, according to its associated creation date file.
501     *
502     * @return the time the active log file was created (in millis since the epoch) or -1 if the
503     * creation date file is not available.
504     *
505     * @throws IOException if any error occurs opening or reading and interpreting the contents of
506     * the file
507     */

508    private long
509    readCreationTime()
510    throws IOException JavaDoc
511    {
512       try
513       {
514          File JavaDoc creationTimeFile =
515             getCreationTimeFile(currentActiveLogFileDirectory, currentActiveLogFile);
516
517          if (!creationTimeFile.exists())
518             storeFileCreationTimeIfNecessary(currentActiveLogFileDirectory, currentActiveLogFile);
519
520          FileReader JavaDoc fileIn = new FileReader JavaDoc(creationTimeFile);
521          BufferedReader JavaDoc in = new BufferedReader JavaDoc(fileIn);
522          String JavaDoc firstLine = in.readLine();
523          in.close();
524          return Long.parseLong(firstLine);
525       }
526       catch (IOException JavaDoc e)
527       {
528          throw new IOException JavaDoc("Error reading creation time file: " + e);
529       }
530       catch (NumberFormatException JavaDoc e)
531       {
532          throw new IOException JavaDoc("Error reading creation time: " + e);
533       }
534    }
535
536    /**
537     * Returns the name of the creation date file associated with the specified active log file.
538     *
539     * @param newActiveLogFile the file that will be opened as the active log file
540     *
541     * @param activeLogFileDirectory the directory in which the active log file resides
542     *
543     * @return the file used to store the creation date of the active log file. It doesn't
544     * necessarily exist when this method returns.
545     */

546    private File JavaDoc
547    getCreationTimeFile(File JavaDoc activeLogFileDirectory, File JavaDoc newActiveLogFile)
548    {
549       return new File JavaDoc(activeLogFileDirectory, newActiveLogFile.getName() + "-CREATED");
550    }
551
552
553    /**
554     * Closes the file resources associated with this <code>RolloverManager</code> and releases any
555     * other resources used by it. Any errors occurring during this operation are captured and
556     * reported to the error reporter.
557     */

558    public void
559    close()
560    {
561       synchronized (WRITER_CHANGE_GATE)
562       {
563          synchronized (PRINTERS_SEMAPHORE)
564          {
565             if (printerCount != 0)
566             {
567                try
568                {
569                   PRINTERS_SEMAPHORE.wait();
570                }
571                catch (InterruptedException JavaDoc e)
572                {
573                   throw new IllegalStateException JavaDoc("Interrupted while attempting to configure writer");
574                }
575             }
576
577             writer = tempWriter;
578          }
579
580          timer.cancel();
581
582          try
583          {
584             writer.close();
585          }
586          catch (IOException JavaDoc e)
587          {
588             reportError("Error closing RolloverManager's writer", e, true);
589          }
590
591          try
592          {
593             fileOut.close();
594          }
595          catch (IOException JavaDoc e)
596          {
597             reportError("Error closing RolloverManager's file", e, true);
598          }
599       }
600    }
601
602    /**
603     * Reports the given error to the error reporter, if there is one.
604     *
605     * @param description a description of the error
606     *
607     * @param e the exception that indicates the error
608     *
609     * @param printExceptionType a flag indicating whether the type of the exception should be
610     * printed.
611     */

612    private void
613    reportError(String JavaDoc description, IOException JavaDoc e, boolean printExceptionType)
614    {
615       if (errorReporter != null)
616          errorReporter.error(description, e, printExceptionType);
617    }
618
619    /**
620     * Does nothing, as the <code>RolloverManager</code> auto-flushes after every write()
621     */

622    public void
623    flush()
624    {
625    }
626
627    /**
628     * Writes the output to the current writer and flushes it.
629     *
630     * @throws IOException if an error occurs writing to the current writer.
631     */

632    public void
633    write(char cbuf[], int off, int len)
634    throws IOException JavaDoc
635    {
636       // Writer priority:
637
// Waiting for this lock prevents new printers from starting once a Writer Change starts
638
synchronized (WRITER_CHANGE_GATE)
639       {}
640
641       // Increment the printers semaphore
642
synchronized (PRINTERS_SEMAPHORE)
643       {
644          printerCount++;
645       }
646
647       try
648       {
649          writer.write(cbuf, off, len);
650          writer.flush();
651       }
652       finally
653       {
654          // Decrement the printers semaphore and notify if we are last out
655
synchronized (PRINTERS_SEMAPHORE)
656          {
657             printerCount--;
658             if (printerCount == 0)
659             {
660                PRINTERS_SEMAPHORE.notifyAll();
661             }
662          }
663       }
664    }
665
666    /**
667     * Prompts the <code>RolloverManager</code> to check whether the log file needs to be rolled and
668     * to perform that rolling if necessary.
669     *
670     * @throws IOException if an error occurs while rolling the log file.
671     */

672    public void
673    rolloverIfNecessary()
674    throws IOException JavaDoc
675    {
676       long creationTime = readCreationTime();
677       FileChannel JavaDoc activeFileChannel = fileOut.getChannel();
678       long fileLength = activeFileChannel.size();
679       Date JavaDoc creationDate = new Date JavaDoc(creationTime);
680
681       boolean rolloverNow = strategy.rolloverNow(creationDate, fileLength);
682
683       if (rolloverNow)
684       {
685          // Switch to the temporary writer
686
synchronized (WRITER_CHANGE_GATE)
687          {
688             synchronized (PRINTERS_SEMAPHORE)
689             {
690                if (printerCount != 0)
691                {
692                   try
693                   {
694                      PRINTERS_SEMAPHORE.wait();
695                   }
696                   catch (InterruptedException JavaDoc e)
697                   {
698                      throw new IllegalStateException JavaDoc("Interrupted while attempting to configure writer");
699                   }
700                }
701
702                writer = tempWriter;
703             }
704          }
705
706          // Create the rollover file
707
int fileIdNumber = uniqueFileId++;
708          StringBuffer JavaDoc fileIdString = new StringBuffer JavaDoc();
709          fileIdString.append(Integer.toHexString(fileIdNumber).toUpperCase());
710          char[] zeroes = new char[8 - fileIdString.length()];
711          Arrays.fill(zeroes, '0');
712          fileIdString.insert(0, zeroes);
713          String JavaDoc rolloverFileName =
714             rolloverLogFileFormat.format(new Object JavaDoc[] {new Date JavaDoc(), fileIdString.toString()});
715          if (!rolloverDirectory.exists())
716             rolloverDirectory.mkdirs();
717          File JavaDoc rolloverFile = new File JavaDoc(rolloverDirectory, rolloverFileName);
718          FileOutputStream JavaDoc rolloverOut = new FileOutputStream JavaDoc(rolloverFile, false);
719
720          // Copy all the contents of the real file over to the rollover file
721
FileChannel JavaDoc rolloverChannel = rolloverOut.getChannel();
722          activeFileChannel.transferTo(0, activeFileChannel.size(), rolloverChannel);
723
724          // Close the rollover file
725
rolloverOut.close();
726
727          // Truncate the real file and create a new writer
728
activeFileChannel.truncate(0);
729          fileOut.seek(0);
730          Writer JavaDoc newFileWriter = Channels.newWriter(activeFileChannel, "UTF-8");
731
732          //Write the active file creation time
733
File JavaDoc creationTimeFile =
734             getCreationTimeFile(currentActiveLogFileDirectory, currentActiveLogFile);
735          creationTimeFile.delete();
736          storeFileCreationTimeIfNecessary(currentActiveLogFileDirectory, currentActiveLogFile);
737
738          // Switch back to the active file writer
739
synchronized (WRITER_CHANGE_GATE)
740          {
741             synchronized (PRINTERS_SEMAPHORE)
742             {
743                if (printerCount != 0)
744                {
745                   try
746                   {
747                      PRINTERS_SEMAPHORE.wait();
748                   }
749                   catch (InterruptedException JavaDoc e)
750                   {
751                      throw new IllegalStateException JavaDoc("Interrupted while attempting to configure writer");
752                   }
753                }
754
755                // Switch back to a real file writer.
756
writer = newFileWriter;
757                // Write the temporary contents to the file writer and clear it
758
StringBuffer JavaDoc tempBuffer = tempWriter.getBuffer();
759                newFileWriter.write(tempBuffer.toString());
760                tempBuffer.delete(0, tempBuffer.length());
761             }
762          }
763       }
764    }
765
766    /**
767     * Returns the {@link RolloverStrategy} object currently in use by this
768     * <code>RolloverManager</code>.
769     */

770    public RolloverStrategy
771    getStrategy()
772    {
773       return strategy;
774    }
775
776    /**
777     * Sets the {@link RolloverStrategy} to be used by this <code>RolloverManager</code>. Once the
778     * strategy has been set using this method, it will never be changed by calls to
779     * {@link #configure}, which includes auto-reloading of the properties.
780     *
781     * @param strategy the new strategy implementation to be used
782     *
783     * @throws IllegalArgumentException if <code>strategy</code> is <code>null</code>.
784     */

785    public void
786    setStrategy(RolloverStrategy strategy)
787    {
788       if (strategy == null)
789       {
790          throw new IllegalArgumentException JavaDoc("strategy cannot be null.");
791       }
792
793       this.strategy = strategy;
794       strategySetProgramatically = true;
795    }
796
797    /**
798     * A {@link TimerTask} used to initiate {@link RolloverManager#rolloverIfNecessary}.
799     */

800    private final class
801    RolloverTask
802    extends TimerTask JavaDoc
803    {
804       public void
805       run()
806       {
807          try
808          {
809             rolloverIfNecessary();
810          }
811          catch (IOException JavaDoc e)
812          {
813             reportError("SimpleLog ERROR: Failed to check or attempt rollover", e, true);
814          }
815       }
816    }
817
818    public static Writer JavaDoc
819    createRolloverManager(Properties JavaDoc properties, ErrorReporter errorReporter)
820    throws IOException JavaDoc
821    {
822       return new RolloverManager(properties, errorReporter);
823    }
824
825    /**
826     * An interface for objects wishing to be notified of errors occurring in a
827     * {@link RolloverManager} while it is running.
828     */

829    public static interface
830    ErrorReporter
831    {
832       public void
833       error(String JavaDoc description, Throwable JavaDoc t, boolean printExceptionType);
834    }
835 }
Popular Tags