2009/04/15 - Apache HiveMind has been retired.

For more information, please explore the Attic.

Clover coverage report - Code Coverage for hivemind release 1.2.1
Coverage timestamp: Fri Feb 10 2006 16:33:43 PST
file stats: LOC: 302   Methods: 12
NCLOC: 189   Classes: 1
 
 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 _delegate 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) _delegate.");
 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("_delegate.");
 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("_delegate", topClass);
 232   
 233  12 classFab.addConstructor(
 234    new Class[] { Log.class, topClass },
 235    null,
 236    "{ _log = $1; _delegate = $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    }