001: /* ====================================================================
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 1997-2003 The Apache Software Foundation. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution,
020: * if any, must include the following acknowledgment:
021: * "This product includes software developed by the
022: * Apache Software Foundation (http://www.apache.org/)."
023: * Alternately, this acknowledgment may appear in the software
024: * itself, if and wherever such third-party acknowledgments
025: * normally appear.
026: *
027: * 4. The names "Jakarta", "Avalon", and "Apache Software Foundation"
028: * must not be used to endorse or promote products derived from this
029: * software without prior written permission. For written
030: * permission, please contact apache@apache.org.
031: *
032: * 5. Products derived from this software may not be called "Apache",
033: * nor may "Apache" appear in their name, without prior written
034: * permission of the Apache Software Foundation.
035: *
036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: * ====================================================================
049: *
050: * This software consists of voluntary contributions made by many
051: * individuals on behalf of the Apache Software Foundation. For more
052: * information on the Apache Software Foundation, please see
053: * <http://www.apache.org/>.
054: */
055: package org.apache.log.util;
056:
057: import java.io.PrintWriter;
058: import java.io.StringWriter;
059:
060: /**
061: * A set of utilities to inspect current stack frame.
062: *
063: * @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
064: * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
065: * @author <a href="mailto:stuart.roebuck@adolos.com">Stuart Roebuck</a>
066: * @version CVS $Revision: 1.13 $ $Date: 2003/02/09 23:33:25 $
067: */
068: public final class StackIntrospector {
069: /**
070: * Hack to get the call stack as an array of classes. The
071: * SecurityManager class provides it as a protected method, so
072: * change it to public through a new method !
073: */
074: private static final class CallStack extends SecurityManager {
075: /**
076: * Returns the current execution stack as an array of classes.
077: * The length of the array is the number of methods on the execution
078: * stack. The element at index 0 is the class of the currently executing
079: * method, the element at index 1 is the class of that method's caller,
080: * and so on.
081: */
082: public Class[] get() {
083: return getClassContext();
084: }
085: }
086:
087: ///Method to cache CallStack hack as needed
088: private static CallStack c_callStack;
089:
090: /**
091: * Private constructor to block instantiation.
092: *
093: */
094: private StackIntrospector() {
095: }
096:
097: /**
098: * Create Hack SecurityManager to get CallStack.
099: *
100: * @return the CallStack object
101: * @exception SecurityException if an existing SecurityManager disallows construction
102: * of another SecurityManager
103: */
104: private static synchronized CallStack getCallStack()
105: throws SecurityException {
106: if (null == c_callStack) {
107: //Lazily create CallStack accessor as appropriate
108: c_callStack = new CallStack();
109: }
110:
111: return c_callStack;
112: }
113:
114: /**
115: * Find the caller of the passed in Class.
116: * May return null if caller not found on execution stack
117: *
118: * @param clazz the Class to search for on stack to find caller of
119: * @return the Class of object that called parrameter class
120: * @exception SecurityException if an existing SecurityManager disallows construction
121: * of another SecurityManager and thus blocks method results
122: */
123: public static final Class getCallerClass(final Class clazz)
124: throws SecurityException {
125: return getCallerClass(clazz, 0);
126: }
127:
128: /**
129: * Find the caller of the passed in Class.
130: * May return null if caller not found on execution stack
131: *
132: * @param clazz the Class to search for on stack to find caller of
133: * @param stackDepthOffset Offset call-stack depth to find caller
134: * @return the Class of object that called parrameter class
135: * @exception SecurityException if an existing SecurityManager disallows construction
136: * of another SecurityManager and thus blocks method results
137: */
138: public static final Class getCallerClass(final Class clazz,
139: int stackDepthOffset) {
140: final Class[] stack = getCallStack().get();
141:
142: // Traverse the call stack in reverse order until we find clazz
143: for (int i = stack.length - 1; i >= 0; i--) {
144: if (clazz.isAssignableFrom(stack[i])) {
145: // Found : the caller is the previous stack element
146: return stack[i + 1 + stackDepthOffset];
147: }
148: }
149:
150: //Unable to locate class in call stack
151: return null;
152: }
153:
154: /**
155: * Get the method path name for the method from which the LogEvent was
156: * created, this includes the path name and the source filename and line
157: * number if the source was compiled with debugging on.
158: *
159: * @param clazz the Class to search for on stack to find caller of
160: * @return The method path name in the form "the.package.path.Method"
161: */
162: public static final String getCallerMethod(final Class clazz) {
163: final String className = clazz.getName();
164:
165: //Extract stack into a StringBuffer
166: final StringWriter sw = new StringWriter();
167: final Throwable throwable = new Throwable();
168: throwable.printStackTrace(new PrintWriter(sw, true));
169: final StringBuffer buffer = sw.getBuffer();
170:
171: //Cache vars used in loop
172: final StringBuffer line = new StringBuffer();
173: final int length = buffer.length();
174:
175: //setup state
176: boolean found = false;
177: int state = 0;
178:
179: //parse line
180: for (int i = 0; i < length; i++) {
181: final char ch = buffer.charAt(i);
182:
183: switch (state) {
184: case 0:
185: //Strip the first line from input
186: if ('\n' == ch) {
187: state = 1;
188: }
189: break;
190:
191: case 1:
192: //strip 't' from 'at'
193: if ('t' == ch) {
194: state = 2;
195: }
196: break;
197:
198: case 2:
199: //Strip space after 'at'
200: line.setLength(0);
201: state = 3;
202: break;
203:
204: case 3:
205: //accumulate all characters to end of line
206: if ('\n' != ch) {
207: line.append(ch);
208: } else {
209: //At this stage you have the line that looks like
210: //com.biz.SomeClass.someMethod(SomeClass.java:22)
211: final String method = line.toString();
212:
213: ///Determine if line is a match for class
214: final boolean match = method.startsWith(className);
215: if (!found && match) {
216: //If this is the first time we cound class then
217: //set found to true and look for caller into class
218: found = true;
219: } else if (found && !match) {
220: //We have now located caller of Clazz
221: return method;
222: }
223:
224: //start parsing from start of line again
225: state = 1;
226: }
227: }
228: }
229:
230: return "";
231: }
232:
233: /**
234: * Return the current call stack as a String, starting with the first call
235: * in the stack after a reference to the <code>clazz</code> class, and then
236: * display <code>entries</code> entries.
237: *
238: * <p>This can be useful for debugging code to determine where calls to a
239: * method are coming from.</p>
240: *
241: * @param clazz the last class on the stack you are <i>not</i> interested in!
242: * @param entries the number of stack lines to return.
243: *
244: * @return The method path name in the form "the.package.path.Method"
245: */
246: public static final String getRecentStack(final Class clazz,
247: int entries) {
248: final String className = clazz.getName();
249:
250: //Extract stack into a StringBuffer
251: final StringWriter sw = new StringWriter();
252: final Throwable throwable = new Throwable();
253: throwable.printStackTrace(new PrintWriter(sw, true));
254: final StringBuffer buffer = sw.getBuffer();
255:
256: //Cache vars used in loop
257: final StringBuffer line = new StringBuffer();
258: final StringBuffer stack = new StringBuffer();
259: final int length = buffer.length();
260:
261: //setup state
262: boolean found = false;
263: int state = 0;
264:
265: //parse line
266: for (int i = 0; i < length; i++) {
267: final char ch = buffer.charAt(i);
268:
269: switch (state) {
270: case 0:
271: //Strip the first line from input
272: if ('\n' == ch) {
273: state = 1;
274: }
275: break;
276:
277: case 1:
278: //strip 't' from 'at'
279: if ('t' == ch) {
280: state = 2;
281: }
282: break;
283:
284: case 2:
285: //Strip space after 'at'
286: line.setLength(0);
287: state = 3;
288: break;
289:
290: case 3:
291: //accumulate all characters to end of line
292: if ('\n' != ch) {
293: line.append(ch);
294: } else {
295: //At this stage you have the line that looks like
296: //com.biz.SomeClass.someMethod(SomeClass.java:22)
297: final String method = line.toString();
298:
299: ///Determine if line is a match for class
300: final boolean match = method.startsWith(className);
301: if (!found && match) {
302: //If this is the first time we cound class then
303: //set found to true and look for caller into class
304: found = true;
305: } else if (found && !match) {
306: //We are looking at the callers of Clazz
307: stack.append(method);
308: entries--;
309: if (entries == 0) {
310: return stack.toString();
311: }
312: stack.append("\n");
313: }
314:
315: //start parsing from start of line again
316: state = 1;
317: }
318: }
319: }
320:
321: return "";
322: }
323: }
|