Package de.petris.dynamicaspects.classhandler

Source Code of de.petris.dynamicaspects.classhandler.ExecutionClassHandler

/*
* Copyright (c) 2004, Marco Petris
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     - Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*     - Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     - Neither the name of Marco Petris nor the names of its
*       contributors may be used to endorse or promote products derived from
*       this software without specific prior written permission.
*      
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

package de.petris.dynamicaspects.classhandler;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.bcel.classfile.Method;

import de.petris.dynamicaspects.Advice;
import de.petris.dynamicaspects.AspectException;
import de.petris.dynamicaspects.AdviceFactory;
import de.petris.dynamicaspects.BeforeAfterAdvice;
import de.petris.dynamicaspects.util.Logger;
import de.petris.dynamicaspects.util.Reflection;
import de.petris.dynamicaspects.wrapper.ExecutionWrapper;
import de.petris.dynamicaspects.wrapper.ExecutionWrapperMethodPatcher;


/**
* This classhandler installs {@link de.petris.dynamicaspects.BeforeAfterAdvice BeforeAfterAdvices}
* which wrap a method or constructor execution.
* A {@link de.petris.dynamicaspects.wrapper.ExecutionWrapper ExecutionWrapper}
* is wrapped around every matching method execution.
* Each time a method executes the wrapper handles the installed advices.
* A {@link de.petris.dynamicaspects.wrapper.ExecutionMapper ExecutionMapper} maps an
* {@link de.petris.dynamicaspects.wrapper.ExecutionWrapper ExecutionWrapper}
* to its corresponding method during runtime.
*
* @author Marco Petris
* @see de.petris.dynamicaspects.WeaveType
* @see de.petris.dynamicaspects.BeforeAfterAdvice
* @see de.petris.dynamicaspects.wrapper.ExecutionWrapper
* @see de.petris.dynamicaspects.wrapper.ExecutionMapper
*/
public class ExecutionClassHandler extends DefaultClassHandler {
 
  // a mapping: methodsignature -> executionwrapper
  private Map<String, ExecutionWrapper> executionWrappers;
 
  /**
   * Constructs an instance of this class.
   */
  public ExecutionClassHandler() {
    executionWrappers = new HashMap<String, ExecutionWrapper>();
  }

  /* (non-Javadoc)
   * @see de.petris.dynamicaspects.classhandler.ClassHandler#install(de.petris.dynamicaspects.AdviceFactory, java.util.regex.Pattern)
   */
  public void install(
    AdviceFactory factory, Pattern joinpointPattern ) {
    handleInstall( null, factory, joinpointPattern );
  }
 
  /* (non-Javadoc)
   * @see de.petris.dynamicaspects.classhandler.ClassHandler#install(java.lang.Class, java.util.regex.Pattern)
   */
  public void install(
    Class<? extends Advice> adviceClass, Pattern joinpointPattern ) {
   
    try {
      install(
        adviceClass.newInstance(),
        joinpointPattern );
    }
    catch( Exception e ) {
      throw new AspectException( e );
    }
  }

 
  /* (non-Javadoc)
   * @see de.petris.dynamicaspects.classhandler.ClassHandler#install(de.petris.dynamicaspects.Advice, java.util.regex.Pattern)
   */
  public void install( Advice advice, Pattern joinpointPattern ) {
    handleInstall( advice, null, joinpointPattern );
  }
 
  /**
     * Handles the installation of the given advice or factory according to
     * the given joinpointPattern. Either advice or factory must not be null.
     *
   * @param advice the advice to be installed, can be null
   * @param factory the factory to be used for advice creation, can be null
   * @param joinpointPattern the pointcut
   */
  private void handleInstall( Advice advice, AdviceFactory factory, Pattern joinpointPattern ) {
     Method[] methods = targetClass.getMethods();

        Logger.info( "checking methods for pattern: %s", joinpointPattern.pattern() );
       
     for( int methodIdx=methods.length-1; methodIdx>=0; methodIdx-- ) {
      
       String completeSig =
         methods[methodIdx].getName() + methods[methodIdx].getSignature();
      
       String methodName =
         Reflection.getMethodDeclaration(
             targetClass.getClassName(),
             methods[methodIdx].getName(),
             methods[methodIdx].getSignature() );
           
       if( joinpointPattern.matcher( methodName ).matches() ) {
        
         if( advice == null ) {
           advice = factory.getAdvice();
         }

                if( !( advice instanceof BeforeAfterAdvice ) ) {
                    throw new AspectException(
                            "all aspects installed via the "
                            + ExecutionClassHandler.class.getName()
                            + " are expected to implement "
                            + BeforeAfterAdvice.class.getName()
                            + ", " + advice.getClass().getName()
                            + " does not respect this restriction!" )
                }
        
         Logger.info( "signature %s matched ",  methodName )

         // get the wrapper for this method
         ExecutionWrapper eWrapper =
           loadWrapper( methodIdx, completeSig );

        eWrapper.install( (BeforeAfterAdvice)advice );
        
       }
       else {
         Logger.info( "no match for signature %s ",  methodName )
       }
     }
  }
 
  /* (non-Javadoc)
   * @see de.petris.dynamicaspects.classhandler.ClassHandler#deinstall(de.petris.dynamicaspects.Advice)
   */
  public void deinstall( Advice advice ) {
   
        Logger.debug( "deinstalling %s ",  advice );
       
    Method[] methods = targetClass.getMethods();
   
    for( int methodIdx=methods.length-1; methodIdx>=0; methodIdx-- ) {
     
       String completeSig =
         methods[methodIdx].getName()
        + methods[methodIdx].getSignature();
       String methodName =
         Reflection.getMethodDeclaration(
             targetClass.getClassName(),
             methods[methodIdx].getName(),
             methods[methodIdx].getSignature() );
      
       // do we have a wrapper for this method?
       if( executionWrappers.containsKey( completeSig ) ) {

         Logger.info( "signature %s matched ",  methodName );
        

         // get the wrapper
         ExecutionWrapper curWrapper =
           executionWrappers.get( completeSig );
        
         // deinstall aspect
         curWrapper.deinstall( advice );
        
         // if this has been the last aspect, remove the wrapper
         if( !curWrapper.hasAdvices() )  { // todo: configurable option
           removeWrapper( methodIdx, completeSig );
           executionWrappers.remove( completeSig );
         }
       }
       else {
         Logger.info( "no match for signature %s ",  methodName )
       }
    }
  }
 
  /* (non-Javadoc)
   * @see de.petris.dynamicaspects.classhandler.ClassHandler#deinstall(java.lang.Class)
   */
  public void deinstall( Class<? extends Advice> adviceClass ) {
        Logger.debug( "deinstalling %s ",  adviceClass );
       
    Method[] methods = targetClass.getMethods();
   
    for( int methodIdx=methods.length-1; methodIdx>=0; methodIdx-- ) {
     
       String completeSig =
         methods[methodIdx].getName()
        + methods[methodIdx].getSignature();
      
       String methodName =
         Reflection.getMethodDeclaration(
           targetClass.getClassName(),
           methods[methodIdx].getName(),
           methods[methodIdx].getSignature() );
      
       if( executionWrappers.containsKey( completeSig ) ) {

         Logger.info( "signature %s matched ",  methodName )
      
         ExecutionWrapper curWrapper =
           executionWrappers.get( completeSig );
        
         curWrapper.deinstall( adviceClass );
        
         if( !curWrapper.hasAdvices() )  { // todo: configurable option
           removeWrapper( methodIdx, completeSig );
           executionWrappers.remove( completeSig );
         }
       }
       else {
         Logger.info( "no match for signature %s ",  methodName )
       }
    }
  }
 
  /**
   * Returns the {@link de.petris.dynamicaspects.wrapper.ExecutionWrapper ExecutionWrapper}
   * for the given method signature. This method is called by the
   * {@link de.petris.dynamicaspects.wrapper.ExecutionMapper ExecutionMapper}
   * during runtime to obtain
   * the appropriate {@link de.petris.dynamicaspects.wrapper.ExecutionWrapper ExecutionWrapper}.
   *
   * @param fullMethodName the signature of the method we want the wrapper for
   * @return the wrapper which corresponds to the method with the given name
   */
  public ExecutionWrapper getExecutionWrapper( String fullMethodName ) {
    return executionWrappers.get( fullMethodName );
  }

  /**
   * Removes the wrapper from a method.
   *
   * @param methodIdx the index of this method in the class' method-array
   * @param completeSig the name+signature of this method
   */
  private void removeWrapper( int methodIdx, String completeSig ) {

    Logger.info( "deinstalling method wrapper for %s", completeSig );
   
    ExecutionWrapperMethodPatcher mp =
      new ExecutionWrapperMethodPatcher(
        targetClass.getMethods()[methodIdx],
        targetClass.getClassName(),
        constPoolGen );
     
    targetClass.getMethods()[methodIdx] = mp.deinstall();
     
     targetClass.setConstantPool(
       constPoolGen.getFinalConstantPool() );
  }
 
 
  /**
   * Loads the wrapper for a method. If the wrapper does not exist yet, the wrapper
   * is installed into the method.
   *
   * @param methodIdx the index of this method in the class' method-array
   * @param completeSig the name+signature of this method
   */
  private ExecutionWrapper loadWrapper(
      int methodIdx, String completeSig ) {
   
    if( !executionWrappers.containsKey( completeSig ) ) {
         
      // no wrapper yet, so we install a wrapper first
         
      ExecutionWrapper eWrapper =
        new ExecutionWrapper(
          targetClass.getClassName(),
          targetClass.getMethods()[methodIdx].getName(),
          targetClass.getMethods()[methodIdx].getSignature(),
          targetClass.getMethods()[methodIdx].isStatic() );
         
      executionWrappers.put( completeSig, eWrapper );
     
      Logger.info( "installing method wrapper for %s", completeSig );
      ExecutionWrapperMethodPatcher mp =
        new ExecutionWrapperMethodPatcher(
          targetClass.getMethods()[methodIdx], targetClass.getClassName(),
          constPoolGen );
      
      // get the wrapped method
      targetClass.getMethods()[methodIdx] = mp.install();
        
      // and update the class because of this change
       targetClass.setConstantPool(
         constPoolGen.getFinalConstantPool() );

       return eWrapper;
    }
    else {
      // get the already installed wrapper
      return executionWrappers.get( completeSig );
    }
  }
}
TOP

Related Classes of de.petris.dynamicaspects.classhandler.ExecutionClassHandler

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.