KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > grlea > log > SimpleLog


1 package org.grlea.log;
2
3 // $Id: SimpleLog.java,v 1.21 2006/07/13 12:40:07 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 org.grlea.log.rollover.RolloverManager;
19
20 import java.io.File JavaDoc;
21 import java.io.FileWriter JavaDoc;
22 import java.io.IOException JavaDoc;
23 import java.io.InputStream JavaDoc;
24 import java.io.OutputStreamWriter JavaDoc;
25 import java.io.PrintWriter JavaDoc;
26 import java.io.StringWriter JavaDoc;
27 import java.io.Writer JavaDoc;
28 import java.io.UnsupportedEncodingException JavaDoc;
29 import java.lang.ref.Reference JavaDoc;
30 import java.lang.ref.ReferenceQueue JavaDoc;
31 import java.lang.ref.WeakReference JavaDoc;
32 import java.net.MalformedURLException JavaDoc;
33 import java.net.URI JavaDoc;
34 import java.net.URISyntaxException JavaDoc;
35 import java.net.URL JavaDoc;
36 import java.net.URLConnection JavaDoc;
37 import java.net.URLEncoder JavaDoc;
38 import java.text.DateFormat JavaDoc;
39 import java.text.FieldPosition JavaDoc;
40 import java.text.Format JavaDoc;
41 import java.text.MessageFormat JavaDoc;
42 import java.text.ParsePosition JavaDoc;
43 import java.text.SimpleDateFormat JavaDoc;
44 import java.util.ArrayList JavaDoc;
45 import java.util.Date JavaDoc;
46 import java.util.Enumeration JavaDoc;
47 import java.util.Iterator JavaDoc;
48 import java.util.List JavaDoc;
49 import java.util.Properties JavaDoc;
50 import java.util.Timer JavaDoc;
51 import java.util.TimerTask JavaDoc;
52 import java.util.Set JavaDoc;
53 import java.util.Map JavaDoc;
54
55 /**
56  * <p>Controls the configuration and formatting of a group of <code>SimpleLogger</code>s.</p>
57  *
58  * <p><code>SimpleLog</code> has a default instance, which is probably the only one that anyone will
59  * ever use. If you use the default instance, you don't even really need to know anything about
60  * <code>SimpleLog</code> - just use the {@link SimpleLogger#SimpleLogger(Class) basic SimpleLogger
61  * constructor} and you'll never even know nor care.</p>
62  *
63  * @version $Revision: 1.21 $
64  * @author $Author: grlea $
65  */

66 public final class
67 SimpleLog
68 {
69    // TODO (grahaml) Need a way to turn logging OFF for a package/class?
70

71    // Constants
72
//...............................................................................................
73

74    /** The name of the system property defining the location of the properties. */
75    private static final String JavaDoc CONFIG_PROPERTY = "simplelog.configuration";
76
77    /** The name of the system property for debugging Simple Log itself. */
78    private static final String JavaDoc DEV_DEBUG_PROPERTY = "simplelog.dev.debug";
79
80    /** The name of the system property printing Simple Log stack traces. */
81    private static final String JavaDoc DEV_STACKTRACES_PROPERTY = "simplelog.dev.printStackTraces";
82
83    /** The prefix used when the configuration is coming from a file. */
84    private static final String JavaDoc FILE_CONFIG_PREFIX = "file:";
85
86    /** The prefix used when the configuration is coming from the classpath. */
87    private static final String JavaDoc CLASSPATH_CONFIG_PREFIX = "classpath:";
88
89    /** The default name of the properties file used to configure a <code>SimpleLog</code>. */
90    private static final String JavaDoc DEFAULT_PROPERTIES_FILE_NAME = "simplelog.properties";
91
92    /** The prefix for all special properties keys. */
93    private static final String JavaDoc KEY_PREFIX = "simplelog.";
94
95    /** The property key for a list of files to import. */
96    private static final String JavaDoc KEY_IMPORT = KEY_PREFIX + "import";
97
98    /** The prefix for all format-related properties keys. */
99    private static final String JavaDoc KEY_FORMAT_PREFIX = KEY_PREFIX + "format.";
100
101    /** The suffix for format property keys relating to instance formats. */
102    private static final String JavaDoc KEY_FORMAT_INSTANCE_SUFFIX = ".instance";
103
104    /** The property key for the debug format. */
105    private static final String JavaDoc KEY_FORMAT_DB = KEY_FORMAT_PREFIX + "debug";
106
107    /** The property key for the debug object format. */
108    private static final String JavaDoc KEY_FORMAT_DBO = KEY_FORMAT_PREFIX + "debugObject";
109
110    /** The property key for the debug exception format. */
111    private static final String JavaDoc KEY_FORMAT_DBE = KEY_FORMAT_PREFIX + "debugException";
112
113    /** The property key for the entry format. */
114    private static final String JavaDoc KEY_FORMAT_ENTRY = KEY_FORMAT_PREFIX + "entry";
115
116    /** The property key for the exit format. */
117    private static final String JavaDoc KEY_FORMAT_EXIT = KEY_FORMAT_PREFIX + "exit";
118
119    /** The property key for properties reloading. */
120    private static final String JavaDoc KEY_RELOADING = KEY_PREFIX + "reloading";
121
122    /** The default value for the reloading properties property. */
123    private static final String JavaDoc RELOADING_DEFAULT = "false";
124
125    /** The property key for the log file name. */
126    private static final String JavaDoc KEY_LOG_FILE = KEY_PREFIX + "logFile";
127
128    /** The property key for whether the log file name should be interpreted. */
129    private static final String JavaDoc KEY_INTERPRET_NAME = KEY_LOG_FILE + ".interpretName";
130
131    /** The default value for the parse log file name property. */
132    private static final boolean INTREPRET_NAME_DEFAULT = true;
133
134    /** The property key for whether the log file should be appended. */
135    private static final String JavaDoc KEY_APPEND = KEY_LOG_FILE + ".append";
136
137    /** The default value for the append property. */
138    private static final boolean APPEND_DEFAULT = true;
139
140    /** The property key for whether output to a log file should also be printed to the console. */
141    private static final String JavaDoc KEY_PIPE_TO_CONSOLE = KEY_LOG_FILE + ".andConsole";
142
143    /** The default value for the andConsole property. */
144    private static final boolean PIPE_TO_CONSOLE_DEFAULT = false;
145
146    /** The property key for the default debug level. */
147    private static final String JavaDoc KEY_DEFAULT_LEVEL = KEY_PREFIX + "defaultLevel";
148
149    /** The property key for the default tracing flag. */
150    private static final String JavaDoc KEY_DEFAULT_TRACE = KEY_PREFIX + "defaultTrace";
151
152    /** The property key for the date format. */
153    private static final String JavaDoc KEY_DATE_FORMAT = KEY_PREFIX + "dateFormat";
154
155    /** The default date format. */
156    private static final String JavaDoc DATE_FORMAT_DEFAULT = "EEE yyyy/MM/dd HH:mm:ss.SSS";
157
158    /** The property key for the print stack traces property. */
159    private static final String JavaDoc KEY_PRINT_STACK_TRACES = KEY_PREFIX + "printStackTraces";
160
161    /** The default value for the print stack traces property. */
162    private static final String JavaDoc PRINT_STACK_TRACES_DEFAULT = "true";
163
164    /** The property key for the rollover strategy. */
165    private static final String JavaDoc KEY_ROLLOVER_STRATEGY = "simplelog.rollover";
166
167    /**
168     * The period (in milliseconds) between checks of the properties file (must be a FILE, not inside
169     * a JAR).
170     */

171    private static final int RELOAD_FILE_CHECK_PERIOD = 20 * 1000;
172
173    /**
174     * The period (in milliseconds) between checks of the properties when they're not in a file.
175     */

176    private static final int RELOAD_URL_CHECK_PERIOD = 60 * 1000;
177
178    /**
179     * The suffix used in the properties file to identify a trace flag.
180     */

181    private static final String JavaDoc TRACE_SUFFIX = "#trace";
182
183    /**
184     * The string to use to print a new line between the debug exception line and the stack trace.
185     */

186    private static final String JavaDoc LINE_SEP = System.getProperty("line.separator");
187
188    /** The class name of the RolloverManager. */
189    private static final String JavaDoc ROLLOVER_WRITER_CLASS = "org.grlea.log.rollover.RolloverManager";
190
191    // Default Message Formats
192
//..........................................................................................
193
// The following arguments are always the same:
194
// {0} - The current date and time (formatted as specified by DATE_FORMAT)
195
// {1} - The name of the current Thread.
196
// {2} - The name of the debugging class.
197
// {3} - The instance ID, if in use.
198
// {4} - The DebugLevel name
199

200    private static final String JavaDoc DEFAULT_FORMAT_STRING_DB = "{0}| |{1}|{2}|{5}";
201    private static final String JavaDoc DEFAULT_FORMAT_STRING_DBO = "{0}|---|{1}|{2}|{5}|{6}";
202    private static final String JavaDoc DEFAULT_FORMAT_STRING_DBE = "{0}|***|{1}|{2}|{5}";
203    private static final String JavaDoc DEFAULT_FORMAT_STRING_ENTRY = "{0}|>>>|{1}|{2}|{5}";
204    private static final String JavaDoc DEFAULT_FORMAT_STRING_EXIT = "{0}|<<<|{1}|{2}|{5}";
205
206    private static final String JavaDoc DEFAULT_FORMAT_STRING_DB_INSTANCE = "{0}| |{1}|{2}[{3}]|{5}";
207    private static final String JavaDoc DEFAULT_FORMAT_STRING_DBO_INSTANCE = "{0}|---|{1}|{2}[{3}]|{5}|{6}";
208    private static final String JavaDoc DEFAULT_FORMAT_STRING_DBE_INSTANCE = "{0}|***|{1}|{2}[{3}]|{5}";
209    private static final String JavaDoc DEFAULT_FORMAT_STRING_ENTRY_INSTANCE = "{0}|>>>|{1}|{2}[{3}]|{5}";
210    private static final String JavaDoc DEFAULT_FORMAT_STRING_EXIT_INSTANCE = "{0}|<<<|{1}|{2}[{3}]|{5}";
211
212    private static final int DEFAULT_FORMAT_EXCEPTION_INDEX = 5;
213    private static final int DEFAULT_FORMAT_EXCEPTION_INDEX_INSTANCE = 5;
214
215    // Static variables
216
//..........................................................................................
217

218    /**
219     * The default instance of <code>SimpleLog</code>.
220     *
221     * @see #defaultInstance()
222     */

223    private static SimpleLog defaultInstance = null;
224
225    /**
226     * An object to synchronize on when checking or changing the {@link #defaultInstance}.
227     */

228    private static final Object JavaDoc defaultInstanceLock = new Object JavaDoc();
229
230    /**
231     * Specifies whether Simple Log will output debug information about itself.
232     */

233    private static boolean devDebug = false;
234
235    static
236    {
237       try
238       {
239          String JavaDoc devDebugString = System.getProperty(DEV_DEBUG_PROPERTY);
240          if (devDebugString != null && "true".equalsIgnoreCase(devDebugString.trim()))
241          {
242             devDebug = true;
243             printDebugIfEnabled("Simple Log DEV Debugging enabled (-Dsimplelog.dev.debug)");
244          }
245       }
246       catch (Exception JavaDoc e)
247       {
248          printError("Exception while reading system property '" + DEV_DEBUG_PROPERTY + "'", e, true);
249       }
250    }
251
252    // Constants
253
//...............................................................................................
254

255    /**
256     * The URL from where this <code>SimpleLog</code> loaded (and may reload) its configuration.
257     */

258    private final URL JavaDoc configurationSource;
259
260    /** The properties governing this <code>SimpleLog</code>. */
261    private final Properties JavaDoc properties;
262
263    /** The destination of this <code>SimpleLog</code>'s output. */
264    private PrintWriter JavaDoc out;
265
266    /** The writer that the print writer is printing to. */
267    private Writer JavaDoc currentWriter;
268
269    /** Indicates whether the output writer has been set programatically. */
270    private boolean outputSetProgramatically = false;
271
272    /**
273     * The path and name of the log file being written to, or <code>null</code> if output is going
274     * somewhere else.
275     */

276    private String JavaDoc logFile;
277
278    /** Whether the PrintWriter is printing straight to the console. */
279    private boolean printWriterGoesToConsole = true;
280
281    /** Whether output is currently going to the console as well as a log file. */
282    private boolean pipingOutputToConsole = false;
283
284    /** The default level of this <code>SimpleLog</code>. */
285    private DebugLevel defaultLevel = DebugLevel.L4_INFO;
286
287    /** The default trace flag of this <code>SimpleLog</code>. */
288    private boolean defaultTracing = false;
289
290    /**
291     * The non-instance {@link SimpleLogger}s attached to this <code>SimpleLog</code>.
292     */

293    private final List JavaDoc loggers = new ArrayList JavaDoc(32);
294
295    /**
296     * A list of {@link WeakReference}s to instance {@link SimpleLogger}s attached to this
297     * <code>SimpleLog</code>.
298     */

299    private final List JavaDoc instanceLoggerReferences = new ArrayList JavaDoc(16);
300
301    /** Queue of references to SimpleLoggers now unreachable. */
302    private ReferenceQueue JavaDoc instanceLoggersReferenceQueue = null;
303
304    /** An object to synchronize on when accessing or modifying the {@link #loggers} list. */
305    private final Object JavaDoc LOGGERS_LOCK = new Object JavaDoc();
306
307    /** The date format used in all formats in this <code>SimpleLog</code>. */
308    private DateFormat JavaDoc dateFormat;
309
310    /**
311     * Message format for a simple debug message.
312     * 4 = message (String)
313     */

314    private MessageFormat JavaDoc dbFormat;
315
316    /**
317     * Message format for a debug object message.
318     * 4 = object name (String)
319     * 5 = object value (Object)
320     */

321    private MessageFormat JavaDoc dboFormat;
322
323    /**
324     * Message format for a debug exception message.
325     * 4 = exception (Throwable)
326     */

327    private MessageFormat JavaDoc dbeFormat;
328
329    /**
330     * Message format for a trace entry message.
331     * 4 = method name (String)
332     */

333    private MessageFormat JavaDoc entryFormat;
334
335    /**
336     * Message format for a trace exit message.
337     * 4 = method name (String)
338     */

339    private MessageFormat JavaDoc exitFormat;
340
341    /**
342     * Message format for a simple debug message (instance version).
343     * 4 = message (String)
344     */

345    private MessageFormat JavaDoc dbFormat4Instance;
346
347    /**
348     * Message format for a debug object message (instance version).
349     * 4 = object name (String)
350     * 5 = object value (Object)
351     */

352    private MessageFormat JavaDoc dboFormat4Instance;
353
354    /**
355     * Message format for a debug exception message (instance version).
356     * 4 = exception (Throwable)
357     */

358    private MessageFormat JavaDoc dbeFormat4Instance;
359
360    /**
361     * Message format for a trace entry message (instance version).
362     * 4 = method name (String)
363     */

364    private MessageFormat JavaDoc entryFormat4Instance;
365
366    /**
367     * Message format for a trace exit message (instance version).
368     * 4 = method name (String)
369     */

370    private MessageFormat JavaDoc exitFormat4Instance;
371
372    /**
373     * Creates a new <code>SimpleLog</code> configured by the given properties object. <br>
374     * All <code>SimpleLog</code>s log to System.err by default.
375     *
376     * @param properties a properties object containing properties that will be used to configure
377     * the <code>SimpleLog</code>.
378     *
379     * @throws IllegalArgumentException if <code>properties</code> is <code>null</code>.
380     */

381    public
382    SimpleLog(Properties JavaDoc properties)
383    {
384       if (properties == null)
385       {
386          throw new IllegalArgumentException JavaDoc("properties cannot be null.");
387       }
388
389       this.configurationSource = null;
390       this.properties = properties;
391       this.out = new PrintWriter JavaDoc(System.err, true);
392
393       readSettingsFromProperties();
394    }
395
396    /**
397     * Creates a new <code>SimpleLog</code> configured by a properties file read from the given URL.
398     * <br>
399     * All <code>SimpleLog</code>s log to System.err by default.
400     *
401     * @param configurationSource the properties file to use to configure the <code>SimpleLog</code>
402     * instance.
403     *
404     * @throws IOException if the properties file cannot be read from the given URL.
405     */

406    public
407    SimpleLog(URL JavaDoc configurationSource)
408    throws IOException JavaDoc
409    {
410       this.configurationSource = configurationSource;
411       this.properties = new Properties JavaDoc();
412       this.out = new PrintWriter JavaDoc(System.err, true);
413
414       loadProperties();
415       readSettingsFromProperties();
416
417       // Note: Reloading is only checked on creation
418
String JavaDoc reloadingString = properties.getProperty(KEY_RELOADING, RELOADING_DEFAULT);
419       boolean reloading = Boolean.valueOf(reloadingString).booleanValue();
420       if (reloading)
421       {
422          printDebugIfEnabled("Configuration reloading enabled");
423
424          Timer JavaDoc timer = new Timer JavaDoc(true);
425          TimerTask JavaDoc reloadTask;
426          int reloadPeriod;
427
428          if (configurationSource.getProtocol() != null &&
429              "file".equals(configurationSource.getProtocol().toLowerCase()))
430          {
431             reloadTask = new FileConfigurationReloader();
432             reloadPeriod = RELOAD_FILE_CHECK_PERIOD;
433          }
434          else
435          {
436             reloadTask = new UrlConfigurationReloader();
437             reloadPeriod = RELOAD_URL_CHECK_PERIOD;
438          }
439
440          timer.schedule(reloadTask, reloadPeriod, reloadPeriod);
441       }
442       else
443       {
444          printDebugIfEnabled("Configuration reloading is disabled");
445       }
446    }
447
448    /**
449     * Loads the properties file from this <code>SimpleLog</code>'s configuration source and reads
450     * all the special properties used to configure the <code>SimpleLog</code> (as opposed to those
451     * that configure the attached <code>SimpleLogger</code>s).
452     *
453     * @throws IOException if the properties file cannot be read from the URL.
454     */

455    private void
456    loadProperties()
457    throws IOException JavaDoc
458    {
459       if (properties == null)
460       {
461          return;
462       }
463
464       printDebugIfEnabled("Loading properties");
465
466       // Load the properties into a new object, then replace the current ones if the read suceeds.
467
InputStream JavaDoc inputStream = configurationSource.openStream();
468       Properties JavaDoc newProperties = new Properties JavaDoc();
469       try
470       {
471          newProperties.load(inputStream);
472       }
473       finally
474       {
475          inputStream.close();
476       }
477
478       // Import any properties files as specified
479
String JavaDoc importList = newProperties.getProperty(KEY_IMPORT);
480       if (importList != null)
481       {
482          importList = importList.trim();
483          if (importList.length() > 0)
484          {
485             String JavaDoc[] filesToImport = importList.split(",");
486             if (filesToImport != null && filesToImport.length != 0)
487             {
488                String JavaDoc configurationContext = configurationSource.toExternalForm();
489                int lastSlash = configurationContext.lastIndexOf('/');
490                lastSlash += 1;
491                configurationContext = configurationContext.substring(0, lastSlash);
492                for (int i = 0; i < filesToImport.length; i++)
493                {
494                   String JavaDoc filenameToImport = filesToImport[i];
495                   URL JavaDoc urlToImport = new URL JavaDoc(configurationContext + filenameToImport);
496                   InputStream JavaDoc importStream = null;
497                   try
498                   {
499                      printDebugIfEnabled("Importing file", urlToImport);
500                      importStream = urlToImport.openStream();
501                      newProperties.load(importStream);
502                   }
503                   catch (IOException JavaDoc e)
504                   {
505                      printError("Error importing properties file: " + filenameToImport +
506                                 "(" + urlToImport + ")", e, true);
507                   }
508                   finally
509                   {
510                      if (importStream != null)
511                         importStream.close();
512                   }
513                }
514             }
515          }
516       }
517
518       // List all loaded properties if debug is on
519
if (devDebug)
520       {
521          Set JavaDoc properties = newProperties.entrySet();
522          printDebugIfEnabled("_____ Properties List START _____");
523          for (Iterator JavaDoc iterator = properties.iterator(); iterator.hasNext();)
524          {
525             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iterator.next();
526             printDebugIfEnabled((String JavaDoc) entry.getKey(), entry.getValue());
527          }
528          printDebugIfEnabled("______ Properties List END ______");
529       }
530
531       // Replace the current properties with the loaded ones
532
properties.clear();
533       properties.putAll(newProperties);
534    }
535
536    /**
537     * Reads settings from this <code>SimpleLog</code>'s properties object and changes the object's
538     * state accordingly.
539     */

540    private void
541    readSettingsFromProperties()
542    {
543       if (!outputSetProgramatically)
544       {
545          try
546          {
547             String JavaDoc rolloverStrategyString = properties.getProperty(KEY_ROLLOVER_STRATEGY);
548             boolean useRollover =
549                rolloverStrategyString != null && rolloverStrategyString.trim().length() != 0;
550
551             Writer JavaDoc newWriter;
552
553             if (useRollover)
554             {
555                newWriter = configureRolloverWriter();
556             }
557             else
558             {
559                newWriter = configureFileWriter();
560             }
561
562             if (newWriter != currentWriter)
563             {
564                out = new PrintWriter JavaDoc(newWriter, true);
565
566                if (currentWriter != null)
567                {
568                   try
569                   {
570                      currentWriter.close();
571                   }
572                   catch (IOException JavaDoc e)
573                   {
574                      printError("Error while closing log file", e, true);
575                   }
576                }
577
578                currentWriter = newWriter;
579             }
580          }
581          catch (IOException JavaDoc e)
582          {
583             printError("Error opening log file for writing", e, true);
584          }
585       }
586
587       // Read the "andConsole" property
588
String JavaDoc pipeOutputToConsoleString = properties.getProperty(KEY_PIPE_TO_CONSOLE);
589       // The strategy here is to only turn andConsole on if the property definitely says true
590
pipingOutputToConsole = PIPE_TO_CONSOLE_DEFAULT;
591       if (pipeOutputToConsoleString != null)
592       {
593          pipingOutputToConsole = pipeOutputToConsoleString.trim().equalsIgnoreCase("true");
594       }
595
596
597       // Read the Default level
598
String JavaDoc defaultLevelStr = properties.getProperty(KEY_DEFAULT_LEVEL);
599       if (defaultLevelStr != null)
600       {
601          defaultLevelStr = defaultLevelStr.trim();
602
603          try
604          {
605             // Try to read it as an int first...
606
int level = Integer.parseInt(defaultLevelStr);
607             defaultLevel = DebugLevel.fromInt(level);
608          }
609          catch (NumberFormatException JavaDoc e1)
610          {
611             // ... then try it as a name.
612
try
613             {
614                defaultLevel = DebugLevel.fromName(defaultLevelStr);
615             }
616             catch (IllegalArgumentException JavaDoc e2)
617             {
618                printError("Error parsing debug level for '" + KEY_DEFAULT_LEVEL + "'", e1, true);
619                printError("Error parsing debug level for '" + KEY_DEFAULT_LEVEL + "'", e2, false);
620             }
621          }
622       }
623
624       // Read the Default trace
625
String JavaDoc defaultTraceStr = properties.getProperty(KEY_DEFAULT_TRACE);
626       if (defaultTraceStr != null)
627       {
628          defaultTracing = Boolean.valueOf(defaultTraceStr).booleanValue();
629       }
630
631       // Read the Date format
632
String JavaDoc dateFormatString = properties.getProperty(KEY_DATE_FORMAT, DATE_FORMAT_DEFAULT);
633       try
634       {
635          dateFormat = new SimpleDateFormat JavaDoc(dateFormatString);
636       }
637       catch (IllegalArgumentException JavaDoc e)
638       {
639          printError("Error parsing date format", e, false);
640       }
641
642       // Read formats
643
dbFormat = readFormat(KEY_FORMAT_DB, DEFAULT_FORMAT_STRING_DB);
644       dboFormat = readFormat(KEY_FORMAT_DBO, DEFAULT_FORMAT_STRING_DBO);
645       dbeFormat = readFormat(KEY_FORMAT_DBE, DEFAULT_FORMAT_STRING_DBE);
646       entryFormat = readFormat(KEY_FORMAT_ENTRY, DEFAULT_FORMAT_STRING_ENTRY);
647       exitFormat = readFormat(KEY_FORMAT_EXIT, DEFAULT_FORMAT_STRING_EXIT);
648
649       dbFormat4Instance =
650          readFormat(KEY_FORMAT_DB + KEY_FORMAT_INSTANCE_SUFFIX, DEFAULT_FORMAT_STRING_DB_INSTANCE);
651       dboFormat4Instance =
652          readFormat(KEY_FORMAT_DBO + KEY_FORMAT_INSTANCE_SUFFIX, DEFAULT_FORMAT_STRING_DBO_INSTANCE);
653       dbeFormat4Instance =
654          readFormat(KEY_FORMAT_DBE + KEY_FORMAT_INSTANCE_SUFFIX, DEFAULT_FORMAT_STRING_DBE_INSTANCE);
655       entryFormat4Instance =
656          readFormat(KEY_FORMAT_ENTRY + KEY_FORMAT_INSTANCE_SUFFIX,
657                     DEFAULT_FORMAT_STRING_ENTRY_INSTANCE);
658       exitFormat4Instance =
659          readFormat(KEY_FORMAT_EXIT + KEY_FORMAT_INSTANCE_SUFFIX,
660                     DEFAULT_FORMAT_STRING_EXIT_INSTANCE);
661
662       updateDateFormats();
663
664       // Read the Print stack traces property
665
String JavaDoc printStackTracesStr =
666          properties.getProperty(KEY_PRINT_STACK_TRACES, PRINT_STACK_TRACES_DEFAULT);
667       boolean printStackTraces = Boolean.valueOf(printStackTracesStr).booleanValue();
668       if (printStackTraces)
669       {
670          ExceptionFormat exceptionFormat = new ExceptionFormat();
671          dbeFormat.setFormatByArgumentIndex(DEFAULT_FORMAT_EXCEPTION_INDEX, exceptionFormat);
672          dbeFormat4Instance.setFormatByArgumentIndex(
673             DEFAULT_FORMAT_EXCEPTION_INDEX_INSTANCE, exceptionFormat);
674       }
675
676       // Copy any properties that have '$' in their name
677
Enumeration JavaDoc propertyNames = properties.propertyNames();
678       Properties JavaDoc newProperties = new Properties JavaDoc();
679       while (propertyNames.hasMoreElements())
680       {
681          String JavaDoc key = (String JavaDoc) propertyNames.nextElement();
682          if (key.indexOf('$') != -1)
683          {
684             newProperties.put(key.replace('$', '.'), properties.getProperty(key));
685          }
686       }
687       properties.putAll(newProperties);
688    }
689
690    /**
691     * Configures this <code>SimpleLog</code> to use a plain FileWriter, according to the properties
692     * in the current properties object.
693     *
694     * @return the writer to be used to write log output.
695     */

696    private Writer JavaDoc
697    configureFileWriter()
698    throws IOException JavaDoc
699    {
700       Writer JavaDoc writer;
701       // Read the log file name
702
String JavaDoc newLogFile = properties.getProperty(KEY_LOG_FILE);
703
704       boolean interpretName = INTREPRET_NAME_DEFAULT;
705       String JavaDoc interpretNameStr = properties.getProperty(KEY_INTERPRET_NAME);
706       // The strategy here is to only turn interpret off if the property definitely says false
707
if (interpretNameStr != null)
708       {
709          interpretName = !(interpretNameStr.trim().equalsIgnoreCase("false"));
710       }
711
712       // Substitue the date into the name if necessary
713
boolean newLogFileNotNull = newLogFile != null;
714       boolean nameContainsBraces = newLogFileNotNull && newLogFile.indexOf('{') != -1;
715       if (newLogFileNotNull && nameContainsBraces && interpretName)
716       {
717          try
718          {
719             MessageFormat JavaDoc logFileNameFormat = new MessageFormat JavaDoc(newLogFile);
720             newLogFile = logFileNameFormat.format(new Object JavaDoc[] {new Date JavaDoc()});
721          }
722          catch (Exception JavaDoc e)
723          {
724             printError("Error generating log file name", e, true);
725             newLogFile = null;
726          }
727       }
728
729       // The log file has changed if it used to be null and now isn't, or vice versa...
730
boolean logFileChanged = (logFile == null) != (newLogFile == null);
731
732       // or it's changed if it wasn't null and still isn't null but the name has changed.
733
logFileChanged |= logFile != null && newLogFileNotNull && !newLogFile.equals(logFile);
734
735       // or it's changed if rollover was on before (but it's now off if we're in here)
736
boolean rolloverWasInUse =
737          currentWriter != null &&
738          currentWriter.getClass().getName().equals(ROLLOVER_WRITER_CLASS);
739
740       logFileChanged |= rolloverWasInUse;
741
742       if (logFileChanged)
743       {
744          if (newLogFile == null)
745          {
746             writer = new OutputStreamWriter JavaDoc(System.err);
747             printWriterGoesToConsole = true;
748          }
749          else
750          {
751             File JavaDoc file = new File JavaDoc(newLogFile).getAbsoluteFile();
752
753             if (file.isDirectory())
754             {
755                throw new IOException JavaDoc(
756                   "The specified log file name already exists as a directory.");
757             }
758
759             File JavaDoc parentFile = file.getParentFile();
760             if (parentFile != null)
761             {
762                parentFile.mkdirs();
763             }
764
765             boolean append = APPEND_DEFAULT;
766             String JavaDoc appendStr = properties.getProperty(KEY_APPEND);
767             // The strategy here is to only turn append off if the property definitely says
768
// false
769
if (appendStr != null)
770             {
771                append = !(appendStr.trim().equalsIgnoreCase("false"));
772             }
773
774             writer = new FileWriter JavaDoc(file, append);
775
776             printWriterGoesToConsole = false;
777          }
778
779          logFile = newLogFile;
780       }
781       else
782       {
783          writer = currentWriter;
784       }
785
786       return writer;
787    }
788
789    /**
790     * Configures this <code>SimpleLog</code> to use a {@link RolloverManager}, according to the
791     * properties in the current properties object.
792     *
793     * @return the writer to be used to write log output.
794     */

795    private Writer JavaDoc
796    configureRolloverWriter()
797    throws IOException JavaDoc
798    {
799       // Test we've got the RolloverManager class, so we can throw something really meaningful.
800
try
801       {
802          Class.forName(ROLLOVER_WRITER_CLASS);
803       }
804       catch (ClassNotFoundException JavaDoc e)
805       {
806          throw new IOException JavaDoc("The RolloverManager class is not available: " + e);
807       }
808       catch (Throwable JavaDoc t)
809       {
810          // Ignore anything else that might be thrown (e.g. SecurityException).
811
}
812
813       Writer JavaDoc writer;
814       if (currentWriter != null && currentWriter.getClass().getName().equals(ROLLOVER_WRITER_CLASS))
815       {
816          ((RolloverManager) currentWriter).configure(properties);
817          writer = currentWriter;
818       }
819       else
820       {
821          writer = RolloverManager.createRolloverManager(properties, ErrorReporter.create());
822       }
823
824       printWriterGoesToConsole = false;
825
826       return writer;
827    }
828
829    /**
830     * Reloads this <code>SimpleLog</code>'s configuration.
831     */

832    public void
833    reloadProperties()
834    {
835       try
836       {
837          if (configurationSource != null)
838          {
839             loadProperties();
840          }
841
842          readSettingsFromProperties();
843          reconfigureAllLoggers();
844       }
845       catch (Exception JavaDoc e)
846       {
847          printError("Falied to reload properties", e, true);
848       }
849    }
850
851    /**
852     * Reads a format from a specified key.
853     *
854     * @param key the key to read from the properties to obtain a new message format string.
855     *
856     * @param defaultPattern the default pattern to use if the property doesn't exist.
857     *
858     * @return a new MessageFormat, created from the resulting message format string.
859     */

860    private MessageFormat JavaDoc
861    readFormat(String JavaDoc key, String JavaDoc defaultPattern)
862    {
863       String JavaDoc formatString = properties.getProperty(key, defaultPattern);
864       MessageFormat JavaDoc format;
865       try
866       {
867          format = new MessageFormat JavaDoc(formatString);
868       }
869       catch (Exception JavaDoc e)
870       {
871          printError("Error reading format string from " + key, e, false);
872          format = new MessageFormat JavaDoc(defaultPattern);
873       }
874       return format;
875    }
876
877    /**
878     * <p>Returns the default instance of <code>SimpleLog</code>.</p>
879     *
880     * <p>The default instance is either configured from a properties file found on the classpath
881     * with the name 'simplelog.properties', or not configured at all (if the file cannot be found).
882     * <br>If the instance is not configured at all, it will not produce any output unless a writer
883     * is programmatically assigned to it (see {@link #setWriter}).</p>
884     *
885     * @return the default instance of <code>SimpleLog</code>.
886     */

887    public static SimpleLog
888    defaultInstance()
889    {
890       synchronized (defaultInstanceLock)
891       {
892          if (defaultInstance == null)
893          {
894             printDebugIfEnabled("Creating default SimpleLog instance");
895
896             URL JavaDoc propertiesUrl = getPropertiesUrl();
897
898             if (propertiesUrl != null)
899             {
900                printDebugIfEnabled("Attempting to configure using properties at", propertiesUrl);
901                try
902                {
903                   defaultInstance = new SimpleLog(propertiesUrl);
904                }
905                catch (Exception JavaDoc e)
906                {
907                   printError("Error while attempting to load default properties", e, true);
908                }
909             }
910
911             if (defaultInstance == null)
912             {
913                printDebugIfEnabled("");
914                printDebugIfEnabled("FAILED to load any SimpleLog configuration.");
915                printDebugIfEnabled("");
916                printDebugIfEnabled("NO LOG OUTPUT WILL BE GENERATED.");
917                defaultInstance = new SimpleLog(new Properties JavaDoc());
918                defaultInstance.setWriter(null);
919             }
920          }
921       }
922       return defaultInstance;
923    }
924
925    private static URL JavaDoc
926    getPropertiesUrl()
927    {
928       // Read the system property
929
String JavaDoc propertiesDefinition = null;
930       try
931       {
932          propertiesDefinition = System.getProperty(CONFIG_PROPERTY);
933       }
934       catch (SecurityException JavaDoc e)
935       {
936          printError("SecurityException while trying to read system property", e, true);
937       }
938       printDebugIfEnabled("System property '" + CONFIG_PROPERTY + "'", propertiesDefinition);
939
940       URL JavaDoc propertiesUrl = null;
941       if (propertiesDefinition != null)
942       {
943          // File
944
if (propertiesDefinition.startsWith(FILE_CONFIG_PREFIX))
945          {
946             String JavaDoc propertiesLocation =
947                propertiesDefinition.substring(FILE_CONFIG_PREFIX.length());
948
949             File JavaDoc propertiesFile = new File JavaDoc(propertiesLocation);
950
951             if (propertiesFile.exists())
952             {
953                try
954                {
955                   propertiesUrl = propertiesFile.toURL();
956                }
957                catch (MalformedURLException JavaDoc e)
958                {
959                   printError("Error creating URL from filename '" + propertiesLocation + "'", e,
960                              false);
961                }
962             }
963             else
964             {
965                printError("Properties file not found at '" + propertiesLocation + "'");
966             }
967          }
968          // Classpath
969
else if (propertiesDefinition.startsWith(CLASSPATH_CONFIG_PREFIX))
970          {
971             String JavaDoc propertiesLocation =
972                propertiesDefinition.substring(CLASSPATH_CONFIG_PREFIX.length());
973
974             propertiesUrl = SimpleLog.class.getClassLoader().getResource(propertiesLocation);
975             if (propertiesUrl == null)
976                printError("Properties not found in classpath at '" + propertiesLocation + "'");
977          }
978          // Junk
979
else
980          {
981             printError(CONFIG_PROPERTY + " property must begin with '" + FILE_CONFIG_PREFIX +
982                        "' or '" + CLASSPATH_CONFIG_PREFIX + "' ('" + propertiesDefinition + "')");
983          }
984       }
985
986       // Default: simplelog.properties in the root of the classpath
987
if (propertiesUrl == null)
988       {
989          printDebugIfEnabled(
990             "Attempting to load default properties (simplelog.properties) from classpath");
991          propertiesUrl = SimpleLog.class.getClassLoader().getResource(DEFAULT_PROPERTIES_FILE_NAME);
992       }
993
994       // Encode any spaces in the URL
995
if (propertiesUrl != null && propertiesUrl.toExternalForm().indexOf(' ') != -1)
996       {
997          try
998          {
999             propertiesUrl = new URL JavaDoc(propertiesUrl.toString().replaceAll(" ", "%20"));
1000         }
1001         catch (MalformedURLException JavaDoc e)
1002         {
1003            printError("Failed to encode spaces in properties URL", e, true);
1004         }
1005      }
1006
1007      return propertiesUrl;
1008   }
1009
1010   /**
1011    * Prints the given string to this <code>SimpleLog</code>'s output destination, followed by a
1012    * newline sequence.
1013    *
1014    * @param s the string to print
1015    *
1016    * @see PrintWriter#println(String)
1017    */

1018   void
1019   println(String JavaDoc s)
1020   {
1021      if (out != null)
1022      {
1023         out.println(s);
1024         if (!printWriterGoesToConsole && pipingOutputToConsole)
1025         {
1026            System.err.println(s);
1027         }
1028      }
1029   }
1030
1031   /**
1032    * Configures the given logger by setting its debug level and trace flag according to the current
1033    * properties.
1034    *
1035    * @param logger the logger to configure.
1036    */

1037   void
1038   configure(SimpleLogger logger)
1039   {
1040      logger.setDebugLevel(getDebugLevel(logger));
1041      logger.setTracing(getTracingFlag(logger));
1042   }
1043
1044   /**
1045    * Retrieves the debug level for the given class from the properties.
1046    *
1047    * @param logger the logger for which to retrieve the debug level
1048    *
1049    * @return the debug level to be used for the class.
1050    */

1051   private DebugLevel
1052   getDebugLevel(SimpleLogger logger)
1053   {
1054      if (properties == null)
1055      {
1056         return defaultLevel;
1057      }
1058
1059      String JavaDoc loggerConfigName = logger.getConfigName();
1060
1061      int dotIndex = loggerConfigName.length();
1062      DebugLevel debugLevel = null;
1063      do
1064      {
1065         // On first iteration, this substring() call returns the whole string.
1066
// On subsequent iterations, it removes everything after and including the last period.
1067
loggerConfigName = loggerConfigName.substring(0, dotIndex);
1068         String JavaDoc value = properties.getProperty(loggerConfigName);
1069         if (value != null)
1070         {
1071            value = value.trim();
1072
1073            try
1074            {
1075               // Try to read it as an int first...
1076
int level = Integer.parseInt(value);
1077               debugLevel = DebugLevel.fromInt(level);
1078            }
1079            catch (NumberFormatException JavaDoc e1)
1080            {
1081               // ... then try it as a loggerConfigName.
1082
try
1083               {
1084                  debugLevel = DebugLevel.fromName(value);
1085               }
1086               catch (IllegalArgumentException JavaDoc e2)
1087               {
1088                  printError("Error parsing debug level for '" + loggerConfigName + "'", e1, true);
1089                  printError("Error parsing debug level for '" + loggerConfigName + "'", e2, false);
1090               }
1091            }
1092         }
1093
1094         dotIndex = loggerConfigName.lastIndexOf('.');
1095      }
1096      while (debugLevel == null && dotIndex != -1);
1097
1098      // If we found no level, use the default.
1099
if (debugLevel == null)
1100      {
1101         debugLevel = defaultLevel;
1102      }
1103
1104      return debugLevel;
1105   }
1106
1107   /**
1108    * Retrieves the tracing flag for the given class from the properties.
1109    *
1110    * @param logger the logger for which to retrieve the debug level
1111    *
1112    * @return the tracing flag to be used for the class.
1113    */

1114   private boolean
1115   getTracingFlag(SimpleLogger logger)
1116   {
1117      if (properties == null)
1118      {
1119         return defaultTracing;
1120      }
1121
1122      String JavaDoc loggerConfigName = logger.getConfigName();
1123
1124      int dotIndex = loggerConfigName.length();
1125      boolean trace = defaultTracing;
1126      do
1127      {
1128         loggerConfigName = loggerConfigName.substring(0, dotIndex);
1129         String JavaDoc value = properties.getProperty(loggerConfigName + TRACE_SUFFIX);
1130         if (value != null)
1131         {
1132            trace = Boolean.valueOf(value).booleanValue();
1133            break;
1134         }
1135
1136         dotIndex = loggerConfigName.lastIndexOf('.');
1137      }
1138      while (dotIndex != -1);
1139
1140      return trace;
1141   }
1142
1143   /**
1144    * Registers the give logger with this <code>SimpleLog</code>, so that the logger can be updaed
1145    * if the properties change.
1146    *
1147    * @param logger the logger to register
1148    */

1149   void
1150   register(SimpleLogger logger)
1151   {
1152      synchronized (LOGGERS_LOCK)
1153      {
1154         if (!logger.isInstanceDebugger())
1155         {
1156            loggers.add(logger);
1157         }
1158         else
1159         {
1160            // Instance loggers get weak referenced, because they're much more likely to be GC'd.
1161
if (instanceLoggersReferenceQueue == null)
1162            {
1163               createInstanceLoggersReferenceQueue();
1164            }
1165            instanceLoggerReferences.add(new WeakReference JavaDoc(logger, instanceLoggersReferenceQueue));
1166         }
1167      }
1168      configure(logger);
1169   }
1170
1171   /**
1172    * Creates the {@link ReferenceQueue} for instance loggers and starts a daemon thread to clear
1173    * queue.
1174    */

1175   private void
1176   createInstanceLoggersReferenceQueue()
1177   {
1178      instanceLoggersReferenceQueue = new ReferenceQueue JavaDoc();
1179      Thread JavaDoc thread = new Thread JavaDoc(new Runnable JavaDoc()
1180      {
1181         public void
1182         run()
1183         {
1184            while (true)
1185            {
1186               try
1187               {
1188                  Thread.yield();
1189                  Reference JavaDoc reference = instanceLoggersReferenceQueue.remove();
1190                  synchronized (LOGGERS_LOCK)
1191                  {
1192                     instanceLoggerReferences.remove(reference);
1193                  }
1194               }
1195               catch (Throwable JavaDoc t)
1196               {
1197                  // Ignore - who cares?
1198
}
1199            }
1200         }
1201      }, "SimpleLog Instance Logger Cleaner");
1202      thread.setDaemon(true);
1203      thread.setPriority(Thread.MIN_PRIORITY);
1204      thread.start();
1205   }
1206
1207   /**
1208    * Re-configures all {@link SimpleLogger}s registered with this <code>SimpleLog</code>.
1209    */

1210   private void
1211   reconfigureAllLoggers()
1212   {
1213      synchronized (LOGGERS_LOCK)
1214      {
1215         printDebugIfEnabled("Re-configuring all loggers");
1216
1217         for (Iterator JavaDoc iter = loggers.iterator(); iter.hasNext();)
1218         {
1219            configure((SimpleLogger) iter.next());
1220         }
1221
1222         for (Iterator JavaDoc iter = instanceLoggerReferences.iterator(); iter.hasNext();)
1223         {
1224            Reference JavaDoc loggerReference = (Reference JavaDoc) iter.next();
1225            SimpleLogger logger = (SimpleLogger) loggerReference.get();
1226            if (logger != null)
1227               configure(logger);
1228         }
1229      }
1230   }
1231
1232   /**
1233    * Prints the given debug message to System.err if Simple Log dev debugging is enabled.
1234    *
1235    * @param message a message to print
1236    */

1237   private static void
1238   printDebugIfEnabled(String JavaDoc message)
1239   {
1240      if (devDebug)
1241         System.err.println("SimpleLog [dev.debug]: " + message);
1242   }
1243
1244   /**
1245    * Prints the given debug message and object value to System.err if Simple Log dev debugging is
1246    * enabled.
1247    *
1248    * @param message a message to print
1249    *
1250    * @param value an object to print
1251    */

1252   private static void
1253   printDebugIfEnabled(String JavaDoc message, Object JavaDoc value)
1254   {
1255      if (devDebug)
1256         System.err.println("SimpleLog [dev.debug]: " + message + ": " + value);
1257   }
1258
1259   /**
1260    * Prints an error message from this <code>SimpleLog</code>.
1261    *
1262    * @param description a description the error
1263    */

1264   private static void
1265   printError(String JavaDoc description)
1266   {
1267      printError(description, null, false);
1268   }
1269
1270   /**
1271    * Prints an error message from this <code>SimpleLog</code>.
1272    *
1273    * @param description a description the error
1274    * @param error the exception that occurred to cause the error (may be <code>null</code>)
1275    * @param printExceptionType whether the whole toString of the exception should be printed (true)
1276    * of just the exception's 'message' (false).
1277    */

1278   private static void
1279   printError(String JavaDoc description, Throwable JavaDoc error, boolean printExceptionType)
1280   {
1281      boolean printStackTraces = false;
1282      try
1283      {
1284         String JavaDoc printStackTracesStr = System.getProperty(DEV_STACKTRACES_PROPERTY);
1285         printStackTraces = printStackTracesStr != null &&
1286                            printStackTracesStr.trim().equalsIgnoreCase("true");
1287      }
1288      catch (SecurityException JavaDoc e)
1289      {
1290         // Ignore SecurityExceptions from trying to read system properties
1291
}
1292
1293      synchronized (System.err)
1294      {
1295         System.err.println();
1296
1297         System.err.print(" SimpleLog ERROR: ");
1298         System.err.print(description);
1299
1300         if (error != null)
1301         {
1302            System.err.print(": ");
1303            if (printExceptionType)
1304            {
1305               System.err.println(error);
1306            }
1307            else
1308            {
1309               System.err.println(error.getMessage());
1310            }
1311
1312            if (printStackTraces)
1313            {
1314               try
1315               {
1316                  System.err.println();
1317                  error.printStackTrace(System.err);
1318               }
1319               catch (SecurityException JavaDoc e)
1320               {
1321                  // Ignore SecurityExceptions from trying to print stack traces
1322
}
1323            }
1324
1325            System.err.println();
1326         }
1327         else
1328         {
1329            System.err.println();
1330            System.err.println();
1331         }
1332      }
1333   }
1334
1335   /**
1336    * Returns this <code>SimpleLog</code>'s default level.
1337    */

1338   public DebugLevel
1339   getDefaultLevel()
1340   {
1341      return defaultLevel;
1342   }
1343
1344   /**
1345    * Sets this <code>SimpleLog</code>'s default level.
1346    *
1347    * @param defaultLevel the new default debug level
1348    *
1349    * @throws IllegalArgumentException if defaultLevel is null.
1350    */

1351   public void
1352   setDefaultLevel(DebugLevel defaultLevel)
1353   {
1354      if (defaultLevel == null)
1355      {
1356         throw new IllegalArgumentException JavaDoc("defaultLevel cannot be null.");
1357      }
1358      this.defaultLevel = defaultLevel;
1359      reconfigureAllLoggers();
1360   }
1361
1362   /**
1363    * Returns this <code>SimpleLog</code>'s default tracing flag.
1364    */

1365   public boolean
1366   isDefaultTracing()
1367   {
1368      return defaultTracing;
1369   }
1370
1371   /**
1372    * Sets this <code>SimpleLog</code>'s default tracing flag.
1373    *
1374    * @param defaultTracing the new default trace value (<code>true</code> for on, <code>false</code>
1375    * for off).
1376    */

1377   public void
1378   setDefaultTracing(boolean defaultTracing)
1379   {
1380      this.defaultTracing = defaultTracing;
1381      reconfigureAllLoggers();
1382   }
1383
1384   /**
1385    * Returns this <code>SimpleLog</code>'s default date format.
1386    */

1387   public DateFormat JavaDoc
1388   getDateFormat()
1389   {
1390      return dateFormat;
1391   }
1392
1393   /**
1394    * Sets this <code>SimpleLog</code>'s default date format. If the given value is
1395    * <code>null</code>, the default date format will be used.
1396    *
1397    * @param newDateFormat the new date format. May be <code>null</code>.
1398    */

1399   public void
1400   setDateFormat(DateFormat JavaDoc newDateFormat)
1401   {
1402      if (newDateFormat == null)
1403      {
1404         dateFormat = new SimpleDateFormat JavaDoc(DATE_FORMAT_DEFAULT);
1405      }
1406      else
1407      {
1408         dateFormat = newDateFormat;
1409      }
1410
1411      updateDateFormats();
1412   }
1413
1414   /**
1415    * Returns the print writer that is the destination for all of this <code>SimpleLog</code>'s
1416    * output.
1417    *
1418    * @return the print writer being used by this <code>SimpleLog</code>, or <code>null</code> if
1419    * it doesn't have one.
1420    */

1421   public PrintWriter JavaDoc
1422   getWriter()
1423   {
1424      return out;
1425   }
1426
1427   /**
1428    * Sets the print writer to be used as the destination for all of this <code>SimpleLog</code>'s
1429    * output.
1430    *
1431    * @param out the print to be used by this <code>SimpleLog</code>, or <code>null</code> if
1432    * it should not create any output.
1433    */

1434   public void
1435   setWriter(PrintWriter JavaDoc out)
1436   {
1437      this.out = out;
1438      this.outputSetProgramatically = true;
1439   }
1440
1441   /**
1442    * Returns whether this <code>SimpleLog</code> is currently printing output at all.
1443    */

1444   boolean
1445   isOutputting()
1446   {
1447      return out != null;
1448   }
1449
1450   /**
1451    * Updates the date format in all of this <code>SimpleLog</code>'s message formats.
1452    */

1453   private void
1454   updateDateFormats()
1455   {
1456      dbFormat.setFormatByArgumentIndex(0, dateFormat);
1457      dboFormat.setFormatByArgumentIndex(0, dateFormat);
1458      dbeFormat.setFormatByArgumentIndex(0, dateFormat);
1459      entryFormat.setFormatByArgumentIndex(0, dateFormat);
1460      exitFormat.setFormatByArgumentIndex(0, dateFormat);
1461
1462      dbFormat4Instance.setFormatByArgumentIndex(0, dateFormat);
1463      dboFormat4Instance.setFormatByArgumentIndex(0, dateFormat);
1464      dbeFormat4Instance.setFormatByArgumentIndex(0, dateFormat);
1465      entryFormat4Instance.setFormatByArgumentIndex(0, dateFormat);
1466      exitFormat4Instance.setFormatByArgumentIndex(0, dateFormat);
1467   }
1468
1469   MessageFormat JavaDoc
1470   getDebugFormat()
1471   {
1472      return dbFormat;
1473   }
1474
1475   MessageFormat JavaDoc
1476   getDebugInstanceFormat()
1477   {
1478      return dbFormat4Instance;
1479   }
1480
1481   MessageFormat JavaDoc
1482   getDebugObjectFormat()
1483   {
1484      return dboFormat;
1485   }
1486
1487   MessageFormat JavaDoc
1488   getDebugObjectInstanceFormat()
1489   {
1490      return dboFormat4Instance;
1491   }
1492
1493   MessageFormat JavaDoc
1494   getDebugExceptionFormat()
1495   {
1496      return dbeFormat;
1497   }
1498
1499   MessageFormat JavaDoc
1500   getDebugExceptionInstanceFormat()
1501   {
1502      return dbeFormat4Instance;
1503   }
1504
1505   MessageFormat JavaDoc
1506   getEntryFormat()
1507   {
1508      return entryFormat;
1509   }
1510
1511   MessageFormat JavaDoc
1512   getEntryInstanceFormat()
1513   {
1514      return entryFormat4Instance;
1515   }
1516
1517   MessageFormat JavaDoc
1518   getExitFormat()
1519   {
1520      return exitFormat;
1521   }
1522
1523   MessageFormat JavaDoc
1524   getExitInstanceFormat()
1525   {
1526      return exitFormat4Instance;
1527   }
1528
1529   /**
1530    * Returns whether this <code>SimpleLog</code>, when printing output to a file, will also print
1531    * the output to the console. This attribute has no effect when output is not going to a file.
1532    *
1533    * @return <code>true</code> if output will be piped to the console, <code>false</code> if it
1534    * won't.
1535    */

1536   public boolean
1537   isPipingOutputToConsole()
1538   {
1539      return pipingOutputToConsole;
1540   }
1541
1542   /**
1543    * Sets whether this <code>SimpleLog</code>, when printing output to a file, should also print
1544    * the output to the console. This attribute has no effect when output is not going to a file.
1545    *
1546    * @param pipeOutputToConsole <code>true</code> if output should be piped to the console as well
1547    * as the output file, <code>false</code> if it should not.
1548    */

1549   public void
1550   setPipingOutputToConsole(boolean pipeOutputToConsole)
1551   {
1552      this.pipingOutputToConsole = pipeOutputToConsole;
1553   }
1554
1555   /**
1556    * Format implementation that prints the stack trace of an exception after the exception object's
1557    * toString.
1558    */

1559   private static final class
1560   ExceptionFormat
1561   extends Format JavaDoc
1562   {
1563      public StringBuffer JavaDoc
1564      format(Object JavaDoc obj, StringBuffer JavaDoc buf, FieldPosition JavaDoc pos)
1565      {
1566         if (!(obj instanceof Throwable JavaDoc))
1567         {
1568            throw new IllegalArgumentException JavaDoc(getClass().getName() + " only formats Throwables.");
1569         }
1570
1571         Throwable JavaDoc t = (Throwable JavaDoc) obj;
1572         buf.append(t);
1573
1574         // Append the stack trace to the buffer.
1575
StringWriter JavaDoc sw;
1576         t.printStackTrace(new PrintWriter JavaDoc(sw = new StringWriter JavaDoc()));
1577         buf.append(LINE_SEP).append(sw.toString());
1578
1579         return buf;
1580      }
1581
1582      public Object JavaDoc
1583      parseObject (String JavaDoc source, ParsePosition JavaDoc pos)
1584      {
1585         // Parsing not supported.
1586
throw new UnsupportedOperationException JavaDoc();
1587      }
1588   }
1589
1590   /**
1591    * A {@link TimerTask} that checks to see if the last modified date of the configuration source
1592    * (which must be a "file" URL) has changed and, if it has, reloads the <code>SimpleLog</code>'s
1593    * configuration.
1594    */

1595   private final class
1596   FileConfigurationReloader
1597   extends TimerTask JavaDoc
1598   {
1599      private final File JavaDoc configurationFile;
1600
1601      private long previousLastModified;
1602
1603      public
1604      FileConfigurationReloader()
1605      {
1606         try
1607         {
1608            URI JavaDoc uri = new URI JavaDoc(configurationSource.toExternalForm());
1609            this.configurationFile = new File JavaDoc(uri);
1610            this.previousLastModified = configurationFile.lastModified();
1611         }
1612         catch (URISyntaxException JavaDoc e)
1613         {
1614            throw new IllegalArgumentException JavaDoc("Failed to create URI from URL due to " + e);
1615         }
1616      }
1617
1618      public void
1619      run()
1620      {
1621         long lastModified = configurationFile.lastModified();
1622         if (previousLastModified != lastModified)
1623         {
1624            reloadProperties();
1625            previousLastModified = lastModified;
1626         }
1627      }
1628   }
1629
1630   /**
1631    * A {@link TimerTask} that checks to see if the last modified date of the configuration source
1632    * has changed and, if it has, reloads the <code>SimpleLog</code>'s configuration. For
1633    * configuration sources that cannot provide a last modified date, the properties in the
1634    * configuration source are loaded on every iteration and compared to the current properties.
1635    */

1636   private final class
1637   UrlConfigurationReloader
1638   extends TimerTask JavaDoc
1639   {
1640      private long previousLastModified;
1641
1642      public
1643      UrlConfigurationReloader()
1644      {
1645         try
1646         {
1647            URLConnection JavaDoc connection = configurationSource.openConnection();
1648            previousLastModified = connection.getLastModified();
1649         }
1650         catch (IOException JavaDoc e)
1651         {
1652            previousLastModified = 0;
1653         }
1654      }
1655
1656      public void
1657      run()
1658      {
1659         long lastModified;
1660         try
1661         {
1662            URLConnection JavaDoc connection = configurationSource.openConnection();
1663            lastModified = connection.getLastModified();
1664         }
1665         catch (IOException JavaDoc e)
1666         {
1667            lastModified = 0;
1668         }
1669
1670         if (lastModified == 0)
1671         {
1672            // Can't get a date, so read the properties and compare
1673
Properties JavaDoc currentProperties = new Properties JavaDoc();
1674            try
1675            {
1676               InputStream JavaDoc inputStream = configurationSource.openStream();
1677               currentProperties.load(inputStream);
1678               if (!currentProperties.equals(properties))
1679               {
1680                  reloadProperties();
1681               }
1682            }
1683            catch (IOException JavaDoc e)
1684            {
1685               // Fail silently! (My pet hate)
1686
}
1687         }
1688         else if (previousLastModified != lastModified)
1689         {
1690            reloadProperties();
1691            previousLastModified = lastModified;
1692         }
1693      }
1694   }
1695
1696   private static class
1697   ErrorReporter
1698   implements RolloverManager.ErrorReporter
1699   {
1700      public void
1701      error(String JavaDoc description, Throwable JavaDoc t, boolean printExceptionType)
1702      {
1703         printError(description, t, printExceptionType);
1704      }
1705
1706      private static RolloverManager.ErrorReporter
1707      create()
1708      {
1709         return new ErrorReporter();
1710      }
1711   }
1712}
1713
Popular Tags