Package org.apache.hivemind.service.impl

Source Code of org.apache.hivemind.service.impl.LoggingInterceptorFactory

//  Copyright 2004 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.apache.hivemind.service.impl;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.hivemind.ApplicationRuntimeException;
import org.apache.hivemind.InterceptorStack;
import org.apache.hivemind.ServiceInterceptorFactory;
import org.apache.hivemind.internal.Module;
import org.apache.hivemind.methodmatch.MethodMatcher;
import org.apache.hivemind.service.BodyBuilder;
import org.apache.hivemind.service.ClassFab;
import org.apache.hivemind.service.ClassFabUtils;
import org.apache.hivemind.service.ClassFactory;
import org.apache.hivemind.service.MethodContribution;
import org.apache.hivemind.service.MethodFab;
import org.apache.hivemind.service.MethodIterator;
import org.apache.hivemind.service.MethodSignature;

/**
* An interceptor factory that adds logging capability to a service. The logging
* is based upon the Jakarta <a
* href="http://jakarta.apache.org/commons/logging.html">commons-logging </a>
* toolkit, which makes it very transportable.
* <p>
* The interceptor will log entry to each method and exit from the method (with
* return value), plus log any exceptions thrown by the method. The logger used
* is the <em>id of the service</em>, which is not necessarily the name of
* the implementing class. Logging occurs at the debug level.
*
* @author Howard Lewis Ship
*/
public class LoggingInterceptorFactory implements ServiceInterceptorFactory
{
    private ClassFactory _factory;

    private String _serviceId;

    /**
     * Creates a method that delegates to the _inner object; this is used for
     * methods that are not logged.
     */
    private void addPassThruMethodImplementation(ClassFab classFab, MethodSignature sig)
    {
        BodyBuilder builder = new BodyBuilder();
        builder.begin();

        builder.add("return ($r) _inner.");
        builder.add(sig.getName());
        builder.addln("($$);");

        builder.end();

        classFab.addMethod(Modifier.PUBLIC, sig, builder.toString());
    }

    protected void addServiceMethodImplementation(ClassFab classFab, MethodSignature sig)
    {
        Class returnType = sig.getReturnType();
        String methodName = sig.getName();

        boolean isVoid = (returnType == void.class);

        BodyBuilder builder = new BodyBuilder();

        builder.begin();
        builder.addln("boolean debug = _log.isDebugEnabled();");

        builder.addln("if (debug)");
        builder.add("  org.apache.hivemind.service.impl.LoggingUtils.entry(_log, ");
        builder.addQuoted(methodName);
        builder.addln(", $args);");

        if (!isVoid)
        {
            builder.add(ClassFabUtils.getJavaClassName(returnType));
            builder.add(" result = ");
        }

        builder.add("_inner.");
        builder.add(methodName);
        builder.addln("($$);");

        if (isVoid)
        {
            builder.addln("if (debug)");
            builder.add("  org.apache.hivemind.service.impl.LoggingUtils.voidExit(_log, ");
            builder.addQuoted(methodName);
            builder.addln(");");
        }
        else
        {
            builder.addln("if (debug)");
            builder.add("  org.apache.hivemind.service.impl.LoggingUtils.exit(_log, ");
            builder.addQuoted(methodName);
            builder.addln(", ($w)result);");
            builder.addln("return result;");
        }

        builder.end();

        MethodFab methodFab = classFab.addMethod(Modifier.PUBLIC, sig, builder.toString());

        builder.clear();

        builder.begin();
        builder.add("org.apache.hivemind.service.impl.LoggingUtils.exception(_log, ");
        builder.addQuoted(methodName);
        builder.addln(", $e);");
        builder.addln("throw $e;");
        builder.end();

        String body = builder.toString();

        Class[] exceptions = sig.getExceptionTypes();

        int count = exceptions.length;

        for (int i = 0; i < count; i++)
        {
            methodFab.addCatch(exceptions[i], body);
        }

        // Catch and log any runtime exceptions, in addition to the
        // checked exceptions.

        methodFab.addCatch(RuntimeException.class, body);
    }

    protected void addServiceMethods(InterceptorStack stack, ClassFab fab, List parameters)
    {
        MethodMatcher matcher = buildMethodMatcher(parameters);

        MethodIterator mi = new MethodIterator(stack.getServiceInterface());

        while (mi.hasNext())
        {
            MethodSignature sig = mi.next();

            if (includeMethod(matcher, sig))
                addServiceMethodImplementation(fab, sig);
            else
                addPassThruMethodImplementation(fab, sig);
        }

        if (!mi.getToString())
            addToStringMethod(stack, fab);
    }

    /**
     * Creates a toString() method that identify the interceptor service id, the
     * intercepted service id, and the service interface class name).
     */
    protected void addToStringMethod(InterceptorStack stack, ClassFab fab)
    {
        ClassFabUtils.addToStringMethod(fab, "<LoggingInterceptor for " + stack.getServiceExtensionPointId() + "("
                + stack.getServiceInterface().getName() + ")>");

    }

    private MethodMatcher buildMethodMatcher(List parameters)
    {
        MethodMatcher result = null;

        Iterator i = parameters.iterator();
        while (i.hasNext())
        {
            MethodContribution mc = (MethodContribution) i.next();

            if (result == null)
                result = new MethodMatcher();

            result.put(mc.getMethodPattern(), mc);
        }

        return result;
    }

    private Class constructInterceptorClass(InterceptorStack stack, List parameters)
    {
        Class serviceInterfaceClass = stack.getServiceInterface();
        Module module = stack.getServiceModule();

        String name = ClassFabUtils.generateClassName("Interceptor");

        ClassFab classFab = _factory.newClass(name, Object.class, module.getClassResolver().getClassLoader());

        classFab.addInterface(serviceInterfaceClass);

        createInfrastructure(stack, classFab);

        addServiceMethods(stack, classFab, parameters);

        return classFab.createClass();
    }

    private void createInfrastructure(InterceptorStack stack, ClassFab classFab)
    {
        Class topClass = ClassFabUtils.getInstanceClass(stack.peek(), stack.getServiceInterface());

        classFab.addField("_log", Log.class);

        // This is very important: since we know the instance of the top object
        // (the next
        // object in the pipeline for this service), we can build the instance
        // variable
        // and constructor to use the exact class rather than the service
        // interface.
        // That's more efficient at runtime, lowering the cost of using
        // interceptors.
        // One of the reasons I prefer Javassist over JDK Proxies.

        classFab.addField("_inner", topClass);

        classFab.addConstructor(new Class[]
        { Log.class, topClass }, null, "{ _log = $1; _inner = $2; }");
    }

    /**
     * Creates the interceptor. The class that is created is cached; if an
     * interceptor is requested for the same extension point, then the
     * previously constructed class is reused (this can happen with the threaded
     * service model, for example, when a thread-local service implementation is
     * created for different threads).
     */
    public void createInterceptor(InterceptorStack stack, Module contributingModule, List parameters)
    {
        Class interceptorClass = constructInterceptorClass(stack, parameters);

        try
        {
            Object interceptor = instantiateInterceptor(stack, interceptorClass);

            stack.push(interceptor);
        }
        catch (Exception ex)
        {
            throw new ApplicationRuntimeException(ServiceMessages.errorInstantiatingInterceptor(_serviceId, stack,
                    interceptorClass, ex), ex);
        }
    }

    private boolean includeMethod(MethodMatcher matcher, MethodSignature sig)
    {
        if (matcher == null)
            return true;

        MethodContribution mc = (MethodContribution) matcher.get(sig);

        return mc == null || mc.getInclude();
    }

    private Object instantiateInterceptor(InterceptorStack stack, Class interceptorClass) throws Exception
    {
        Object stackTop = stack.peek();

        Constructor c = interceptorClass.getConstructors()[0];

        return c.newInstance(new Object[]
        { stack.getServiceLog(), stackTop });
    }

    public void setFactory(ClassFactory factory)
    {
        _factory = factory;
    }

    public void setServiceId(String string)
    {
        _serviceId = string;
    }
}
TOP

Related Classes of org.apache.hivemind.service.impl.LoggingInterceptorFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.