001: package migration.modules.rules;
002:
003: import java.io.*;
004: import java.util.*;
005: import java.text.*;
006:
007: public class Debug {
008: /** flags the disabled debug state. */
009: public static final int OFF = 0;
010:
011: /** flags the state where error debugging is enabled. When debugging is set
012: * to less than <code>ERROR</code>, error debugging is also disabled.
013: */
014: public static final int ERROR = 1;
015:
016: /** flags the state where warning debugging is enabled, but message
017: * debugging is disabled. When debugging is set to less than
018: * <code>WARNING</code>, warning debugging is also disabled.
019: */
020: public static final int WARNING = 2;
021:
022: /** This state enables debugging of messages, warnings and errors. */
023: public static final int MESSAGE = 3;
024:
025: /** flags the enabled debug state for warnings, errors and messages.
026: * Printing to a file is disabled. All printing is done on System.out.
027: */
028: public static final int ON = 4;
029:
030: /** debugMap is a container of all active Debug objects. Log file name is
031: * the key and Debug is the value of this map.
032: */
033: private static Map debugMap = new HashMap();
034:
035: /** serviceInitialized indicates if the service is already initialized. */
036: private static boolean serviceInitialized = false;
037:
038: private static DateFormat dateFormat;
039:
040: /** The default debug level for the entire service and the level that is
041: * used when a Debug object is first created and before its level is
042: * modified. Don't initialize the following two variables in a static
043: * initializer/block because other components may initialize Debug in their
044: * static initializers (as opposed to constructors or methods). The
045: * order of execution of static blocks is not guaranteed by JVM. So if we
046: * set the following two static variables to some default values here, then
047: * it will interfere with the execution of {@link #initService}.
048: */
049: private static String defaultDebugLevel;
050: private static String outputDirectory;
051:
052: private final String debugName;
053: private PrintWriter debugFile = null;
054: private int debugLevel;
055:
056: /** Initializes the Debug service so that Debug objects can be created. At
057: * startup (when the first Debug object is ever created in a JVM), this
058: * method reads <code>DebugConfig.properties</code> file (using
059: * {@link java.util.ResourceBundle} semantics) from one of the directories
060: * in the CLASSPATH, and loads the properties. It creates the debug
061: * directory. If all the directories in output dir don't have adequate
062: * permissions then the creation of the debug directory will fail and all
063: * the debug files will be located in the "current working directory" of the
064: * process running debug code
065: * If there is an error reading or loading the properties, it will set the
066: * debug service to redirect all debug information to <code>System.out</code>
067: */
068: private static void initService() {
069: /* We will use the double-checked locking pattern. Rarely entered
070: * block. Push synchronization inside it. This is the first check.
071: */
072: if (!serviceInitialized) {
073: /* Only 1 thread at a time gets past the next point. Rarely
074: * executed synchronization statement and hence synchronization
075: * penalty is not paid every time this method is called.
076: */
077: synchronized (Debug.class) {
078: /* If a second thread was waiting to get here, it will now
079: * find that the instance has already been initialized, and
080: * it will not re-initialize the instance variable. This is the
081: * (second) double-check.
082: */
083: if (!serviceInitialized) {
084: dateFormat = new SimpleDateFormat(
085: "MM/dd/yyyy hh:mm:ss:SSS a zzz");
086: try {
087: ResourceBundle bundle = ResourceBundle
088: .getBundle("AMConfig");
089: defaultDebugLevel = bundle
090: .getString("com.iplanet.services.debug.level");
091: outputDirectory = bundle
092: .getString("com.iplanet.services.debug.directory");
093: /* bundle =
094: com.iplanet.am.util.Locale.getInstallResourceBundle("amUtilMsgs");
095: */
096: if (outputDirectory != null) {
097: File createDir = new File(outputDirectory);
098: if (!createDir.exists()) {
099: if (!createDir.mkdirs()) {
100: System.err
101: .println(bundle
102: .getString("com.iplanet.services.debug.nodir"));
103: }
104: }
105:
106: }
107: } catch (MissingResourceException e) {
108: /* System.err.println(e.getMessage());
109: e.printStackTrace();
110:
111: // If there is any error in getting the level or
112: // outputDirectory, defaultDebugLevel will be set to
113: // ON so that output will go to
114: // System.out
115:
116: defaultDebugLevel = "on";
117: outputDirectory = null;*/
118: } catch (SecurityException se) {
119: //System.err.println(se.getMessage());
120: }
121: serviceInitialized = true;
122: }
123: }
124: }
125: }
126:
127: /** This constructor takes as an argument the name of the debug file. The
128: * debug file is neither created nor opened until the first time
129: * <code>message()</code>, <code>warning()</code> or <code>error()</code>
130: * is invoked and the debug state is neither <code>OFF</code> nor
131: * <code>ON</code>.
132: * <p><b>NOTE:</b>The recommended and preferred method to create Debug
133: * objects is <code>getInstance(String)</code>. This constructor may be
134: * deprecated in future.</p>
135: *
136: * @param debugFileName name of the debug file to create or use
137: */
138: private Debug(String debugName) {
139: // Initialize the debug service the first time a Debug object is
140: // created.
141:
142: initService();
143:
144: // Now initialize this instance itself
145: this .debugName = debugName;
146: setDebug(defaultDebugLevel);
147:
148: synchronized (debugMap) {
149: // explicitly ignore any duplicate instances.
150: debugMap.put(debugName, this );
151: }
152: }
153:
154: /** Gets an existing instance of Debug for the specified debug file or a new
155: * one if no such instance already exists. If a Debug object has to be
156: * created, its level is set to the level defined in the
157: * <code>AMConfig.properties</code> file. The level can be changed later
158: * by using {@link #setDebug(int)} or {@link #setDebug(String)}
159: *
160: * @param debugName the name of the debug file to create or use
161: * @return a debug instance corresponding to the specified debug file
162: */
163: public static synchronized Debug getInstance(String debugName) {
164: Debug debugObj = (Debug) debugMap.get(debugName);
165: if (debugObj == null) {
166: debugObj = new Debug(debugName);
167: }
168: return debugObj;
169: }
170:
171: /** Checks if message debugging is enabled.
172: *
173: * <p><b>NOTE:</b> It is recommended that <code>messageEnabled()</code>
174: * be used instead of <code>debugEnabled()</code> as the former is more
175: * intuitive.</>
176: *
177: * @return <code>true</code> if message debugging is enabled
178: * <code>false</code> if message debugging is disabled
179: *
180: * @deprecated Use {@link #messageEnabled}
181: */
182: private boolean debugEnabled() {
183: return (debugLevel > Debug.WARNING);
184: }
185:
186: /** Checks if message debugging is enabled.
187: *
188: * <p><b>NOTE:</b> Debugging is an IO intensive operation and may hurt
189: * application performance when abused. Particularly, note that Java
190: * evaluates arguments to <code>message()</code> even when
191: * debugging is turned off. It is recommended that
192: * <code>messageEnabled()</code> be called to check the debug state
193: * before invoking any <code>message()</code> methods to avoid
194: * unnecessary argument evaluation and maximize application performance.</p>
195: *
196: * @return <code>true</code> if message debugging is enabled
197: * <code>false</code> if message debugging is disabled
198: */
199: public boolean messageEnabled() {
200: return (debugLevel > Debug.WARNING);
201: }
202:
203: /** Checks if warning debugging is enabled.
204: *
205: * <p><b>NOTE:</b> Debugging is an IO intensive operation and may hurt
206: * application performance when abused. Particularly, note that Java
207: * evaluates arguments to <code>warning()</code> even when
208: * warning debugging is turned off. It is recommended that
209: * <code>warningEnabled()</code> be called to check the debug state
210: * before invoking any <code>warning()</code> methods to avoid
211: * unnecessary argument evaluation and maximize application performance.</p>
212: *
213: * @return <code>true</code> if warning debugging is enabled
214: * <code>false</code> if warning debugging is disabled
215: */
216: public boolean warningEnabled() {
217: return (debugLevel > Debug.ERROR);
218: }
219:
220: /** Returns one of the five possible values:
221: * <p><code>Debug.OFF</code><p>
222: * <p><code>Debug.ERROR</code><p>
223: * <p><code>Debug.WARNING</code><p>
224: * <p><code>Debug.MESSAGE</code><p>
225: * <p><code>Debug.ON</code><p>
226: *
227: * @return the debug level
228: */
229: public int getState() {
230: return debugLevel;
231: }
232:
233: /** Prints messages only when the debug state is either
234: * DEBUG.MESSAGE or Debug.ON.
235: *
236: * <p><b>NOTE:</b> Debugging is an IO intensive operation and may hurt
237: * application performance when abused. Particularly, note that Java
238: * evaluates arguments to <code>message()</code> even when
239: * debugging is turned off. So when the argument to this method involves
240: * the String concatenation operator '+' or any other method invocation,
241: * <code>messageEnabled</code> <b>MUST</b> be used. It is recommended that
242: * the debug state be checked by invoking <code>messageEnabled()</code>
243: * before invoking any <code>message()</code> methods to avoid
244: * unnecessary argument evaluation and maximize application performance.</p>
245: *
246: * @see Debug#message(String msg, Throwable t)
247: */
248: public void message(String msg) {
249: if (debugLevel > Debug.WARNING) {
250: message(msg, null);
251: }
252: }
253:
254: /** <p> Prints debug and exception messages only when the debug
255: * state is either DEBUG.MESSAGE or Debug.ON. If the debug file is not
256: * accessible and debugging is enabled, the message along with a timestamp
257: * and thread info will be printed on <code>System.out</code>.</p>
258: *
259: * <p>This method creates the debug file if does not exist; otherwise it
260: * starts appending to the existing debug file. When invoked for the first
261: * time on this object, the method writes a line delimiter of '*'s.</p>
262: *
263: * <p>Note that the debug file will remain open until <code>destroy()</code>
264: * is invoked. To conserve file resources, you should invoke
265: * <code>destroy()</code> explicitly rather than wait for the garbage
266: * collector to clean up.</p>
267: *
268: * <p><b>NOTE:</b> Debugging is an IO intensive operation and may hurt
269: * application performance when abused. Particularly, note that
270: * Java evaluates arguments to <code>message()</code> even when
271: * debugging is turned off. It is recommended that the debug state be
272: * checked by invoking <code>messageEnabled()</code> before invoking any
273: * <code>message()</code> methods to avoid unnecessary argument evaluation
274: * and to maximize application performance.</p>
275: *
276: * @param msg message to be printed. A newline will be appended to the
277: * message before printing either to <code>System.out</code>
278: * or to the debug file. If <code>msg</code> is null, it is
279: * ignored.
280: *
281: * @param t Throwable, on which <code>printStackTrace</code> will be
282: * invoked to print the stack trace. If <code>t</code> is
283: * null, it is ignored.
284: *
285: * @see Debug#error(String msg, Throwable t)
286: */
287: public void message(String msg, Throwable t) {
288: if (debugLevel > Debug.WARNING) {
289: formatAndWrite(null, msg, t);
290: }
291: }
292:
293: /** Prints warning messages only when debug level is greater than
294: * DEBUG.ERROR.
295: *
296: * <p><b>NOTE:</b> Debugging is an IO intensive operation and may hurt
297: * application performance when abused. Particularly, note that
298: * Java evaluates arguments to <code>warning()</code> even when
299: * debugging is turned off. So when the argument to this method involves
300: * the String concatenation operator '+' or any other method invocation,
301: * <code>warningEnabled</code> <b>MUST</b> be used. It is recommended that
302: * the debug state be checked by invoking <code>warningEnabled()</code>
303: * before invoking any <code>warning()</code> methods to avoid
304: * unnecessary argument evaluation and to maximize application
305: * performance.</p>
306: *
307: * @param msg message to be printed. A newline will be appended to the
308: * message before printing either to <code>System.out</code>
309: * or to the debug file. If <code>msg</code> is null, it is
310: * ignored.
311: *
312: * @see Debug#warning(String msg, Throwable t)
313: */
314: public void warning(String msg) {
315: if (debugLevel > Debug.ERROR) {
316: formatAndWrite("WARNING: ", msg, null);
317: }
318: }
319:
320: /** Prints warning messages only when debug level is greater than
321: * DEBUG.ERROR.
322: *
323: * <p><b>NOTE:</b> Debugging is an IO intensive operation and may hurt
324: * application performance when abused. Particularly, note that
325: * Java evaluates arguments to <code>warning()</code> even when
326: * debugging is turned off. It is recommended that the debug state be
327: * checked by invoking <code>warningEnabled()</code> before invoking any
328: * <code>warning()</code> methods to avoid unnecessary argument evaluation
329: * and to maximize application performance.</p>
330: *
331: * <p>If the debug file is not accessible and debuging is enabled, the
332: * message along with a timestamp and thread info will be printed on
333: * <code>System.out</code>.</p>
334: *
335: * <p>This method creates the debug file if does not exist; otherwise it
336: * starts appending to the existing debug file. When invoked for the first
337: * time on this object, the method writes a line delimiter of '*'s.</p>
338: *
339: * <p>Note that the debug file will remain open until <code>destroy()</code>
340: * is invoked. To conserve file resources, you should invoke
341: * <code>destroy()</code> explicitly rather than wait for the garbage
342: * collector to clean up.</p>
343: *
344: * @param msg message to be printed. A newline will be appended to the
345: * message before printing either to <code>System.out</code>
346: * or to the debug file. If <code>msg</code> is null, it is
347: * ignored.
348: *
349: * @param t Throwable, on which <code>printStackTrace()</code> will be
350: * invoked to print the stack trace. If <code>t</code> is
351: * null, it is ignored.
352: */
353: public void warning(String msg, Throwable t) {
354: if (debugLevel > Debug.ERROR) {
355: formatAndWrite("WARNING: ", msg, t);
356: }
357: }
358:
359: /** Prints error messages only when debug level is greater than DEBUG.OFF.
360: *
361: * @param msg message to be printed. A newline will be appended to the
362: * message before printing either to <code>System.out</code>
363: * or to the debug file. If <code>msg</code> is null, it is
364: * ignored.
365: *
366: * @see Debug#error(String msg, Throwable t)
367: */
368: public void error(String msg) {
369: if (debugLevel > Debug.OFF) {
370: formatAndWrite("ERROR: ", msg, null);
371: }
372: }
373:
374: /** Prints error messages only if debug state is greater than
375: * Debug.OFF. If the debug file is not accessible and debugging is enabled,
376: * the message along with a timestamp and thread info will be printed on
377: * <code>System.out</code>.</p>
378: *
379: * <p>This method creates the debug file if does not exist; otherwise it
380: * starts appending to the existing debug file. When invoked for the first
381: * time on this object, the method writes a line delimiter of '*'s.</p>
382: *
383: * <p>Note that the debug file will remain open until <code>destroy()</code>
384: * is invoked. To conserve file resources, you should invoke
385: * <code>destroy()</code> explicitly rather than wait for the garbage
386: * collector to clean up.</p>
387: *
388: * @param msg message to be printed. A newline will be appended to the
389: * message before printing either to <code>System.out</code>
390: * or to the debug file. If <code>msg</code> is null, it is
391: * ignored.
392: *
393: * @param t Throwable, on which <code>printStackTrace()</code> will be
394: * invoked to print the stack trace. If <code>t</code> is
395: * null, it is ignored.
396: */
397: public void error(String msg, Throwable t) {
398: if (debugLevel > Debug.OFF) {
399: formatAndWrite("ERROR: ", msg, t);
400: }
401: }
402:
403: private void formatAndWrite(String prefix, String msg, Throwable t) {
404: if (debugLevel == Debug.ON) {
405: if (msg != null) {
406: if (prefix == null) {
407: System.out.println(msg);
408: } else {
409: System.out.println(prefix + msg);
410: }
411: }
412: if (t != null) {
413: System.out.println(t.getMessage());
414: t.printStackTrace(System.out);
415: }
416: return;
417: }
418:
419: // The default capacity of StringBuffer in StringWriter is 16, but we
420: // know for sure that the minimum header size is about 35. Hence, to
421: // avoid reallocation allocate at least 160 chars.
422:
423: StringWriter swriter = new StringWriter(160);
424: PrintWriter buf = new PrintWriter(swriter, true);
425: synchronized (dateFormat) {
426: buf.write(dateFormat.format(new Date()));
427: }
428: buf.write(": ");
429: buf.write(Thread.currentThread().toString());
430: buf.write("\n");
431: if (prefix != null) {
432: buf.write(prefix);
433: }
434: if (msg != null) {
435: buf.write(msg);
436: }
437: if (t != null) {
438: buf.write("\n");
439: t.printStackTrace(buf);
440: }
441: buf.flush();
442:
443: write(swriter.toString());
444: }
445:
446: /** Actually writes to the debug file. If it cannot write to the debug
447: * file, it turn off debugging. The first time this method is invoked on
448: * a Debug object, that object's debug file is created/opened in the
449: * directory specified by the
450: * <code>property com.iplanet.services.debug.directory</code> in the
451: * properties file, <code>DebugConfig.properties</code>.
452: */
453: private synchronized void write(String msg) {
454: try {
455: // debugging is enabled.
456: // First, see if the debugFile is already open. If not, open it now.
457:
458: if (debugFile == null) {
459: // open file in append mode
460: FileOutputStream fos = new FileOutputStream(
461: outputDirectory + File.separator + debugName,
462: true);
463: debugFile = new PrintWriter(new BufferedWriter(
464: new OutputStreamWriter(fos, "UTF-8")), true); // autoflush enabled
465:
466: debugFile
467: .println("******************************************************");
468: }
469:
470: debugFile.println(msg);
471: } catch (IOException e) {
472: System.err.println(msg);
473:
474: // turn off debugging because debugFile is not accessible
475: debugLevel = Debug.OFF;
476: }
477: }
478:
479: /** Sets the debug capabilities based on the values of the
480: * debugType argument.
481: *
482: * @param debugType is any one of five possible values:
483: * <p><code>Debug.OFF</code><p>
484: * <p><code>Debug.ERROR</code><p>
485: * <p><code>Debug.WARNING</code><p>
486: * <p><code>Debug.MESSAGE</code><p>
487: * <p><code>Debug.ON</code><p>
488: */
489: public void setDebug(int debugType) {
490: switch (debugType) {
491: case Debug.OFF:
492: case Debug.ERROR:
493: case Debug.WARNING:
494: case Debug.MESSAGE:
495: case Debug.ON:
496: debugLevel = debugType;
497: break;
498:
499: default:
500: // ignore invalid debugType values
501: break;
502: }
503: }
504:
505: /** Enables or disables degbugging based on the value of debug
506: * attribute, com.iplanet.services.debug.level, in the
507: * DebugConfig.properties file. DebugConfig.properties file should be
508: * accessible from CLASSPATH.
509: * If the property is not defined, debug level is set to 'error'.
510: * @deprecated Use {@link #getInstance}. {@link #getInstance} will
511: * automatically set the debug level based on the information in
512: * DebugConfig.properties file.
513: */
514: private void setDebug() {
515: // The following initService is temporary. setDebug() is anyways
516: // deprecated and will be removed in future.
517: initService();
518: setDebug(defaultDebugLevel);
519: }
520:
521: /** Sets the debug capabilities based on the values of the
522: * debugType argument.
523: *
524: * @param debugType is any one of the following possible values:
525: * <p>off - debugging is disabled</p>
526: * <p>on - all debugging is enabled and written to
527: * <code>System.out</code></p>
528: * <p>message - message debugging is enabled and written to the debug
529: * file</p>
530: * <p>warning - warning debugging is enabled and written to the debug</p>
531: * file</p>
532: * <p>error - error debugging is enabled and written to the debug</p>
533: * file</p>
534: */
535: public void setDebug(String debugType) {
536: if (debugType == null) {
537: return;
538: } else if (debugType.equalsIgnoreCase("error")) {
539: debugLevel = Debug.ERROR;
540: } else if (debugType.equalsIgnoreCase("warning")) {
541: debugLevel = Debug.WARNING;
542: } else if (debugType.equalsIgnoreCase("message")) {
543: debugLevel = Debug.MESSAGE;
544: } else if (debugType.equalsIgnoreCase("on")) {
545: debugLevel = Debug.ON;
546: } else if (debugType.equalsIgnoreCase("off")) {
547: debugLevel = Debug.OFF;
548: } else if (debugType.equals("*")) {
549: debugLevel = Debug.ON;
550: } else {
551: if (debugType.endsWith("*")) {
552: debugType = debugType.substring(0,
553: debugType.length() - 1);
554: }
555: if (debugName.startsWith(debugType)) {
556: debugLevel = Debug.ON;
557: }
558: }
559: }
560:
561: /** Destroys the debug object, closes the debug file and releases any system
562: * resources. Note that the debug file will remain open until
563: * <code>destroy()</code> is invoked. To conserve file resources, you should
564: * invoke <code>destroy()</code> explicitly rather than wait for the garbage
565: * collector to clean up.
566: *
567: * <p> If this object is accessed after <code>destroy()</code> has been
568: * invoked, the results are undefined.</p>
569: */
570: public void destroy() {
571: finalize();
572: }
573:
574: /** Flushes and then closes the debug file. */
575: protected void finalize() {
576: synchronized (debugMap) {
577: debugMap.remove(debugName);
578: }
579:
580: synchronized (this ) {
581: if (debugFile == null) {
582: return;
583: }
584:
585: debugLevel = Debug.OFF;
586: debugFile.flush();
587: debugFile.close();
588: debugFile = null;
589: }
590: }
591:
592: private static final String sccsID = "$Id: Debug.java,v 1.3 2003/08/05 00:55:31 sorensen Exp $ Sun Microsystems, Inc.";
593: }
|