gt: 				List<? extends CtBehavior> behaviors;
context: package info.novatec.inspectit.agent.analyzer.impl;
  
  import info.novatec.inspectit.agent.analyzer.IByteCodeAnalyzer;
  import info.novatec.inspectit.agent.analyzer.IClassPoolAnalyzer;
  import info.novatec.inspectit.agent.analyzer.IMatcher;
  import info.novatec.inspectit.agent.config.IConfigurationStorage;
  import info.novatec.inspectit.agent.config.StorageException;
  import info.novatec.inspectit.agent.config.impl.MethodSensorTypeConfig;
  import info.novatec.inspectit.agent.config.impl.PropertyAccessor.PropertyPathStart;
  import info.novatec.inspectit.agent.config.impl.RegisteredSensorConfig;
  import info.novatec.inspectit.agent.config.impl.UnregisteredSensorConfig;
  import info.novatec.inspectit.agent.hooking.IHookInstrumenter;
  import info.novatec.inspectit.agent.hooking.impl.HookException;
  import info.novatec.inspectit.communication.data.ParameterContentType;
  import info.novatec.inspectit.spring.logger.Log;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import javassist.ByteArrayClassPath;
  import javassist.CannotCompileException;
  import javassist.ClassPath;
  import javassist.ClassPool;
  import javassist.CtBehavior;
  import javassist.CtClass;
  import javassist.CtConstructor;
  import javassist.CtMethod;
  import javassist.LoaderClassPath;
  import javassist.NotFoundException;
  
  import org.apache.commons.collections.CollectionUtils;
  import org.slf4j.Logger;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.beans.factory.annotation.Value;
  import org.springframework.stereotype.Component;
  
  /**
   * The default implementation of the {@link IByteCodeAnalyzer} interface. First it tries to analyze
   * the given byte code and collects all the methods which need to be instrumented in a Map. This is
   * done in the {@link #analyze(byte[], String, ClassLoader)} method. Afterwards, the Map is passed
   * to the {@link #instrument(Map)} method which will do the instrumentation.
   * 
   * @author Patrice Bouillet
   * @author Eduard Tudenhoefner
   * 
   */
  @Component
  public class ByteCodeAnalyzer implements IByteCodeAnalyzer {
  
  	/**
  	 * Log for the class.
  	 */
  	@Log
  	Logger log;
  
  	/**
  	 * The implementation of the hook instrumenter.
  	 */
  	private final IHookInstrumenter hookInstrumenter;
  
  	/**
  	 * The implementation of the configuration storage where all definitions of the user are stored.
  	 */
  	private final IConfigurationStorage configurationStorage;
  
  	/**
  	 * The class pool analyzer is used here to add the passed byte code to the class pool which is
  	 * responsible for the class loader.
  	 */
  	private final IClassPoolAnalyzer classPoolAnalyzer;
  
  	/**
  	 * If class loader delegation should be active.
  	 */
  	@Value("${instrumentation.classLoaderDelegation}")
  	boolean classLoaderDelegation;
  
  	/**
  	 * The default constructor which accepts two parameters which are needed.
  	 * 
  	 * @param configurationStorage
  	 *            The configuration storage reference.
  	 * @param hookInstrumenter
  	 *            The hook instrumenter reference.
  	 * @param classPoolAnalyzer
  	 *            The class pool analyzer reference.
  	 */
  	@Autowired
  	public ByteCodeAnalyzer(IConfigurationStorage configurationStorage, IHookInstrumenter hookInstrumenter, IClassPoolAnalyzer classPoolAnalyzer) {
  		if (null == configurationStorage) {
  			throw new IllegalArgumentException("Configuration storage cannot be null!");
  		}
  		if (null == hookInstrumenter) {
  			throw new IllegalArgumentException("Hook instrumenter cannot be null!");
  		}
  		if (null == classPoolAnalyzer) {
  			throw new IllegalArgumentException("Class pool analyzer cannot be null!");
  		}
  		this.configurationStorage = configurationStorage;
  		this.hookInstrumenter = hookInstrumenter;
  		this.classPoolAnalyzer = classPoolAnalyzer;
  	}
  
  	/**
  	 * {@inheritDoc}
  	 */
  	public byte[] analyzeAndInstrument(byte[] byteCode, String className, ClassLoader classLoader) {
  		// The reason to create a byte array class path here is to handle
  		// classes created at runtime (reflection / byte code engineering
  		// libraries etc.) and to get the real content of that class (think of
  		// classes modified by other java agents before.)
  		ClassPool classPool = classPoolAnalyzer.getClassPool(classLoader);
  		ClassPath classPath = null;
  		ClassPath loaderClassPath = null;
  		try {
  			if (null == byteCode) {
  				// this occurs if we are in the initialization phase and are instrumenting classes
  				// where we don't have the bytecode directly. Thus we try to load it.
  				byteCode = classPool.get(className).toBytecode();
  			}
  			if (null != Thread.currentThread().getContextClassLoader() && classLoader != Thread.currentThread().getContextClassLoader()) {
  				// only use the context class loader if it is even set and not the same as the
  				// classloader being passed to the instrumentation
  				loaderClassPath = new LoaderClassPath(Thread.currentThread().getContextClassLoader());
  				classPool.insertClassPath(loaderClassPath);
  			}
  			// the byte array classpath needs to be the last one to be the first for access
  			classPath = new ByteArrayClassPath(className, byteCode);
  			classPool.insertClassPath(classPath);
  
  			byte[] instrumentedByteCode = null;
  			Map<CtBehavior, List<UnregisteredSensorConfig>> behaviorToConfigMap = analyze(className, classLoader);
  
  			// class loader delegation behaviors
  			List<? extends CtBehavior> classLoaderDelegationBehaviors = analyzeForClassLoaderDelegation(className, classLoader);
  
  			CtBehavior ctBehavior = null;
  			if (!behaviorToConfigMap.isEmpty()) {
  				ctBehavior = instrumentSensors(behaviorToConfigMap);
  			}
  
  			if (!classLoaderDelegationBehaviors.isEmpty()) {
  				ctBehavior = instrumentClassLoader(classLoaderDelegationBehaviors);
  			}
  
  			if (null != ctBehavior) {
  				instrumentedByteCode = ctBehavior.getDeclaringClass().toBytecode();
  			}
  
  			return instrumentedByteCode;
  		} catch (NotFoundException notFoundException) {
  			log.error("Error occurred instrumenting the byte code of class " + className, notFoundException);
  			return null;
  		} catch (IOException iOException) {
  			log.error("Error occurred instrumenting the byte code of class " + className, iOException);
  			return null;
  		} catch (CannotCompileException cannotCompileException) {
  			log.error("Error occurred instrumenting the byte code of class " + className, cannotCompileException);
  			return null;
  		} catch (HookException hookException) {
  			log.error("Error occurred instrumenting the byte code of class " + className, hookException);
  			return null;
  		} catch (StorageException storageException) {
  			log.error("Error occurred instrumenting the byte code of class " + className, storageException);
  			return null;
  		} finally {
  			// Remove the byte array class path from the class pool. The class
  			// loader now should know this class, thus it can be accessed
  			// through the standard way.
  			if (null != classPath) {
  				classPool.removeClassPath(classPath);
  			}
  			if (null != loaderClassPath) {
  				classPool.removeClassPath(loaderClassPath);
  			}
  		}
  	}
  
  	/**
  	 * Returns the list of {@link CtBehavior} that relate to the class loader delegation.
  	 * 
  	 * @param className
  	 *            The name of the class.
  	 * @param classLoader
  	 *            The class loader of the passed class.
  	 * @return Returns the list of {@link CtBehavior} that relate to the class loader delegation.
  	 * @throws NotFoundException
  	 *             Something could not be found.
  	 */
  	private List<? extends CtBehavior> analyzeForClassLoaderDelegation(String className, ClassLoader classLoader) throws NotFoundException {
  		if (!classLoaderDelegation) {
  			return Collections.emptyList();
  		}
  
  		if (log.isTraceEnabled()) {
  			log.trace("analyzeForClassLoaderDelegation: " + className);
  		}
  
  		for (IMatcher matcher : configurationStorage.getClassLoaderDelegationMatchers()) {
  			if (null != matcher && matcher.compareClassName(classLoader, className)) {
  				List<? extends CtBehavior> behaviors = matcher.getMatchingMethods(classLoader, className);
  				if (CollectionUtils.isNotEmpty(behaviors)) {
  					matcher.checkParameters(behaviors);
  					return behaviors;
  				}
  			}
  		}
  		return Collections.emptyList();
  	}
  
  	/**
  	 * The analyze method will analyze the passed byte code, class name and class loader and returns
  	 * a {@link Map} with all matching methods to be instrumented.
  	 * 
  	 * @param className
  	 *            The name of the class.
  	 * @param classLoader
  	 *            The class loader of the passed class.
  	 * @return Returns a {@link Map} with all found methods ({@link CtBehavior}) as the Key and a
  	 *         {@link List} of {@link UnregisteredSensorConfig} as the value.
  	 * @throws NotFoundException
  	 *             Something could not be found.
  	 * @throws StorageException
  	 *             Sensor could not be added.
  	 */
  	private Map<CtBehavior, List<UnregisteredSensorConfig>> analyze(String className, ClassLoader classLoader) throws NotFoundException, StorageException {
  		Map<CtBehavior, List<UnregisteredSensorConfig>> behaviorToConfigMap = new HashMap<CtBehavior, List<UnregisteredSensorConfig>>();
  
  		// Iterating over all stored unregistered sensor configurations
  		for (UnregisteredSensorConfig unregisteredSensorConfig : configurationStorage.getUnregisteredSensorConfigs()) {
  			// try to match the class name first
  			IMatcher matcher = unregisteredSensorConfig.getMatcher();
  			if (matcher.compareClassName(classLoader, className)) {
  