root/trunk/src/java/org/jcoderz/commons/tracing/TracingProxy.java

Revision 1628, 8.4 kB (checked in by amandel, 2 years ago)

Add work in progress tracing components.
- Extended TracingProxy? that allows different Traces to be used
- TracingInjector? that allows aspect like injection of the tracing aspect.

Please note that this is still work in progress.

  • Property svn:mime-type set to text/plain
Line 
1/*
2 * $Id: LoggingProxy.java 1011 2008-06-16 17:57:36Z amandel $
3 *
4 * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *    * Redistributions of source code must retain the above copyright
11 *      notice, this list of conditions and the following disclaimer.
12 *    * Redistributions in binary form must reproduce the above
13 *      copyright notice, this list of conditions and the following
14 *      disclaimer in the documentation and/or other materials
15 *      provided with the distribution.
16 *    * Neither the name of the jCoderZ.org Project nor the names of
17 *      its contributors may be used to endorse or promote products
18 *      derived from this software without specific prior written
19 *      permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33package org.jcoderz.commons.tracing;
34
35import java.lang.reflect.InvocationHandler;
36import java.lang.reflect.InvocationTargetException;
37import java.lang.reflect.Method;
38import java.lang.reflect.Modifier;
39import java.lang.reflect.Proxy;
40import java.util.Arrays;
41import java.util.HashSet;
42import java.util.Set;
43
44import org.jcoderz.commons.ArgumentMalformedException;
45import org.jcoderz.commons.tracing.Tracer.TracingToken;
46
47/**
48 * <p>
49 * This class can be used to proxy any object, providing tracing for
50 * all <i>interfaces</i> of the object.
51 * </p>
52 * <p>
53 * <b>Note:</b> Java Dynamic Proxies only work on <i>interfaces</i>.
54 * The object returned by the {@link #getProxy(Object)} can be cast to
55 * any interface implemented by the argument or one of its ancestors. It
56 * can't, however, be cast to an implementation class.
57 * </p>
58 *
59 * @author Andreas Mandel
60 */
61public final class TracingProxy
62      implements InvocationHandler
63{
64   private final Object mRealObject;
65   private final String mRealObjectClassName;
66   private final Tracer mObjectTracer;
67
68   /**
69    * Create a proxy that directs all calls to the real object and logs all
70    * method calls with entering/exiting/throwing, using the given logger.
71    *
72    * @param realObject the object for which a proxy is created
73    * @param logger the logger to which calls are logged
74    */
75   private TracingProxy (Object realObject, Tracer tracer)
76   {
77      mRealObject = realObject;
78      mRealObjectClassName = mRealObject.getClass().getName();
79      mObjectTracer = tracer;
80   }
81
82   /**
83    * Static factory that wraps an object into a proxy depending on the
84    * log level for that object.
85    *
86    * @param obj an object for which a proxy should be created
87    * @return a logging proxy for the obj, if the log level for that
88    *       object is FINER or finest, the object itself otherwise
89    */
90   public static Object getProxy (Object obj, Class tracerClass)
91   {
92      final String classname = obj.getClass().getName();
93      final Tracer tracer = getTracer(tracerClass, classname);
94
95      final Object proxy;
96      if (tracer.isTracing())
97      {
98         // collect all interfaces implemented by this objects class and
99         // its super classes
100         //  Note: We do not add super-interfaces here....
101         final Set interfaces = new HashSet();
102         Class currentClass = obj.getClass();
103         while (currentClass != null)
104         {
105            interfaces.addAll(Arrays.asList(currentClass.getInterfaces()));
106            currentClass = currentClass.getSuperclass();
107         }
108
109         proxy = Proxy.newProxyInstance(
110               obj.getClass().getClassLoader(),
111               (Class[]) interfaces.toArray(new Class[interfaces.size()]),
112               new TracingProxy(obj, tracer));
113      }
114      else
115      {
116         proxy = obj;
117      }
118      return proxy;
119   }
120
121   /**
122    * Log the entering, exiting and throwing events of the proxied object.
123    *
124    * @see java.lang.reflect.InvocationHandler#invoke(
125    *       java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
126    */
127   public Object invoke (Object proxy, Method method, Object[] args)
128         throws Throwable
129   {
130      final TracingToken tt;
131      if (mObjectTracer.isTracing())
132      {
133         if (args == null || !mObjectTracer.isTracingArguments())
134         {
135            tt = mObjectTracer.entering(
136                mRealObjectClassName, method.getName());
137         }
138         else
139         {
140             tt = mObjectTracer.entering(
141                  mRealObjectClassName, method.getName(), args);
142         }
143      }
144      else
145      {
146          tt = null;
147      }
148
149      final Object result = invokeMethod(method, args, tt);
150
151      if (tt != null)
152      {
153         if (result != null
154             || method.getReturnType() != Void.TYPE
155             || !mObjectTracer.isTracingArguments())
156         {
157           mObjectTracer.exiting(tt, result);
158         }
159         else
160         {
161           mObjectTracer.exiting(tt);
162         }
163      }
164      return result;
165   }
166
167   private static Tracer getTracer(Class tracer, String className)
168   {
169       final Tracer result;
170       try
171       {
172           final Method method
173               = tracer.getMethod("getTracer", new Class[] {String.class});
174           if (!Modifier.isStatic(method.getModifiers()))
175           {
176               throw new ArgumentMalformedException(
177                   "tracer", tracer,
178                   "Factory method 'getTracer' must be static.");
179           }
180           if (!Modifier.isPublic(method.getModifiers()))
181           {
182               throw new ArgumentMalformedException(
183                   "tracer", tracer,
184                   "Factory method 'getTracer' must be public.");
185           }
186           result = (Tracer) method.invoke(null, new Object[] {className});
187       }
188       catch (IllegalArgumentException e)
189       {
190           throw new ArgumentMalformedException(
191               "tracer", tracer,
192               "The static tracer factory did not accept the String argument.",
193               e);
194       }
195       catch (IllegalAccessException e)
196       {
197           throw new ArgumentMalformedException(
198               "tracer", tracer,
199               "The static tracer factory did deny access.",
200               e);
201       }
202       catch (InvocationTargetException e)
203       {
204           throw new ArgumentMalformedException(
205               "tracer", tracer,
206               "The static tracer factory threw an exception with detail: "
207               + e.getMessage() + ".",
208               e);
209       }
210       catch (SecurityException e)
211       {
212           throw new ArgumentMalformedException(
213               "tracer", tracer,
214               "Failed to look up static factory method.",
215               e);
216       }
217       catch (NoSuchMethodException e)
218       {
219           throw new ArgumentMalformedException(
220               "tracer", tracer,
221               "The Tracer implementation must implement a static factory "
222               + "method 'public Tracer getTracer(String)'.",
223               e);
224       }
225       return result;
226   }
227   
228   private Object invokeMethod (
229       Method method, Object[] args, TracingToken tt)
230       throws Throwable
231   {
232      final Object result;
233      try
234      {
235         result = method.invoke(mRealObject, args);
236      }
237      catch (InvocationTargetException x)
238      {
239         if (tt != null)
240         {
241            mObjectTracer.throwing(tt, x.getCause());
242         }
243         throw x.getCause();
244      }
245      catch (Exception x)
246      {
247         if (tt != null)
248         {
249            mObjectTracer.throwing(tt, x);
250         }
251         throw x;
252      }
253      return result;
254   }
255}
Note: See TracBrowser for help on using the browser.