001    // Copyright 2008 Waterken Inc. under the terms of the MIT X license
002    // found at http://www.opensource.org/licenses/mit-license.html
003    package org.waterken.trace.application;
004    
005    import java.io.Serializable;
006    import java.lang.reflect.Member;
007    import java.lang.reflect.Proxy;
008    
009    import org.joe_e.Struct;
010    import org.joe_e.array.IntArray;
011    import org.joe_e.array.PowerlessArray;
012    import org.ref_send.log.CallSite;
013    import org.ref_send.log.Trace;
014    import org.waterken.trace.Tracer;
015    
016    /**
017     * Produces a stack trace composed of only calls initiated by application code.
018     */
019    public final class
020    ApplicationTracer {
021        private ApplicationTracer() {}
022    
023        /**
024         * Constructs an instance.
025         * @param code  application class loader
026         */
027        static public Tracer
028        make(final ClassLoader code) {
029            class TracerX extends Struct implements Tracer, Serializable {
030                static private final long serialVersionUID = 1L;
031                
032                public long
033                timestamp() { return System.currentTimeMillis(); }
034                
035                public String
036                readException(final Throwable e) { return e.getMessage(); }
037    
038                public Trace
039                traceException(final Throwable e) {return trace(e.getStackTrace());}
040                
041                public Trace
042                traceMember(final Member lambda) {
043                    return trace(new StackTraceElement(
044                        lambda.getDeclaringClass().getName(), lambda.getName(),
045                        null, -1));
046                    // TODO: want the line number of the method declaration
047                }
048    
049                public Trace
050                traceHere() {return trace(Thread.currentThread().getStackTrace());}
051                
052                private Trace
053                trace(final StackTraceElement... frames) {
054                    final PowerlessArray.Builder<CallSite> sites =
055                        PowerlessArray.builder(frames.length);
056                    for (final StackTraceElement frame : frames) {
057                        final int line = frame.getLineNumber();
058                        if (1 == line) { continue; }    // skip synthetic method
059                        
060                        // Is this frame from application code or system code?
061                        boolean included = false;
062                        try {
063                            final Class<?> c = code.loadClass(frame.getClassName());
064                            if (!Proxy.isProxyClass(c)) {
065                                final ClassLoader lib = c.getClassLoader();
066                                final ClassLoader sys =
067                                    ApplicationTracer.class.getClassLoader();
068                                for (ClassLoader i=code; i!=sys; i=i.getParent()) {
069                                    if (lib == i) {
070                                        included = true;
071                                        break;
072                                    }
073                                }
074                            }
075                        } catch (final ClassNotFoundException e) {
076                            // Assume it's not an application code class.
077                        }
078                        if (!included) { continue; }
079                        
080                        // Describe the application stack frame.
081                        final String name; {
082                            final String fqn = frame.getClassName();
083                            final String cn = fqn.substring(fqn.lastIndexOf('.')+1);
084                            final String sn = cn.replace("$1",".").replace("$",".");
085                            name = sn + "." + frame.getMethodName();
086                        }
087                        final String source; {
088                            final String fqn = frame.getClassName();
089                            final int end = fqn.indexOf('$');
090                            final String top = -1==end ? fqn : fqn.substring(0,end);
091                            source = top.replace('.', '/') + ".java";
092                        }
093                        sites.append(new CallSite(name, source, line > 1
094                            ? PowerlessArray.array(IntArray.array(line)) : null));
095                    }
096                    return sites.length() != 0 ? new Trace(sites.snapshot()) : null;
097                }
098            }
099            return new TracerX();
100        }
101    }