Clover coverage report - Code Coverage for hivemind release 1.0-rc-2
Coverage timestamp: Sat Sep 11 2004 09:09:48 EDT
file stats: LOC: 303   Methods: 12
NCLOC: 191   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.
 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
         MethodMatcher matcher = buildMethodMatcher(parameters);
 151   
 
 152  11
         MethodIterator mi = new MethodIterator(stack.getServiceInterface());
 153   
 
 154  11
         while (mi.hasNext())
 155   
         {
 156  18
             MethodSignature sig = mi.next();
 157   
 
 158  18
             if (includeMethod(matcher, sig))
 159  17
                 addServiceMethodImplementation(fab, sig);
 160   
             else
 161  1
                 addPassThruMethodImplementation(fab, sig);
 162   
         }
 163   
 
 164  11
         if (!mi.getToString())
 165  10
             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  10
     protected void addToStringMethod(InterceptorStack stack, ClassFab fab)
 173   
     {
 174  10
         ClassFabUtils.addToStringMethod(
 175   
             fab,
 176   
             "<LoggingInterceptor for "
 177   
                 + stack.getServiceExtensionPointId()
 178   
                 + "("
 179   
                 + stack.getServiceInterface().getName()
 180   
                 + ")>");
 181   
 
 182   
     }
 183   
 
 184  11
     private MethodMatcher buildMethodMatcher(List parameters)
 185   
     {
 186  11
         MethodMatcher result = null;
 187   
 
 188  11
         Iterator i = parameters.iterator();
 189  11
         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  11
         return result;
 200   
     }
 201   
 
 202  11
     private Class constructInterceptorClass(InterceptorStack stack, List parameters)
 203   
     {
 204  11
         Class serviceInterfaceClass = stack.getServiceInterface();
 205  11
         Module module = stack.getServiceModule();
 206   
 
 207  11
         String name = ClassFabUtils.generateClassName("Interceptor");
 208   
 
 209  11
         ClassFab classFab =
 210   
             _factory.newClass(name, Object.class, module.getClassResolver().getClassLoader());
 211   
 
 212  11
         classFab.addInterface(serviceInterfaceClass);
 213   
 
 214  11
         createInfrastructure(stack, classFab);
 215   
 
 216  11
         addServiceMethods(stack, classFab, parameters);
 217   
 
 218  11
         return classFab.createClass();
 219   
     }
 220   
 
 221  11
     private void createInfrastructure(InterceptorStack stack, ClassFab classFab)
 222   
     {
 223  11
         Class topClass = stack.peek().getClass();
 224   
 
 225  11
         classFab.addField("_log", Log.class);
 226   
 
 227   
         // This is very important: since we know the instance of the top object (the next
 228   
         // object in the pipeline for this service), we can build the instance variable
 229   
         // and constructor to use the exact class rather than the service interface.
 230   
         // That's more efficient at runtime, lowering the cost of using interceptors.
 231   
         // One of the reasons I prefer Javassist over JDK Proxies.
 232   
 
 233  11
         classFab.addField("_inner", topClass);
 234   
 
 235  11
         classFab.addConstructor(
 236   
             new Class[] { Log.class, topClass },
 237   
             null,
 238   
             "{ _log = $1; _inner = $2; }");
 239   
     }
 240   
 
 241   
     /**
 242   
      * Creates the interceptor.
 243   
      * The class that is created is cached; if an interceptor is requested
 244   
      * for the same extension point, then the previously constructed class
 245   
      * is reused (this can happen with the threaded service model, for example,
 246   
      * when a thread-local service implementation is created for different threads).
 247   
      */
 248  11
     public void createInterceptor(
 249   
         InterceptorStack stack,
 250   
         Module contributingModule,
 251   
         List parameters)
 252   
     {
 253  11
         Class interceptorClass = constructInterceptorClass(stack, parameters);
 254   
 
 255  11
         try
 256   
         {
 257  11
             Object interceptor = instantiateInterceptor(stack, interceptorClass);
 258   
 
 259  11
             stack.push(interceptor);
 260   
         }
 261   
         catch (Exception ex)
 262   
         {
 263  0
             throw new ApplicationRuntimeException(
 264   
                 ServiceMessages.errorInstantiatingInterceptor(
 265   
                     _serviceId,
 266   
                     stack,
 267   
                     interceptorClass,
 268   
                     ex),
 269   
                 ex);
 270   
         }
 271   
     }
 272   
 
 273  18
     private boolean includeMethod(MethodMatcher matcher, MethodSignature sig)
 274   
     {
 275  18
         if (matcher == null)
 276  15
             return true;
 277   
 
 278  3
         MethodContribution mc = (MethodContribution) matcher.get(sig);
 279   
 
 280  3
         return mc == null || mc.getInclude();
 281   
     }
 282   
 
 283  11
     private Object instantiateInterceptor(InterceptorStack stack, Class interceptorClass)
 284   
         throws Exception
 285   
     {
 286  11
         Object stackTop = stack.peek();
 287   
 
 288  11
         Constructor c = interceptorClass.getConstructors()[0];
 289   
 
 290  11
         return c.newInstance(new Object[] { stack.getServiceLog(), stackTop });
 291   
     }
 292   
 
 293  11
     public void setFactory(ClassFactory factory)
 294   
     {
 295  11
         _factory = factory;
 296   
     }
 297   
 
 298  11
     public void setServiceId(String string)
 299   
     {
 300  11
         _serviceId = string;
 301   
     }
 302   
 }
 303