Clover coverage report - Code Coverage for hivemind release 1.1-alpha-1
Coverage timestamp: Tue Jan 18 2005 07:55:08 EST
file stats: LOC: 303   Methods: 12
NCLOC: 189   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, 2005 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.
 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  18
     protected void addServiceMethodImplementation(ClassFab classFab, MethodSignature sig)
 76   
     {
 77  18
         Class returnType = sig.getReturnType();
 78  18
         String methodName = sig.getName();
 79   
 
 80  18
         boolean isVoid = (returnType == void.class);
 81   
 
 82  18
         BodyBuilder builder = new BodyBuilder();
 83   
 
 84  18
         builder.begin();
 85  18
         builder.addln("boolean debug = _log.isDebugEnabled();");
 86   
 
 87  18
         builder.addln("if (debug)");
 88  18
         builder.add("  org.apache.hivemind.service.impl.LoggingUtils.entry(_log, ");
 89  18
         builder.addQuoted(methodName);
 90  18
         builder.addln(", $args);");
 91   
 
 92  18
         if (!isVoid)
 93   
         {
 94  7
             builder.add(ClassFabUtils.getJavaClassName(returnType));
 95  7
             builder.add(" result = ");
 96   
         }
 97   
 
 98  18
         builder.add("_inner.");
 99  18
         builder.add(methodName);
 100  18
         builder.addln("($$);");
 101   
 
 102  18
         if (isVoid)
 103   
         {
 104  11
             builder.addln("if (debug)");
 105  11
             builder.add("  org.apache.hivemind.service.impl.LoggingUtils.voidExit(_log, ");
 106  11
             builder.addQuoted(methodName);
 107  11
             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  18
         builder.end();
 119   
 
 120  18
         MethodFab methodFab = classFab.addMethod(Modifier.PUBLIC, sig, builder.toString());
 121   
 
 122  18
         builder.clear();
 123   
 
 124  18
         builder.begin();
 125  18
         builder.add("org.apache.hivemind.service.impl.LoggingUtils.exception(_log, ");
 126  18
         builder.addQuoted(methodName);
 127  18
         builder.addln(", $e);");
 128  18
         builder.addln("throw $e;");
 129  18
         builder.end();
 130   
 
 131  18
         String body = builder.toString();
 132   
 
 133  18
         Class[] exceptions = sig.getExceptionTypes();
 134   
 
 135  18
         int count = exceptions.length;
 136   
 
 137  18
         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  18
         methodFab.addCatch(RuntimeException.class, body);
 146   
     }
 147   
 
 148  12
     protected void addServiceMethods(InterceptorStack stack, ClassFab fab, List parameters)
 149   
     {
 150  12
         MethodMatcher matcher = buildMethodMatcher(parameters);
 151   
 
 152  12
         MethodIterator mi = new MethodIterator(stack.getServiceInterface());
 153   
 
 154  12
         while (mi.hasNext())
 155   
         {
 156  19
             MethodSignature sig = mi.next();
 157   
 
 158  19
             if (includeMethod(matcher, sig))
 159  18
                 addServiceMethodImplementation(fab, sig);
 160   
             else
 161  1
                 addPassThruMethodImplementation(fab, sig);
 162   
         }
 163   
 
 164  12
         if (!mi.getToString())
 165  11
             addToStringMethod(stack, fab);
 166   
     }
 167   
 
 168   
     /**
 169   
      * Creates a toString() method that identify the interceptor service id,
 170   
      * the intercepted service id, and the service interface class name).
 171   
      */
 172  11
     protected void addToStringMethod(InterceptorStack stack, ClassFab fab)
 173   
     {
 174  11
         ClassFabUtils.addToStringMethod(
 175   
             fab,
 176   
             "<LoggingInterceptor for "
 177   
                 + stack.getServiceExtensionPointId()
 178   
                 + "("
 179   
                 + stack.getServiceInterface().getName()
 180   
                 + ")>");
 181   
 
 182   
     }
 183   
 
 184  12
     private MethodMatcher buildMethodMatcher(List parameters)
 185   
     {
 186  12
         MethodMatcher result = null;
 187   
 
 188  12
         Iterator i = parameters.iterator();
 189  12
         while (i.hasNext())
 190   
         {
 191  2
             MethodContribution mc = (MethodContribution) i.next();
 192   
 
 193  2
             if (result == null)
 194  1
                 result = new MethodMatcher();
 195   
 
 196  2
             result.put(mc.getMethodPattern(), mc);
 197   
         }
 198   
 
 199  12
         return result;
 200   
     }
 201   
 
 202  12
     private Class constructInterceptorClass(InterceptorStack stack, List parameters)
 203   
     {
 204  12
         Class serviceInterfaceClass = stack.getServiceInterface();
 205   
         
 206  12
         String name = ClassFabUtils.generateClassName(serviceInterfaceClass);
 207   
 
 208  12
         ClassFab classFab = _factory.newClass(name, Object.class);
 209   
 
 210  12
         classFab.addInterface(serviceInterfaceClass);
 211   
 
 212  12
         createInfrastructure(stack, classFab);
 213   
 
 214  12
         addServiceMethods(stack, classFab, parameters);
 215   
 
 216  12
         return classFab.createClass();
 217   
     }
 218   
 
 219  12
     private void createInfrastructure(InterceptorStack stack, ClassFab classFab)
 220   
     {
 221  12
         Class topClass = ClassFabUtils.getInstanceClass(stack.peek(), stack.getServiceInterface());
 222   
 
 223  12
         classFab.addField("_log", Log.class);
 224   
 
 225   
         // This is very important: since we know the instance of the top object (the next
 226   
         // object in the pipeline for this service), we can build the instance variable
 227   
         // and constructor to use the exact class rather than the service interface.
 228   
         // That's more efficient at runtime, lowering the cost of using interceptors.
 229   
         // One of the reasons I prefer Javassist over JDK Proxies.
 230   
 
 231  12
         classFab.addField("_inner", topClass);
 232   
 
 233  12
         classFab.addConstructor(
 234   
             new Class[] { Log.class, topClass },
 235   
             null,
 236   
             "{ _log = $1; _inner = $2; }");
 237   
     }
 238   
 
 239   
     /**
 240   
      * Creates the interceptor.
 241   
      * The class that is created is cached; if an interceptor is requested
 242   
      * for the same extension point, then the previously constructed class
 243   
      * is reused (this can happen with the threaded service model, for example,
 244   
      * when a thread-local service implementation is created for different threads).
 245   
      */
 246  12
     public void createInterceptor(
 247   
         InterceptorStack stack,
 248   
         Module contributingModule,
 249   
         List parameters)
 250   
     {
 251  12
         Class interceptorClass = constructInterceptorClass(stack, parameters);
 252   
 
 253  12
         try
 254   
         {
 255  12
             Object interceptor = instantiateInterceptor(stack, interceptorClass);
 256   
 
 257  12
             stack.push(interceptor);
 258   
         }
 259   
         catch (Exception ex)
 260   
         {
 261  0
             throw new ApplicationRuntimeException(
 262   
                 ServiceMessages.errorInstantiatingInterceptor(
 263   
                     _serviceId,
 264   
                     stack,
 265   
                     interceptorClass,
 266   
                     ex),
 267   
                 ex);
 268   
         }
 269   
     }
 270   
 
 271   
 
 272   
 
 273  19
     private boolean includeMethod(MethodMatcher matcher, MethodSignature sig)
 274   
     {
 275  19
         if (matcher == null)
 276  16
             return true;
 277   
 
 278  3
         MethodContribution mc = (MethodContribution) matcher.get(sig);
 279   
 
 280  3
         return mc == null || mc.getInclude();
 281   
     }
 282   
 
 283  12
     private Object instantiateInterceptor(InterceptorStack stack, Class interceptorClass)
 284   
         throws Exception
 285   
     {
 286  12
         Object stackTop = stack.peek();
 287   
 
 288  12
         Constructor c = interceptorClass.getConstructors()[0];
 289   
 
 290  12
         return c.newInstance(new Object[] { stack.getServiceLog(), stackTop });
 291   
     }
 292   
 
 293  12
     public void setFactory(ClassFactory factory)
 294   
     {
 295  12
         _factory = factory;
 296   
     }
 297   
 
 298  11
     public void setServiceId(String string)
 299   
     {
 300  11
         _serviceId = string;
 301   
     }
 302   
 }
 303