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