001: package com.sun.portal.desktop.util;
002:
003: import java.io.*;
004: import java.util.*;
005: import java.text.*;
006:
007: // USED JUST BY Properties Mode ( instead of DSAME).
008:
009: // NOTE: Since JVM specs guarantee atomic access/updates to int variables
010: // (actually all variables except double and long), the design consciously
011: // avoids synchronized methods, particularly for message(). This is done to
012: // reduce the performance overhead of synchronized message() when debugging
013: // is disabled. This does not have serious side-effects other than an occasional
014: // invocation of message() missing concurrent update of 'debug'.
015:
016: /** <p>Allows a uniform interface to file debug and exception information in a
017: * uniform format. <code>PropertiesDebug</code> supports different levels/states of
018: * filing debug information (in the ascending order): <code>OFF</code>,
019: * <code>ERROR</code>, <code>WARNING</code>, <code>MESSAGE</code> and
020: * <code>ON</code>. A given debug level/state is enabled if the debug
021: * state/level is set to at least that state/level. For example, if the debug
022: * state is <code>ERROR</code>, only errors will be filed. If the debug state is
023: * <code>WARNING</code>, only errors and warnings will be filed. If the debug
024: * state is <code>MESSAGE</code>, everything will be filed.
025: * <code>MESSAGE</code> and <code>ON</code> are of the same levels;
026: * the difference between them being <code>MESSAGE</code> writes to a file,
027: * whereas <code>ON</code> writes to System.out.</p>
028: *
029: * <p> PropertiesDebug files are created in the directory specified by the property,
030: * <code>ps.debug.dir</code>, in /etc/opt/SUNWportal/platform.conf file. The
031: * default value for this property is /var/opt/SUNWportal/debug.</p>
032: *
033: * <p><b>NOTE:</b> Debugging is an IO intensive operation and may hurt
034: * application performance when abused. Particularly, note that Java evaluates
035: * the arguments to <code>message()</code> and <code>warning()</code> even
036: * when debugging is turned off.
037: * It is recommended that the debug state be checked before invoking any
038: * <code>message()</code> or <code>warning()</code> methods to avoid unnecessary
039: * argument evaluation and to maximize application performance.</p>
040: */
041: public class PropertiesDebug {
042: /** flags the disabled debug state. */
043: public static final int OFF = 0;
044:
045: /** flags the state where error debugging is enabled. When debugging is set
046: * to less than <code>ERROR</code>, error debugging is also disabled.
047: */
048: public static final int ERROR = 1;
049:
050: /** flags the state where warning debugging is enabled, but message
051: * debugging is disabled. When debugging is set to less than
052: * <code>WARNING</code>, warning debugging is also disabled.
053: */
054: public static final int WARNING = 2;
055:
056: /** This state enables debugging of messages, warnings and errors. */
057: public static final int MESSAGE = 3;
058:
059: /** flags the enabled debug state for warnings, errors and messages.
060: * Printing to a file is disabled. All printing is done on System.out.
061: */
062: public static final int ON = 4;
063:
064: /** debugMap is a container of all active PropertiesDebug objects. Log file name is
065: * the key and PropertiesDebug is the value of this map.
066: */
067: private static Map debugMap = new HashMap();
068:
069: private static DateFormat dateFormat;
070:
071: private final String debugName;
072: private PrintWriter debugFile = null;
073: private int debug = PropertiesDebug.OFF;
074:
075: static {
076: dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT,
077: DateFormat.LONG, java.util.Locale.US);
078: }
079:
080: /** This constructor takes as an argument the name of the debug file. The
081: * debug file is neither created nor opened until the first time
082: * <code>message()</code>, <code>warning()</code> or <code>error()</code>
083: * is invoked and the debug state is neither <code>OFF</code> nor
084: * <code>ON</code>.
085: * <p><b>NOTE:</b>The recommended and preferred method to create PropertiesDebug
086: * objects is <code>getInstance(String)</code>. This constructor may be
087: * deprecated in future.</p>
088: *
089: * @param debugFileName name of the debug file to create or use
090: * @see PropertiesDebug#getInstance
091: */
092: public PropertiesDebug(String debugName) {
093: this .debugName = debugName;
094:
095: synchronized (debugMap) {
096: // explicitly ignore any duplicate instances.
097: debugMap.put(debugName, this );
098: }
099: }
100:
101: /** Gets an existing instance of PropertiesDebug for the specified debug file or a new
102: * one if no such instance already exists.
103: */
104: public static synchronized PropertiesDebug getInstance(
105: String debugName) {
106: PropertiesDebug debugObj = (PropertiesDebug) debugMap
107: .get(debugName);
108: if (debugObj == null) {
109: debugObj = new PropertiesDebug(debugName);
110: }
111: return debugObj;
112: }
113:
114: /** Checks if message debugging is enabled.
115: *
116: * <p><b>NOTE:</b> It is recommended that <code>messageEnabled()</code>
117: * be used instead of <code>debugEnabled()</code> as the former is more
118: * intuitive.</>
119: *
120: * @return <code>true</code> if message debugging is enabled
121: * <code>false</code> if message debugging is disabled
122: *
123: * @see PropertiesDebug#messageEnabled
124: */
125: public boolean debugEnabled() {
126: return (debug > PropertiesDebug.WARNING);
127: }
128:
129: /** Checks if message debugging is enabled.
130: *
131: * <p><b>NOTE:</b> Debugging is an IO intensive operation and may hurt
132: * application performance when abused. Particularly, note that Java
133: * evaluates arguments to <code>message()</code> even when
134: * debugging is turned off. It is recommended that
135: * <code>messageEnabled()</code> be called to check the debug state
136: * before invoking any <code>message()</code> methods to avoid
137: * unnecessary argument evaluation and maximize application performance.</p>
138: *
139: * @return <code>true</code> if message debugging is enabled
140: * <code>false</code> if message debugging is disabled
141: */
142: public boolean messageEnabled() {
143: return (debug > PropertiesDebug.WARNING);
144: }
145:
146: /** Checks if warning debugging is enabled.
147: *
148: * <p><b>NOTE:</b> Debugging is an IO intensive operation and may hurt
149: * application performance when abused. Particularly, note that Java
150: * evaluates arguments to <code>warning()</code> even when
151: * warning debugging is turned off. It is recommended that
152: * <code>warningEnabled()</code> be called to check the debug state
153: * before invoking any <code>warning()</code> methods to avoid
154: * unnecessary argument evaluation and maximize application performance.</p>
155: *
156: * @return <code>true</code> if warning debugging is enabled
157: * <code>false</code> if warning debugging is disabled
158: */
159: public boolean warningEnabled() {
160: return (debug > PropertiesDebug.ERROR);
161: }
162:
163: /** Returns one of the five possible values:
164: * <p><code>PropertiesDebug.OFF</code><p>
165: * <p><code>PropertiesDebug.ERROR</code><p>
166: * <p><code>PropertiesDebug.WARNING</code><p>
167: * <p><code>PropertiesDebug.MESSAGE</code><p>
168: * <p><code>PropertiesDebug.ON</code><p>
169: */
170: public int getState() {
171: return debug;
172: }
173:
174: /** Prints messages only when the debug state is either
175: * DEBUG.MESSAGE or PropertiesDebug.ON.
176: *
177: * <p><b>NOTE:</b> Debugging is an IO intensive operation and may hurt
178: * application performance when abused. Particularly, note that Java
179: * evaluates arguments to <code>message()</code> even when
180: * debugging is turned off. It is recommended that the debug state be
181: * checked by invoking <code>messageEnabled()</code> before invoking any
182: * <code>message()</code> methods to avoid
183: * unnecessary argument evaluation and maximize application performance.</p>
184: *
185: * @see PropertiesDebug#message(String msg, Throwable t)
186: */
187: public void message(String msg) {
188: if (debug > PropertiesDebug.WARNING) {
189: message(msg, null);
190: }
191: }
192:
193: /** <p> Prints debug and exception messages only when the debug
194: * state is either DEBUG.MESSAGE or PropertiesDebug.ON. If the debug file is not
195: * accessible and debugging is enabled, the message along with a timestamp
196: * and thread info will be printed on <code>System.out</code>.</p>
197: *
198: * <p>This method creates the debug file if does not exist; otherwise it
199: * starts appending to the existing debug file. When invoked for the first
200: * time on this object, the method writes a line delimiter of '*'s.</p>
201: *
202: * <p>Note that the debug file will remain open until <code>destroy()</code>
203: * is invoked. To conserve file resources, you should invoke
204: * <code>destroy()</code> explicitly rather than wait for the garbage
205: * collector to clean up.</p>
206: *
207: * <p><b>NOTE:</b> Debugging is an IO intensive operation and may hurt
208: * application performance when abused. Particularly, note that
209: * Java evaluates arguments to <code>message()</code> even when
210: * debugging is turned off. It is recommended that the debug state be
211: * checked by invoking <code>messageEnabled()</code> before invoking any
212: * <code>message()</code> methods to avoid unnecessary argument evaluation
213: * and to maximize application performance.</p>
214: *
215: * @param msg message to be printed. A newline will be appended to the
216: * message before printing either to <code>System.out</code>
217: * or to the debug file. If <code>msg</code> is null, it is
218: * ignored.
219: *
220: * @param t Throwable, on which <code>printStackTrace</code> will be
221: * invoked to print the stack trace. If <code>t</code> is
222: * null, it is ignored.
223: *
224: * @see PropertiesDebug#error(String msg, Throwable t)
225: */
226: public void message(String msg, Throwable t) {
227: if (debug > PropertiesDebug.WARNING) {
228: formatAndWrite(null, msg, t);
229: }
230: }
231:
232: /** Prints warning messages only when debug level is greater than
233: * DEBUG.ERROR.
234: *
235: * <p><b>NOTE:</b> Debugging is an IO intensive operation and may hurt
236: * application performance when abused. Particularly, note that
237: * Java evaluates arguments to <code>warning()</code> even when
238: * debugging is turned off. It is recommended that the debug state be
239: * checked by invoking <code>warningEnabled()</code> before invoking any
240: * <code>warning()</code> methods to avoid unnecessary argument evaluation
241: * and to maximize application performance.</p>
242: *
243: * @see PropertiesDebug#warning(String msg, Throwable t)
244: */
245: public void warning(String msg) {
246: if (debug > PropertiesDebug.ERROR) {
247: formatAndWrite("WARNING: ", msg, null);
248: }
249: }
250:
251: /** Prints warning messages only when debug level is greater than
252: * DEBUG.ERROR.
253: *
254: * <p><b>NOTE:</b> Debugging is an IO intensive operation and may hurt
255: * application performance when abused. Particularly, note that
256: * Java evaluates arguments to <code>warning()</code> even when
257: * debugging is turned off. It is recommended that the debug state be
258: * checked by invoking <code>warningEnabled()</code> before invoking any
259: * <code>warning()</code> methods to avoid unnecessary argument evaluation
260: * and to maximize application performance.</p>
261: *
262: * <p>If the debug file is not accessible and debuging is enabled, the
263: * message along with a timestamp and thread info will be printed on
264: * <code>System.out</code>.</p>
265: *
266: * <p>This method creates the debug file if does not exist; otherwise it
267: * starts appending to the existing debug file. When invoked for the first
268: * time on this object, the method writes a line delimiter of '*'s.</p>
269: *
270: * <p>Note that the debug file will remain open until <code>destroy()</code>
271: * is invoked. To conserve file resources, you should invoke
272: * <code>destroy()</code> explicitly rather than wait for the garbage
273: * collector to clean up.</p>
274: *
275: * @param msg message to be printed. A newline will be appended to the
276: * message before printing either to <code>System.out</code>
277: * or to the debug file. If <code>msg</code> is null, it is
278: * ignored.
279: *
280: * @param t Throwable, on which <code>printStackTrace()</code> will be
281: * invoked to print the stack trace. If <code>t</code> is
282: * null, it is ignored.
283: */
284: public void warning(String msg, Throwable t) {
285: if (debug > PropertiesDebug.ERROR) {
286: formatAndWrite("WARNING: ", msg, t);
287: }
288: }
289:
290: /** Prints error messages only when debug level is greater than DEBUG.OFF.
291: *
292: * @see PropertiesDebug#error(String msg, Throwable t)
293: */
294: public void error(String msg) {
295: if (debug > PropertiesDebug.OFF) {
296: formatAndWrite("ERROR: ", msg, null);
297: }
298: }
299:
300: /** Prints error messages only if debug state is greater than
301: * PropertiesDebug.OFF. If the debug file is not accessible and debugging is enabled,
302: * the message along with a timestamp and thread info will be printed on
303: * <code>System.out</code>.</p>
304: *
305: * <p>This method creates the debug file if does not exist; otherwise it
306: * starts appending to the existing debug file. When invoked for the first
307: * time on this object, the method writes a line delimiter of '*'s.</p>
308: *
309: * <p>Note that the debug file will remain open until <code>destroy()</code>
310: * is invoked. To conserve file resources, you should invoke
311: * <code>destroy()</code> explicitly rather than wait for the garbage
312: * collector to clean up.</p>
313: *
314: * @param msg message to be printed. A newline will be appended to the
315: * message before printing either to <code>System.out</code>
316: * or to the debug file. If <code>msg</code> is null, it is
317: * ignored.
318: *
319: * @param t Throwable, on which <code>printStackTrace()</code> will be
320: * invoked to print the stack trace. If <code>t</code> is
321: * null, it is ignored.
322: */
323: public void error(String msg, Throwable t) {
324: if (debug > PropertiesDebug.OFF) {
325: formatAndWrite("ERROR: ", msg, t);
326: }
327: }
328:
329: private void formatAndWrite(String prefix, String msg, Throwable t) {
330: if (debug == PropertiesDebug.ON) {
331: if (msg != null) {
332: if (prefix == null) {
333: System.out.println(msg);
334: } else {
335: System.out.println(prefix + msg);
336: }
337: }
338: if (t != null) {
339: System.out.println(t.getMessage());
340: t.printStackTrace(System.out);
341: }
342: return;
343: }
344:
345: // The default capacity of StringBuffer in StringWriter is 16, but we
346: // know for sure that the minimum header size is about 35. Hence, to
347: // avoid reallocation allocate at least 160 chars.
348:
349: StringWriter swriter = new StringWriter(160);
350: PrintWriter buf = new PrintWriter(swriter, true);
351: synchronized (dateFormat) {
352: buf.write(dateFormat.format(new Date()));
353: }
354: buf.write(": ");
355: buf.write(Thread.currentThread().toString());
356: buf.write("\n");
357: if (prefix != null) {
358: buf.write(prefix);
359: }
360: if (msg != null) {
361: buf.write(msg);
362: }
363: if (t != null) {
364: buf.write("\n");
365: t.printStackTrace(buf);
366: }
367: buf.flush();
368:
369: write(swriter.toString());
370: }
371:
372: /** Actually writes to the debug file. If it cannot write to the debug
373: * file, it turn off debugging.
374: */
375: private synchronized void write(String msg) {
376: try {
377: // debugging is enabled.
378: // First, see if the debugFile is already open. If not, open it now.
379:
380: if (debugFile == null) {
381: // open file in append mode
382: FileOutputStream fos = new FileOutputStream(
383: getDebugBaseDir() + "/" + debugName, true);
384: debugFile = new PrintWriter(new BufferedWriter(
385: new OutputStreamWriter(fos, "UTF8")), true); // autoflush enabled
386:
387: debugFile
388: .println("******************************************************");
389: }
390:
391: debugFile.println(msg);
392: } catch (IOException e) {
393: System.err.println(msg);
394:
395: // turn off debugging because debugFile is not accessible
396: debug = PropertiesDebug.OFF;
397: }
398: }
399:
400: /** Sets the debug capabilities based on the values of the
401: * debugType argument.
402: *
403: * @param debugType is any one of five possible values:
404: * <p><code>PropertiesDebug.OFF</code><p>
405: * <p><code>PropertiesDebug.ERROR</code><p>
406: * <p><code>PropertiesDebug.WARNING</code><p>
407: * <p><code>PropertiesDebug.MESSAGE</code><p>
408: * <p><code>PropertiesDebug.ON</code><p>
409: */
410: public void setDebug(int debugType) {
411: switch (debugType) {
412: case PropertiesDebug.OFF:
413: case PropertiesDebug.ERROR:
414: case PropertiesDebug.WARNING:
415: case PropertiesDebug.MESSAGE:
416: case PropertiesDebug.ON:
417: debug = debugType;
418: break;
419:
420: default:
421: // ignore invalid debugType values
422: break;
423: }
424: }
425:
426: /** Sets the debug capabilities based on the values of the
427: * debugType argument.
428: *
429: * @param debugType is any one of the following possible values:
430: * <p>off - debugging is disabled</p>
431: * <p>on - all debugging is enabled and written to
432: * <code>System.out</code></p>
433: * <p>message - message debugging is enabled and written to the debug
434: * file</p>
435: * <p>warning - warning debugging is enabled and written to the debug</p>
436: * file</p>
437: * <p>error - error debugging is enabled and written to the debug</p>
438: * file</p>
439: */
440: public void setDebug(String debugType) {
441: if (debugType == null) {
442: return;
443: } else if (debugType.equalsIgnoreCase("error")) {
444: debug = PropertiesDebug.ERROR;
445: } else if (debugType.equalsIgnoreCase("warning")) {
446: debug = PropertiesDebug.WARNING;
447: } else if (debugType.equalsIgnoreCase("message")) {
448: debug = PropertiesDebug.MESSAGE;
449: } else if (debugType.equalsIgnoreCase("on")) {
450: debug = PropertiesDebug.ON;
451: } else if (debugType.equalsIgnoreCase("off")) {
452: debug = PropertiesDebug.OFF;
453: } else if (debugType.equals("*")) {
454: debug = PropertiesDebug.ON;
455: } else {
456: if (debugType.endsWith("*")) {
457: debugType = debugType.substring(0,
458: debugType.length() - 1);
459: }
460: if (debugName.startsWith(debugType)) {
461: debug = PropertiesDebug.ON;
462: }
463: }
464: }
465:
466: /** Destroys the debug object, closes the debug file and releases any system
467: * resources. Note that the debug file will remain open until
468: * <code>destroy()</code> is invoked. To conserve file resources, you should
469: * invoke <code>destroy()</code> explicitly rather than wait for the garbage
470: * collector to clean up.
471: *
472: * <p> If this object is accessed after <code>destroy()</code> has been
473: * invoked, the results are undefined.</p>
474: */
475: public void destroy() {
476: finalize();
477: }
478:
479: /** Flushes and then closes the debug file. */
480: protected void finalize() {
481: synchronized (debugMap) {
482: debugMap.remove(debugName);
483: }
484:
485: synchronized (this ) {
486: if (debugFile == null) {
487: return;
488: }
489:
490: debug = PropertiesDebug.OFF;
491: debugFile.flush();
492: debugFile.close();
493: debugFile = null;
494: }
495: }
496:
497: private String getDebugBaseDir() {
498: try {
499: return "/var/tmp";
500: } catch (Exception ex) {
501: System.out.println("Error setting basedir: ");
502: ex.printStackTrace();
503: }
504: return null;
505: }
506: }
|