Package alt.jiapi.util

Source Code of alt.jiapi.util.ChainBuilder

package alt.jiapi.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.StringTokenizer;

import org.apache.log4j.Category;

import alt.jiapi.instrumentor.ChainInstrumentor;
import alt.jiapi.instrumentor.InstrumentorChain;
import alt.jiapi.JiapiException;
import alt.jiapi.Runtime;

import alt.jiapi.instrumentor.Strategy;
import alt.jiapi.instrumentor.Hook;

/**
* This utility class may be used to create InstrumentorChain from String.
* It uses a special String format for specifying how the chain is to be
* built. <p>
*
* <blockquote>
* <b>NOTE: This code is experimental at the moment, and may not function
* correctly. Furthermore, String specs described below, might change
* without a notice.</b>
* </blockquote>
*
* General format of the chainSpec String is :
* <blockquote>
* <code> instrumentorSpec (| instrumentorSpec)* </code>
* </blockquote>
*
* Each instrumentorSpec represents an Instrumentor in chain that is
* to be created. First instrumentor(Spec) in chainSpec forward its output
* to the next instrumentor(Spec) and so on. '|' character acts as a marker
* between instrumentorSpecs. One may think that marker as a pipe marker
* in some operating system shells. <p>
*
* Format of the instrumentorSpec is :
* <blockquote>
* <code> instrumentorSymbol(#childSymbol(#childHint)?)?</code>
* </blockquote>
*
* instrumentorSymbol is a symbolic name of the Instrumentor to be used.
* It may be a fully qualified class name of the Instrumentor, or it may
* be its symbolic equivalence. SymbolMap is used to map symbolic
* names to class names.<p>
* childSymbol has a similar way of instantiating child; It could be a fully
* qualified class name or its symbolic name from SymbolMap.
* If child cannot be instantiated, it is considered to be an instance of
* String. In that case, child hints make no sense.<p>
* Instrumentor gets its child associated with itself through constructor.
* Reflection API, <code>Constructor</code> in particular, is
* used to instantiate Instrumentor. <p>
*
* childHint is passed to child the same way, as child gets passed to
* Instrumentor. Trough Constructor. This version supports only integer
* hints. Child is looked for a field named 'childHint'. If one is found,
* it is assumed, that there is a Constructor with same type as its
* argument.<p>
*
* Future versions of this class might slightly modify format described
* above. More childHint types might be added if there is need for one.
* and different ways of passing childHints and childs to their parents.
* Like java beans style get/set methods.<p>
*
* Samples:
* <blockquote>
* <code>"GrepInstrumentor#MethodCallStrategy | HeadInstrumentor | MethodCallInstrumentor#DummyHook"</code><br>
* If the default SymbolMap is used, chainSpec above would generate a chain
* with instrumentors added to it in the following order:
*
* <ol>
* <li>Instatiate <code>alt.jiapi.instrumentor.GrepInstrumentor</code> with
*     <code>alt.jiapi.instrumentor.MethodCallStrategy</code> as its argument.
*     This instrumentor is put first in the chain.
* <li><code>alt.jiapi.instrumentor.HeadInstrumentor</code> is
*     instatiated, and added to chain.
* <li><code>alt.jiapi.instrumentor.MethodCallInstrumentor</code> is
*     instatiated with and instance of <code>DummyHook</code> as its
*     constructor argument. It is then added to chain.
* </ol>
*
* Resulting chain would instrument its input so, that a call is being made
* to DummyHook before a method call is being made.
* </blockquote>
*
* @see SymbolMap
* @author Mika Riekkinen, Joni Suominen
* @version $Revision: 1.5 $ $Date: 2004/03/15 14:47:53 $
*/
public class ChainBuilder {
    private static Category log = Runtime.getLogCategory(ChainBuilder.class);

    private static final Class instrumentorClass = ChainInstrumentor.class;
    private static final Class strategyClass = Strategy.class;
    private static final Class hookClass = Hook.class;

    private SymbolMap map;

    /**
     * Used in debugging.
     */
    public static void main(String [] args) throws Exception {
        SymbolMap map = new SymbolMap("alt.jiapi.instrumentor");

        ChainBuilder cb = new ChainBuilder(map);
        InstrumentorChain chain = cb.createChain(args[0]);
        System.out.println(chain);
    }

    public ChainBuilder() {
        this.map = new SymbolMap("alt.jiapi.instrumentor");
    }

    public ChainBuilder(SymbolMap map) {
        this.map = map;
    }

    /**
     * Creates a chain from chainSpec and SymbolMap given in constructor.
     */
    public InstrumentorChain createChain(String chainSpec) throws JiapiException, ClassNotFoundException, InstantiationException {
        return createChain(chainSpec, map);
    }

    /**
     * Creates a chain from chainSpec and given SymbolMap
     */
    public InstrumentorChain createChain(String chainSpec, SymbolMap symbols) throws JiapiException, ClassNotFoundException, InstantiationException {
        InstrumentorChain chain = new InstrumentorChain();

        StringTokenizer st = new StringTokenizer(chainSpec, "|");
        while (st.hasMoreTokens()) {
            String instrumentorSpec = st.nextToken().trim();

            ChainInstrumentor i = createInstrumentor(instrumentorSpec, symbols);
            if (i == null) {
                throw new JiapiException("ChainBuilder.createInstrumentor(...) returned null");
            }

            chain.add(i);
        }

        return chain;
    }

    /**
     * Create an Instrumentor from instrumentorSpec.
     *
     * @param instrumentorSpec String spec as defined above
     * @param symbols SymbolMap used to resolve symbolic names
     *        to Class names.
     * @return An instance of Instrumentor if instrumentorSpec is valid
     * @exception JiapiException is thrown if instrumentorSpec does not meet
     *        the basic requirements of its format.
     * @exception ClassNotFoundException is thrown, if instrumentor class
     *        was not found.
     * @exception InstantiationException is thrown, if instantiation failes
     *        in any way. This exception also wraps all the other reflection
     *        API exception that might be thrown during instantiation.
     */
    public ChainInstrumentor createInstrumentor(String instrumentorSpec,
                                           SymbolMap symbols) throws JiapiException, ClassNotFoundException, InstantiationException {
        log.debug("Creating Instrumentor from spec: " + instrumentorSpec);

        ChainInstrumentor instrumentor = null;

        String instrumentorClassName = null;
        String childClassName = null;
        String childHints = null;

        StringTokenizer st = new StringTokenizer(instrumentorSpec, "#");
        if (!st.hasMoreTokens()) {
            throw new JiapiException("Illegal insrumentorSpec " +
                                     instrumentorSpec);
        }

        // (Symbolic)Class name of the Instrumentor
        String instrumentorSymbol = st.nextToken().trim();
        instrumentorClassName = symbols.getSymbol(instrumentorSymbol);
        if (instrumentorClassName == null) {
            instrumentorClassName = instrumentorSymbol;
        }

        if (st.hasMoreTokens()) {
            String childSymbol = st.nextToken().trim();
            childClassName = symbols.getSymbol(childSymbol);
            if (childClassName == null) {
                childClassName = childSymbol;
            }
        }

        if (st.hasMoreTokens()) {
            childHints = st.nextToken().trim();
        }


        // Load relevant classes
        Class instrumentorClass = null;
        Class childClass = null;
        Object child = null;

        instrumentorClass = Class.forName(instrumentorClassName);
        if (childClassName != null) {
            try {
                childClass = Class.forName(childClassName);
                child = createChild(childClass, childHints);
            }
            catch (ClassNotFoundException e) {
                // ForwardingInstrumentor wants at least a String
                // in constructor.
                // Child could be a plain number !!!
                child = childClassName; // Try String as a child
            }
        }

        instrumentor = createInstrumentor(instrumentorClass, child);

        log.info("Created instrumentor: " + instrumentor);
        return instrumentor;
    }


    private Object createChild(Class child, String childHint) throws InstantiationException {
        System.out.println("Creating child : " + child);
        Object childObject = null;
        log.debug("Creating child: " + child.getName() + ", hints " +
                  childHint);

        if (childHint == null) {
            // Use empty constructor
            try {
                childObject = child.newInstance();
            }
            catch (IllegalAccessException iae) {
                throw new InstantiationException("Illegal access: " +
                                                 iae.getMessage());
            }
        }
        else {
            // Try to figure out how hint gets passed to child
            // Try field:
            Field hintField = null;
            try {
                hintField = child.getField(childHint);
            }
            catch (Exception e) {
                log.debug("Exception occured: " + e.getMessage());
            }

            if (hintField != null) {
                log.debug("hint field: " + hintField);
                // We have a hint field
                // Try a matching constructor first
                Class[] parameterTypes = new Class[1];
                parameterTypes[0] = hintField.getType();
                try {
                    Constructor c = child.getConstructor(parameterTypes);
                    log.debug("Child constructor: " + c);

                    Object[] params = new Object[1];
                    // Assume integers here
                    params[0] = new Integer(hintField.getInt(null));
                    childObject = c.newInstance(params);
                }
                catch (NoSuchMethodException nsme) {
                    throw new InstantiationException("No such method: " +
                                                     nsme.getMessage());
                }
                catch (IllegalAccessException iae) {
                    throw new InstantiationException("Illegal access: " +
                                                     iae.getMessage());
                }
                catch (InvocationTargetException ite) {
                    throw new InstantiationException("Exception in constructor: "+
                                                     ite.getMessage());
                }

            }
        }

        return childObject;
    }

    private ChainInstrumentor createInstrumentor(Class instrumentorClass, Object child) throws InstantiationException {
        log.debug("Creating Instrumentor: " + instrumentorClass.getName() +
                  ", child: " + child);

        ChainInstrumentor instrumentor = null;

        if (child == null) {
            // Use empty constructor
            try {
                instrumentor = (ChainInstrumentor)instrumentorClass.newInstance();
            }
            catch (IllegalAccessException iae) {
                throw new InstantiationException("Illegal access: " +
                                                 iae.getMessage());
            }
        }
        else {
//              Try primitive types in constructor.
//              if(child instanceof java.lang.Number) {
//                  ...
//              } else {

            // Use constructor with child as argument
            Class[] parameterTypes = new Class[1];
            if (instrumentorClass.isAssignableFrom(child.getClass())) {
                parameterTypes[0] = instrumentorClass;
            }
            else if (strategyClass.isAssignableFrom(child.getClass())) {
                parameterTypes[0] = strategyClass;
            }
            else if (hookClass.isAssignableFrom(child.getClass())){
                parameterTypes[0] = hookClass;
            }
            else {
                parameterTypes[0] = child.getClass();
            }

//              System.out.println("child " + child);
//              System.out.println(parameterTypes[0]);
            try {
                Constructor c =
                    instrumentorClass.getConstructor(parameterTypes);

                log.debug("Using constructor: " + c);

                Object [] params = new Object[] {child};
                instrumentor = (ChainInstrumentor)c.newInstance(params);
            }
            catch (NoSuchMethodException nsme) {
                // Instead of rethrowing, we could try JavaBean stuff...
                throw new InstantiationException("No such method: " +
                                                 nsme.getMessage());
            }
            catch (IllegalAccessException iae) {
                throw new InstantiationException("Illegal access: " +
                                                 iae.getMessage());
            }
            catch (InvocationTargetException ite) {
                throw new InstantiationException("Exception in constructor: "+
                                                 ite.getMessage());
            }
        }

        return instrumentor;
    }
}
TOP

Related Classes of alt.jiapi.util.ChainBuilder

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.