001: /*
002: * $Id: Utils.java,v 1.48 2007/05/22 09:35:33 agoubard Exp $
003: *
004: * Copyright 2003-2007 Orange Nederland Breedband B.V.
005: * See the COPYRIGHT file for redistribution and use restrictions.
006: */
007: package org.xins.common;
008:
009: import org.xins.common.text.TextUtils;
010:
011: /**
012: * General utility functions.
013: *
014: * @version $Revision: 1.48 $ $Date: 2007/05/22 09:35:33 $
015: * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
016: *
017: * @since XINS 1.1.0
018: */
019: public final class Utils {
020:
021: /**
022: * Constructs a new <code>Utils</code> object.
023: */
024: private Utils() {
025: // empty
026: }
027:
028: /**
029: * Retrieves the actual (major) Java version.
030: *
031: * @return
032: * the actual Java version.
033: *
034: * @since XINS 1.2.0
035: */
036: public static double getJavaVersion() {
037: String s = System.getProperty("java.version").substring(0, 3);
038: return Double.parseDouble(s);
039: }
040:
041: /**
042: * Retrieves the name of the calling class at the specified level. The level
043: * <code>0</code> indicates the direct caller, while <code>1</code>
044: * indicates the caller of the caller.
045: *
046: * <p>If it cannot be determined, then <code>"<unknown>"</code> is
047: * returned.
048: *
049: * @param level
050: * the level of the caller, must be >= 0.
051: *
052: * @param methodMode
053: * <code>true</code> if the information wanted is the method name,
054: * <code>false</code> if the information wanted is the class name.
055: *
056: * @return
057: * the class name of method name of the caller of the caller of this method, at the
058: * specified level, never an empty string and never <code>null</code>.
059: *
060: * @throws IllegalArgumentException
061: * if <code>level < 0</code>.
062: *
063: * @since XINS 2.0
064: */
065: private static String getCallingTrace(int level, boolean methodMode)
066: throws IllegalArgumentException {
067:
068: // Check preconditions
069: if (level < 0) {
070: throw new IllegalArgumentException("level (" + level
071: + ") < 0");
072: }
073: int depth = level + 3;
074:
075: // Only execute on Java 1.4 and up
076: if (getJavaVersion() >= 1.4) {
077:
078: // Create an exception in order to have a stack trace
079: Throwable exception = new Throwable();
080:
081: // Analyze the stack trace
082: StackTraceElement[] trace = exception.getStackTrace();
083: if (trace != null) {
084: for (int pos = 0, i = 0; i < trace.length; i++) {
085:
086: // Skip all non-authentic methods
087: String method = trace[pos].getMethodName();
088: while (method.startsWith("access$")) {
089: method = trace[++pos].getMethodName();
090: }
091:
092: // If we are at the right depth, then return the method name
093: if (i == depth) {
094: if (methodMode) {
095: return method;
096: } else {
097: return trace[pos].getClassName();
098: }
099:
100: // Otherwise go deeper
101: } else {
102: pos++;
103: }
104: }
105: }
106: }
107:
108: // Fallback
109: return "<unknown>";
110: }
111:
112: /**
113: * Retrieves the name of the calling class at the specified level. The level
114: * <code>0</code> indicates the direct caller, while <code>1</code>
115: * indicates the caller of the caller.
116: *
117: * <p>If it cannot be determined, then <code>"<unknown>"</code> is
118: * returned.
119: *
120: * @param level
121: * the level of the caller, must be >= 0.
122: *
123: * @return
124: * the class name of the caller of the caller of this method, at the
125: * specified level, never an empty string and never <code>null</code>.
126: *
127: * @throws IllegalArgumentException
128: * if <code>level < 0</code>.
129: *
130: * @since XINS 1.3.0
131: */
132: public static String getCallingClass(int level)
133: throws IllegalArgumentException {
134:
135: return getCallingTrace(level, false);
136: }
137:
138: /**
139: * Retrieves the name of the calling class. If it cannot be determined,
140: * then a special string (e.g. <code>"<unknown>"</code>) is returned.
141: *
142: * @return
143: * the class name of the caller of the caller of this method, never an
144: * empty string and never <code>null</code>.
145: */
146: public static String getCallingClass() {
147: if (getJavaVersion() >= 1.4) {
148: Throwable exception = new Throwable();
149: StackTraceElement[] trace = exception.getStackTrace();
150: if (trace != null && trace.length >= 3) {
151: StackTraceElement caller = trace[2];
152: if (caller != null) {
153: String callingClass = caller.getClassName();
154: if (!TextUtils.isEmpty(callingClass)) {
155: return callingClass;
156: }
157: }
158: }
159: }
160:
161: // Fallback
162: return "<unknown>";
163: }
164:
165: /**
166: * Retrieves the name of the calling method at the specified level. The
167: * level <code>0</code> indicates the direct caller, while <code>1</code>
168: * indicates the caller of the caller.
169: *
170: * <p>If it cannot be determined, then <code>"<unknown>"</code> is
171: * returned.
172: *
173: * @param level
174: * the level of the caller, must be >= 0.
175: *
176: * @return
177: * the method name of the caller of the caller of this method, at the
178: * specified level, never an empty string and never <code>null</code>.
179: *
180: * @throws IllegalArgumentException
181: * if <code>level < 0</code>.
182: *
183: * @since XINS 1.3.0
184: */
185: public static String getCallingMethod(int level)
186: throws IllegalArgumentException {
187:
188: return getCallingTrace(level, true);
189: }
190:
191: /**
192: * Retrieves the name of the calling method. If it cannot be determined,
193: * then a special string (e.g. <code>"<unknown>"</code>) is returned.
194: *
195: * @return
196: * the method name of the caller of the caller of this method, never an
197: * empty string and never <code>null</code>.
198: */
199: public static String getCallingMethod() {
200: if (getJavaVersion() >= 1.4) {
201: Throwable exception = new Throwable();
202: StackTraceElement[] trace = exception.getStackTrace();
203: if (trace != null && trace.length >= 3) {
204: StackTraceElement caller = trace[2];
205: if (caller != null) {
206: String callingMethod = caller.getMethodName();
207: if (!TextUtils.isEmpty(callingMethod)) {
208: return callingMethod;
209: }
210: }
211: }
212: }
213:
214: // Fallback
215: return "<unknown>";
216: }
217:
218: /**
219: * Logs an exception that will be ignored, with the specified detail
220: * message.
221: *
222: * @param detectingClass
223: * the name of the class that caught the exception, cannot be
224: * <code>null</code>.
225: *
226: * @param detectingMethod
227: * the name of the method within the <code>detectingClass</code> that
228: * caught the exception, cannot be <code>null</code>.
229: *
230: * @param subjectClass
231: * the name of the class which threw the exception, cannot be
232: * <code>null</code>.
233: *
234: * @param subjectMethod
235: * the name of the method (within the <code>subjectClass</code>) which
236: * threw the exception, cannot be <code>null</code>.
237: *
238: * @param detail
239: * detail message, can be <code>null</code>.
240: *
241: * @param exception
242: * the exception to log, cannot be <code>null</code>.
243: *
244: * @throws IllegalArgumentException
245: * if <code>detectingClass == null || detectingMethod == null
246: * || subjectClass == null || subjectMethod == null
247: * || exception == null</code>.
248: *
249: * @since XINS 1.3.0
250: */
251: private static void logIgnoredException(String detectingClass,
252: String detectingMethod, String subjectClass,
253: String subjectMethod, String detail, Throwable exception)
254: throws IllegalArgumentException {
255:
256: // Check preconditions
257: MandatoryArgumentChecker.check("detectingClass",
258: detectingClass, "detectingMethod", detectingMethod,
259: "subjectClass", subjectClass, "subjectMethod",
260: subjectMethod);
261: MandatoryArgumentChecker.check("exception", exception);
262:
263: // Perform the actual logging
264: Log.log_1051(exception.getClass().getName(), exception
265: .getMessage(), detectingClass, detectingMethod,
266: subjectClass, subjectMethod, detail);
267: }
268:
269: /**
270: * Logs an exception that will be ignored.
271: *
272: * @param exception
273: * the exception to log, cannot be <code>null</code>.
274: *
275: * @throws IllegalArgumentException
276: * if <code>exception == null</code>.
277: *
278: * @since XINS 1.5.0
279: */
280: public static void logIgnoredException(Throwable exception)
281: throws IllegalArgumentException {
282:
283: // Determine detecting class and method
284: String detectingClass = getCallingClass();
285: String detectingMethod = getCallingMethod();
286:
287: String sourceClass = null;
288: String sourceMethod = null;
289:
290: try {
291:
292: // Determine the source of the exception
293: StackTraceElement[] trace = exception.getStackTrace();
294:
295: for (int i = 1; i < trace.length && sourceClass == null; i++) {
296: StackTraceElement stackTraceElement = trace[i];
297: if (stackTraceElement.getClassName().equals(
298: detectingClass)
299: && stackTraceElement.getMethodName().equals(
300: detectingMethod)) {
301:
302: // Go one level up the stack trace to know which method threw the exception
303: StackTraceElement source = trace[i - 1];
304: sourceClass = source.getClassName();
305: sourceMethod = source.getMethodName();
306: }
307: }
308: if (sourceClass == null) {
309: sourceClass = "<unknown>";
310: sourceMethod = "<unknown>";
311: }
312:
313: // If there's any exception, then fallback to default values
314: } catch (Throwable t) {
315: sourceClass = "<unknown>";
316: sourceMethod = "<unknown>";
317: }
318:
319: // Call alternative method with detail set to null
320: logIgnoredException(detectingClass, detectingMethod,
321: sourceClass, sourceMethod, null, exception);
322: }
323:
324: /**
325: * Logs an exception that will be ignored.
326: *
327: * @param detectingClass
328: * the name of the class that caught the exception, cannot be
329: * <code>null</code>.
330: *
331: * @param detectingMethod
332: * the name of the method within the <code>detectingClass</code> that
333: * caught the exception, cannot be <code>null</code>.
334: *
335: * @param subjectClass
336: * the name of the class which threw the exception, cannot be
337: * <code>null</code>.
338: *
339: * @param subjectMethod
340: * the name of the method (within the <code>subjectClass</code>) which
341: * threw the exception, cannot be <code>null</code>.
342: *
343: * @param exception
344: * the exception to log, cannot be <code>null</code>.
345: *
346: * @throws IllegalArgumentException
347: * if <code>detectingClass == null || detectingMethod == null
348: * || subjectClass == null || subjectMethod == null
349: * || exception == null</code>.
350: *
351: * @since XINS 1.3.0
352: *
353: * @deprecated Since XINS 2.0, use {@link #logProgrammingError(Throwable exception)}
354: */
355: public static void logIgnoredException(String detectingClass,
356: String detectingMethod, String subjectClass,
357: String subjectMethod, Throwable exception)
358: throws IllegalArgumentException {
359:
360: // Call alternative method with detail set to null
361: logIgnoredException(detectingClass, detectingMethod,
362: subjectClass, subjectMethod, null, exception);
363: }
364:
365: /**
366: * Logs a programming error with an optional cause exception, and returns a
367: * <code>ProgrammingException</code> object for it.
368: *
369: * <p>The calling class/method are considered the detecting class and the
370: * caller of those (one level up) is considered the subject class/method
371: * for the programming error.
372: *
373: * @param detail
374: * the detail message, can be <code>null</code>.
375: *
376: * @return
377: * an appropriate {@link ProgrammingException} that can be thrown by the
378: * calling method, never <code>null</code>.
379: */
380: public static ProgrammingException logProgrammingError(String detail) {
381: return logProgrammingError(getCallingClass(0),
382: getCallingMethod(0), getCallingClass(1),
383: getCallingMethod(1), detail);
384: }
385:
386: /**
387: * Logs a programming error with an optional cause exception, and returns a
388: * <code>ProgrammingException</code> object for it.
389: *
390: * @param cause
391: * the cause exception, cannot be <code>null</code>.
392: *
393: * @return
394: * an appropriate {@link ProgrammingException} that can be thrown by the
395: * calling method, never <code>null</code>.
396: */
397: public static ProgrammingException logProgrammingError(
398: Throwable cause) {
399: return logProgrammingError(null, cause);
400: }
401:
402: /**
403: * Logs a programming error with an optional cause exception and an optional
404: * message, and returns a <code>ProgrammingException</code> object for it.
405: *
406: * @param detail
407: * the detail message, can be <code>null</code>.
408: *
409: * @param cause
410: * the cause exception, cannot be <code>null</code>.
411: *
412: * @return
413: * an appropriate {@link ProgrammingException} that can be thrown by the
414: * calling method, never <code>null</code>.
415: *
416: * @since XINS 2.0.
417: */
418: public static ProgrammingException logProgrammingError(
419: String detail, Throwable cause) {
420:
421: // Determine detecting class and method
422: String detectingClass = getCallingClass();
423: String detectingMethod = getCallingMethod();
424:
425: String sourceClass = null;
426: String sourceMethod = null;
427:
428: try {
429:
430: // Determine the source of the exception
431: StackTraceElement[] trace = cause.getStackTrace();
432:
433: for (int i = 1; i < trace.length && sourceClass == null; i++) {
434: StackTraceElement stackTraceElement = trace[i];
435: if (stackTraceElement.getClassName().equals(
436: detectingClass)
437: && stackTraceElement.getMethodName().equals(
438: detectingMethod)) {
439:
440: // Go one level up the stack trace to know which method threw the exception
441: StackTraceElement source = trace[i - 1];
442: sourceClass = source.getClassName();
443: sourceMethod = source.getMethodName();
444: }
445: }
446: if (sourceClass == null) {
447: sourceClass = "<unknown>";
448: sourceMethod = "<unknown>";
449: }
450:
451: // If there's any exception, then fallback to default values
452: } catch (Throwable t) {
453: sourceClass = "<unknown>";
454: sourceMethod = "<unknown>";
455: }
456:
457: // Log the programming error
458: return logProgrammingError(detectingClass, detectingMethod,
459: sourceClass, sourceMethod, detail, cause);
460: }
461:
462: /**
463: * }
464: * Logs a programming error with an optional cause exception, and returns a
465: * <code>ProgrammingException</code> object for it.
466: *
467: * @param detectingClass
468: * the name of the class that detected the problem, or
469: * <code>null</code> if unknown.
470: *
471: * @param detectingMethod
472: * the name of the method within the <code>detectingClass</code> that
473: * detected the problem, or <code>null</code> if unknown.
474: *
475: * @param subjectClass
476: * the name of the class which exposes the programming error, or
477: * <code>null</code> if unknown.
478: *
479: * @param subjectMethod
480: * the name of the method (within the <code>subjectClass</code>) which
481: * exposes the programming error, or <code>null</code> if unknown.
482: *
483: * @param detail
484: * the detail message, can be <code>null</code>.
485: *
486: * @param cause
487: * the cause exception, can be <code>null</code>.
488: *
489: * @return
490: * an appropriate {@link ProgrammingException} that can be thrown by the
491: * calling method, never <code>null</code>.
492: *
493: * @deprecated Since XINS 2.0, use {@link #logProgrammingError(String detail, Throwable cause)}
494: */
495: public static ProgrammingException logProgrammingError(
496: String detectingClass, String detectingMethod,
497: String subjectClass, String subjectMethod, String detail,
498: Throwable cause) {
499:
500: // Log programming error (not due to exception)
501: if (cause == null) {
502: Log.log_1050(detectingClass, detectingMethod, subjectClass,
503: subjectMethod, detail);
504:
505: // Log programming error (due to exception)
506: } else {
507: Log.log_1052(cause, detectingClass, detectingMethod,
508: subjectClass, subjectMethod, detail);
509: }
510:
511: // Construct and return ProgrammingException object
512: return new ProgrammingException(detectingClass,
513: detectingMethod, subjectClass, subjectMethod, detail,
514: cause);
515:
516: }
517:
518: /**
519: * Logs a programming error with no cause exception, and returns a
520: * <code>ProgrammingException</code> object for it.
521: *
522: * @param detectingClass
523: * the name of the class that detected the problem, or
524: * <code>null</code> if unknown.
525: *
526: * @param detectingMethod
527: * the name of the method within the <code>detectingClass</code> that
528: * detected the problem, or <code>null</code> if unknown.
529: *
530: * @param subjectClass
531: * the name of the class which exposes the programming error, or
532: * <code>null</code> if unknown.
533: *
534: * @param subjectMethod
535: * the name of the method (within the <code>subjectClass</code>) which
536: * exposes the programming error, or <code>null</code> if unknown.
537: *
538: * @param detail
539: * the detail message, can be <code>null</code>.
540: *
541: * @return
542: * an appropriate {@link ProgrammingException} that can be thrown by the
543: * calling method, never <code>null</code>.
544: *
545: * @deprecated Since XINS 2.0, use {@link #logProgrammingError(String detail)}
546: */
547: public static ProgrammingException logProgrammingError(
548: String detectingClass, String detectingMethod,
549: String subjectClass, String subjectMethod, String detail) {
550:
551: return logProgrammingError(detectingClass, detectingMethod,
552: subjectClass, subjectMethod, detail, null);
553: }
554:
555: /**
556: * Determines the name of the specified class.
557: *
558: * @param c
559: * the class to determine the name for, not <code>null</code>.
560: *
561: * @return
562: * the name of the class, never <code>null</code>.
563: *
564: * @throws IllegalArgumentException
565: * if <code>c == null</code>.
566: *
567: * @since XINS 1.2.0
568: */
569: public static String getNameOfClass(Class c)
570: throws IllegalArgumentException {
571:
572: // Check preconditions
573: MandatoryArgumentChecker.check("c", c);
574:
575: // Handle arrays
576: if (c.isArray()) {
577: Class comp = c.getComponentType();
578: String name = getNameOfClass(comp);
579: if (c.getName().charAt(0) == '[') {
580: name += "[]";
581: }
582: return name;
583:
584: // Handle non-arrays (primitives and classes)
585: } else {
586: return c.getName();
587: }
588: }
589:
590: /**
591: * Determines the name of the class of the specified object.
592: *
593: * @param object
594: * the object to determine the name of the class for, not
595: * <code>null</code>.
596: *
597: * @return
598: * the name of the class, never <code>null</code>.
599: *
600: * @throws IllegalArgumentException
601: * if <code>object == null</code>.
602: *
603: * @since XINS 1.2.0
604: */
605: public static String getClassName(Object object)
606: throws IllegalArgumentException {
607:
608: // Check preconditions
609: MandatoryArgumentChecker.check("object", object);
610:
611: return getNameOfClass(object.getClass());
612: }
613: }
|