Clover coverage report - Code Coverage for hivemind release 1.0-rc-1
Coverage timestamp: Wed Aug 25 2004 13:06:02 EDT
file stats: LOC: 308   Methods: 12
NCLOC: 193   Classes: 1
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
LoggingInterceptorFactory.java 94.4% 97.9% 100% 97.6%
coverage coverage
 1   
 //  Copyright 2004 The Apache Software Foundation
 2   
 //
 3   
 // Licensed under the Apache License, Version 2.0 (the "License");
 4   
 // you may not use this file except in compliance with the License.
 5   
 // You may obtain a copy of the License at
 6   
 //
 7   
 //     http://www.apache.org/licenses/LICENSE-2.0
 8   
 //
 9   
 // Unless required by applicable law or agreed to in writing, software
 10   
 // distributed under the License is distributed on an "AS IS" BASIS,
 11   
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12   
 // See the License for the specific language governing permissions and
 13   
 // limitations under the License.
 14   
 
 15   
 package org.apache.hivemind.service.impl;
 16   
 
 17   
 import java.lang.reflect.Constructor;
 18   
 import java.lang.reflect.Method;
 19   
 import java.lang.reflect.Modifier;
 20   
 import java.util.Iterator;
 21   
 import java.util.List;
 22   
 
 23   
 import org.apache.commons.logging.Log;
 24   
 import org.apache.hivemind.ApplicationRuntimeException;
 25   
 import org.apache.hivemind.InterceptorStack;
 26   
 import org.apache.hivemind.ServiceInterceptorFactory;
 27   
 import org.apache.hivemind.internal.Module;
 28   
 import org.apache.hivemind.methodmatch.MethodMatcher;
 29   
 import org.apache.hivemind.service.BodyBuilder;
 30   
 import org.apache.hivemind.service.ClassFab;
 31   
 import org.apache.hivemind.service.ClassFabUtils;
 32   
 import org.apache.hivemind.service.ClassFactory;
 33   
 import org.apache.hivemind.service.MethodContribution;
 34   
 import org.apache.hivemind.service.MethodFab;
 35   
 import org.apache.hivemind.service.MethodSignature;
 36   
 
 37   
 /**
 38   
  * An interceptor factory that adds logging capability to a service.
 39   
  * The logging is based upon the Jakarta 
 40   
  * <a href="http://jakarta.apache.org/commons/logging.html">commons-logging</a> toolkit, 
 41   
  * which makes
 42   
  * it very transportable.
 43   
  * 
 44   
  * <p>
 45   
  * The interceptor will log entry to each method and exit from the method
 46   
  * (with return value), plus log any exceptions thrown by the method.
 47   
  * The logger used is the <em>id of the service</em>, which is not necessarily
 48   
  * the name of the implementing class.  Logging occurs at the debug level.
 49   
  *
 50   
  * @author Howard Lewis Ship
 51   
  */
 52   
 public class LoggingInterceptorFactory implements ServiceInterceptorFactory
 53   
 {
 54   
     private ClassFactory _factory;
 55   
     private String _serviceId;
 56   
 
 57   
     /**
 58   
      * Creates a method that delegates to the _inner object; this is used for
 59   
      * methods that are not logged.
 60   
      */
 61  1
     private void addPassThruMethodImplementation(ClassFab classFab, MethodSignature sig)
 62   
     {
 63  1
         BodyBuilder builder = new BodyBuilder();
 64  1
         builder.begin();
 65   
 
 66  1
         builder.add("return ($r) _inner.");
 67  1
         builder.add(sig.getName());
 68  1
         builder.addln("($$);");
 69   
 
 70  1
         builder.end();
 71   
 
 72  1
         classFab.addMethod(Modifier.PUBLIC, sig, builder.toString());
 73   
     }
 74   
 
 75  17
     protected void addServiceMethodImplementation(ClassFab classFab, MethodSignature sig)
 76   
     {
 77  17
         Class returnType = sig.getReturnType();
 78  17
         String methodName = sig.getName();
 79   
 
 80  17
         boolean isVoid = (returnType == void.class);
 81   
 
 82  17
         BodyBuilder builder = new BodyBuilder();
 83   
 
 84  17
         builder.begin();
 85  17
         builder.addln("boolean debug = _log.isDebugEnabled();");
 86   
 
 87  17
         builder.addln("if (debug)");
 88  17
         builder.add("  org.apache.hivemind.service.impl.LoggingUtils.entry(_log, ");
 89  17
         builder.addQuoted(methodName);
 90  17
         builder.addln(", $args);");
 91   
 
 92  17
         if (!isVoid)
 93   
         {
 94  7
             builder.add(ClassFabUtils.getJavaClassName(returnType));
 95  7
             builder.add(" result = ");
 96   
         }
 97   
 
 98  17
         builder.add("_inner.");
 99  17
         builder.add(methodName);
 100  17
         builder.addln("($$);");
 101   
 
 102  17
         if (isVoid)
 103   
         {
 104  10
             builder.addln("if (debug)");
 105  10
             builder.add("  org.apache.hivemind.service.impl.LoggingUtils.voidExit(_log, ");
 106  10
             builder.addQuoted(methodName);
 107  10
             builder.addln(");");
 108   
         }
 109   
         else
 110   
         {
 111  7
             builder.addln("if (debug)");
 112  7
             builder.add("  org.apache.hivemind.service.impl.LoggingUtils.exit(_log, ");
 113  7
             builder.addQuoted(methodName);
 114  7
             builder.addln(", ($w)result);");
 115  7
             builder.addln("return result;");
 116   
         }
 117   
 
 118  17
         builder.end();
 119   
 
 120  17
         MethodFab methodFab = classFab.addMethod(Modifier.PUBLIC, sig, builder.toString());
 121   
 
 122  17
         builder.clear();
 123   
 
 124  17
         builder.begin();
 125  17
         builder.add("org.apache.hivemind.service.impl.LoggingUtils.exception(_log, ");
 126  17
         builder.addQuoted(methodName);
 127  17
         builder.addln(", $e);");
 128  17
         builder.addln("throw $e;");
 129  17
         builder.end();
 130   
 
 131  17
         String body = builder.toString();
 132   
 
 133  17
         Class[] exceptions = sig.getExceptionTypes();
 134   
 
 135  17
         int count = exceptions.length;
 136   
 
 137  17
         for (int i = 0; i < count; i++)
 138   
         {
 139  0
             methodFab.addCatch(exceptions[i], body);
 140   
         }
 141   
 
 142   
         // Catch and log any runtime exceptions, in addition to the
 143   
         // checked exceptions.
 144   
 
 145  17
         methodFab.addCatch(RuntimeException.class, body);
 146   
     }
 147   
 
 148  11
     protected void addServiceMethods(InterceptorStack stack, ClassFab fab, List parameters)
 149   
     {
 150  11
         boolean toString = false;
 151  11
         Method[] methods = stack.getServiceInterface().getMethods();
 152   
 
 153  11
         MethodMatcher matcher = buildMethodMatcher(parameters);
 154   
 
 155  11
         for (int i = 0; i < methods.length; i++)
 156   
         {
 157  18
             Method m = methods[i];
 158  18
             MethodSignature sig = new MethodSignature(m);
 159   
 
 160  18
             if (includeMethod(matcher, sig))
 161  17
                 addServiceMethodImplementation(fab, sig);
 162   
             else
 163  1
                 addPassThruMethodImplementation(fab, sig);
 164   
 
 165  18
             toString |= ClassFabUtils.isToString(m);
 166   
         }
 167   
 
 168  11
         if (!toString)
 169  10
             addToStringMethod(stack, fab);
 170   
     }
 171   
 
 172   
     /**
 173   
      * Creates a toString() method that identify the interceptor service id,
 174   
      * the intercepted service id, and the service interface class name).
 175   
      */
 176  10
     protected void addToStringMethod(InterceptorStack stack, ClassFab fab)
 177   
     {
 178  10
         ClassFabUtils.addToStringMethod(
 179   
             fab,
 180   
             "<LoggingInterceptor for "
 181   
                 + stack.getServiceExtensionPointId()
 182   
                 + "("
 183   
                 + stack.getServiceInterface().getName()
 184   
                 + ")>");
 185   
 
 186   
     }
 187   
 
 188  11
     private MethodMatcher buildMethodMatcher(List parameters)
 189   
     {
 190  11
         MethodMatcher result = null;
 191   
 
 192  11
         Iterator i = parameters.iterator();
 193  11
         while (i.hasNext())
 194   
         {
 195  2
             MethodContribution mc = (MethodContribution) i.next();
 196   
 
 197  2
             if (result == null)
 198  1
                 result = new MethodMatcher();
 199   
 
 200  2
             result.put(mc.getMethodPattern(), mc);
 201   
         }
 202   
 
 203  11
         return result;
 204   
     }
 205   
 
 206  11
     private Class constructInterceptorClass(InterceptorStack stack, List parameters)
 207   
     {
 208  11
         Class serviceInterfaceClass = stack.getServiceInterface();
 209  11
         Module module = stack.getServiceModule();
 210   
 
 211  11
         String name = ClassFabUtils.generateClassName("Interceptor");
 212   
 
 213  11
         ClassFab classFab = _factory.newClass(name, Object.class, module);
 214   
 
 215  11
         classFab.addInterface(serviceInterfaceClass);
 216   
 
 217  11
         createInfrastructure(stack, classFab);
 218   
 
 219  11
         addServiceMethods(stack, classFab, parameters);
 220   
 
 221  11
         return classFab.createClass();
 222   
     }
 223   
 
 224  11
     private void createInfrastructure(InterceptorStack stack, ClassFab classFab)
 225   
     {
 226  11
         Class topClass = stack.peek().getClass();
 227   
 
 228  11
         classFab.addField("_log", Log.class);
 229   
 
 230   
         // This is very important: since we know the instance of the top object (the next
 231   
         // object in the pipeline for this service), we can build the instance variable
 232   
         // and constructor to use the exact class rather than the service interface.
 233   
         // That's more efficient at runtime, lowering the cost of using interceptors.
 234   
         // One of the reasons I prefer Javassist over JDK Proxies.
 235   
 
 236  11
         classFab.addField("_inner", topClass);
 237   
 
 238  11
         classFab.addConstructor(
 239   
             new Class[] { Log.class, topClass },
 240   
             null,
 241   
             "{ _log = $1; _inner = $2; }");
 242   
     }
 243   
 
 244   
     /**
 245   
      * Creates the interceptor.
 246   
      * The class that is created is cached; if an interceptor is requested
 247   
      * for the same extension point, then the previously constructed class
 248   
      * is reused (this can happen with the threaded service model, for example,
 249   
      * when a thread-local service implementation is created for different threads).
 250   
      */
 251  11
     public void createInterceptor(
 252   
         InterceptorStack stack,
 253   
         Module contributingModule,
 254   
         List parameters)
 255   
     {
 256  11
         Class interceptorClass = constructInterceptorClass(stack, parameters);
 257   
 
 258  11
         try
 259   
         {
 260  11
             Object interceptor = instantiateInterceptor(stack, interceptorClass);
 261   
 
 262  11
             stack.push(interceptor);
 263   
         }
 264   
         catch (Exception ex)
 265   
         {
 266  0
             throw new ApplicationRuntimeException(
 267   
                 ServiceMessages.errorInstantiatingInterceptor(
 268   
                     _serviceId,
 269   
                     stack,
 270   
                     interceptorClass,
 271   
                     ex),
 272   
                 ex);
 273   
         }
 274   
     }
 275   
 
 276   
 
 277   
 
 278  18
     private boolean includeMethod(MethodMatcher matcher, MethodSignature sig)
 279   
     {
 280  18
         if (matcher == null)
 281  15
             return true;
 282   
 
 283  3
         MethodContribution mc = (MethodContribution) matcher.get(sig);
 284   
 
 285  3
         return mc == null || mc.getInclude();
 286   
     }
 287   
 
 288  11
     private Object instantiateInterceptor(InterceptorStack stack, Class interceptorClass)
 289   
         throws Exception
 290   
     {
 291  11
         Object stackTop = stack.peek();
 292   
 
 293  11
         Constructor c = interceptorClass.getConstructors()[0];
 294   
 
 295  11
         return c.newInstance(new Object[] { stack.getServiceLog(), stackTop });
 296   
     }
 297   
 
 298  11
     public void setFactory(ClassFactory factory)
 299   
     {
 300  11
         _factory = factory;
 301   
     }
 302   
 
 303  11
     public void setServiceId(String string)
 304   
     {
 305  11
         _serviceId = string;
 306   
     }
 307   
 }
 308