Package de.petris.dynamicaspects.classhandler

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

/*
* 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.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.MethodGen;

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.InstructionSearcher;
import de.petris.dynamicaspects.util.Logger;
import de.petris.dynamicaspects.wrapper.CallMatch;
import de.petris.dynamicaspects.wrapper.CallWrapper;
import de.petris.dynamicaspects.wrapper.CallWrapperMethodPatcher;

/**
* This classhandler installs {@link de.petris.dynamicaspects.BeforeAfterAdvice BeforeAfterAdvices}
* which wrap a method or constructor call.
* A {@link de.petris.dynamicaspects.wrapper.CallWrapper CallWrapper}
* is wrapped around every matching method/constructor call.
* The mapping of a CallClassHandler to a class is done by the
* {@link de.petris.dynamicaspects.wrapper.CallMapper CallMapper}
* during runtime. A {@link de.petris.dynamicaspects.WeaveType WeaveType.CALL}
* can be queried for a CallClassHandler which handles a certain class.
* The CallClassHandler has a mapping from method to
* joinPointPattern and a mapping from joinpointPattern to a
* {@link de.petris.dynamicaspects.wrapper.CallWrapper CallWrapper}. During runtime the appropriate
* {@link de.petris.dynamicaspects.wrapper.CallWrapper CallWrapper} can be determined.
* The {@link de.petris.dynamicaspects.wrapper.CallWrapper CallWrapper} maintains
* a list of {@link de.petris.dynamicaspects.Advice Advices} which belong to the positions
* that match the joinpointPattern in the method.
*
* @author Marco Petris
* @see de.petris.dynamicaspects.wrapper.CallWrapper
* @see de.petris.dynamicaspects.wrapper.CallMapper
* @see de.petris.dynamicaspects.WeaveType
* @see de.petris.dynamicaspects.BeforeAfterAdvice
*/
public class CallClassHandler extends DefaultClassHandler {

  // a mapping: methodIndex -> joinPointPattern -> callwrapper
  private Map<Integer, Map<String, CallWrapper>> callWrappers;
 
  /**
   * Creates an instance of this class.
   */
  public CallClassHandler() {
    callWrappers = new HashMap<Integer, Map<String, CallWrapper>>();
  }

 
  /* (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 exc ) {
      throw new AspectException( exc );
    }
  }
 
 
  /* (non-Javadoc)
   * @see de.petris.dynamicaspects.classhandler.ClassHandler#install(de.petris.dynamicaspects.Advice, java.util.regex.Pattern)
   */
  public void install( Advice advice, Pattern joinpointPattern ) {

        Logger.info( "checking methods for pattern: %s", joinpointPattern.pattern() );
       
     Method[] methods = targetClass.getMethods();
    
     for( int methodIdx=methods.length-1; methodIdx>=0; methodIdx-- ) {
      
       handleInstall(
         new Integer( methodIdx ), joinpointPattern,
        advice, null );
     }
  }
 
  /* (non-Javadoc)
   * @see de.petris.dynamicaspects.classhandler.ClassHandler#install(de.petris.dynamicaspects.AdviceFactory, java.util.regex.Pattern)
   */
  public void install( AdviceFactory factory, Pattern joinpointPattern ) {
   
        Logger.info( "checking methods for pattern: %s", joinpointPattern.pattern() );
       
     Method[] methods = targetClass.getMethods();
    
     for( int methodIdx=methods.length-1; methodIdx>=0; methodIdx-- ) {
      
       handleInstall(
         new Integer( methodIdx ), joinpointPattern,
        null, factory );
     }

  }
 
  /* (non-Javadoc)
   * @see de.petris.dynamicaspects.classhandler.ClassHandler#deinstall(de.petris.dynamicaspects.Advice)
   */
  public void deinstall( Advice advice ) {

    // get a iterator over all method->joinpointPattern mappings
    // of all woven methods of the class
     Iterator<Map.Entry<Integer, Map<String, CallWrapper>>> joinPointIter
       = callWrappers.entrySet().iterator();
    
     // loop over the method->joinpointPattern mappings
     while( joinPointIter.hasNext() ) {
      
       // get the current mapping: method->joinpointPattern
       Map.Entry<Integer, Map<String, CallWrapper>> curJoinPointMapping =
         joinPointIter.next();
      
       // get a iterator over the joinpointPattern->wrapper mappings
       Iterator<Map.Entry<String, CallWrapper>> wrapperIter
         = curJoinPointMapping.getValue().entrySet().iterator();
      
       // loop over the joinpointPattern->wrapper mappings
       while( wrapperIter.hasNext() ) {
        
         // get the current joinpoint->wrapper mapping
         Map.Entry<String, CallWrapper> curWrapperEntry =
           wrapperIter.next();
        
         // does the wrapper contain our advice?
         if( curWrapperEntry.getValue().contains( advice ) ) {
          
           // yes, so deinstall the advice
           curWrapperEntry.getValue().deinstall( advice );
          
           // shall we deinstall the wrapper because
           // it contains no more advices?
           if( !curWrapperEntry.getValue().hasAdvices() ) { //todo: configurable option
            
             // remove the joinpointPattern->wrapper from the list
             wrapperIter.remove();
             // remove the wrapper from the method
             removeWrapper(
               curJoinPointMapping.getKey(),
               curWrapperEntry.getKey() );
           }
         }
       }
     }
  }
 
  /* (non-Javadoc)
   * @see de.petris.dynamicaspects.classhandler.ClassHandler#deinstall(java.lang.Class)
   */
  public void deinstall( Class<? extends Advice> adviceClass ) {
   
    // get a iterator over all method->joinpointPattern mappings
    // of all woven methods of the class
     Iterator<Map.Entry<Integer, Map<String, CallWrapper>>> joinPointIter
      = callWrappers.entrySet().iterator();
   
     // loop over the method->joinpointPattern mappings
    while( joinPointIter.hasNext() ) {
     
       // get the current mapping: method->joinpointPattern
      Map.Entry<Integer, Map<String, CallWrapper>> curJoinPointMapping =
        joinPointIter.next();
     
       // get a iterator over the joinpointPattern->wrapper mappings
      Iterator<Map.Entry<String, CallWrapper>> wrapperIter
        = curJoinPointMapping.getValue().entrySet().iterator();
     
       // loop over the joinpointPattern->wrapper mappings
      while( wrapperIter.hasNext() ) {
        Map.Entry<String, CallWrapper> curWrapperEntry =
          wrapperIter.next();
       
         // get the current joinpoint->wrapper mapping
        curWrapperEntry.getValue().deinstall( adviceClass );
       
        // shall we deinstall the wrapper because
        // it contains no more advices?
        if( !curWrapperEntry.getValue().hasAdvices() ) { //todo: configurable option
         
          // remove the joinpointPattern->wrapper from the list
          wrapperIter.remove();
          // remove the wrapper from the method
          removeWrapper(
            curJoinPointMapping.getKey(),
            curWrapperEntry.getKey() );
        }
      }
    }
  }
 
  /**
   * Returns the wrapper for the given method and the given joinpointPattern.
   * This method is called by the {@link de.petris.dynamicaspects.wrapper.CallMapper CallMapper}.
   * The returned {@link de.petris.dynamicaspects.wrapper.CallWrapper CallWrapper} is then used
     * during runtime to execute the {@link de.petris.dynamicaspects.Advice Advice}s.
   *
   * @param methodIdx the index of the method
   * @param joinpointPattern the joinpointPattern
   * @return the wrapper which belongs to the arguments.
   */
  public CallWrapper getCallWrapper( int methodIdx, String joinpointPattern ) {
    return (CallWrapper)((Map)callWrappers.get(
        new Integer( methodIdx ))).get( joinpointPattern );
  }
 
 
  /**
   * Removes the wrapper from the given method at the given joinpoints.
   *
   * @param methodIdx index of the method.
   * @param joinpointPattern pattern of the joinpoints.
   */
  private void removeWrapper( int methodIdx, String joinpointPattern ) {
   
    // a method generator for the method
    MethodGen mGen =
      new MethodGen(
        targetClass.getMethods()[methodIdx],
        targetClass.getClassName(),
      constPoolGen );

    // a searcher to look for the joinpoints
    InstructionSearcher searcher =
      new InstructionSearcher( constPoolGen, mGen.getInstructionList() );
   
    // look for joinpoints
    List<CallMatch> targets =
      searcher.lookUpMethodCall(
          Pattern.compile( joinpointPattern ) );
   
    // a patcher for the given method
    CallWrapperMethodPatcher mp =
      new CallWrapperMethodPatcher(
        methodIdx, mGen, constPoolGen,
        joinpointPattern, targets );

    // get the new method without the wrapper
    targetClass.getMethods()[methodIdx] =
      mp.deinstall();
     
    // and update the class because of this change
     targetClass.setConstantPool(
       constPoolGen.getFinalConstantPool() );
  }
 
 
  /**
   * Installs an advice to a wrapper.
   * It the wrapper does not exist yet, the wrapper is installed.
   * Either the advice argument or the factory argument must not be null.
   * If the advice is not null it is installed, else the factory is used to
   * create the advice an the created advice will be installed.
   *
   * @param methodIdx Index of the method.
   * @param joinpointPattern Pattern of the joinpoints
   * @param factory the factory to create an advice, can be null.
   */
  private void handleInstall(
    Integer methodIdx, Pattern joinpointPattern,
    Advice advice, AdviceFactory factory ) {
   
        if( !( advice instanceof BeforeAfterAdvice ) ) {
            throw new AspectException(
                    "all advices installed via the "
                    + CallClassHandler.class.getName()
                    + " are expected to implement "
                    + BeforeAfterAdvice.class.getName()
                    + ", " + advice.getClass().getName()
                    + " does not respect this restriction!" )
        }
       
    // do we have wrapper for the method/joinpointPattern combination?
    if( ( callWrappers.containsKey( methodIdx ) )
      && ( ((Map)callWrappers.get( methodIdx )).containsKey(
          joinpointPattern.pattern() ) ) ) {

      // yes, so we use this wrapper
     
      // do we need to create the advice?
      if( advice == null ) {
        advice = (BeforeAfterAdvice)factory.getAdvice();
      }
     
      // get the already installed wrapper and install the advice
      ((CallWrapper)((Map)callWrappers.get( methodIdx )).get(
        joinpointPattern.pattern() )).install( (BeforeAfterAdvice)advice );
    }
    else {
   
      // no, so we install the wrapper first
     
      // get a method generator
      MethodGen mGen =
        new MethodGen(
          targetClass.getMethods()[methodIdx.intValue()],
          targetClass.getClassName(),
        constPoolGen );
     
      // get a searcher for the joinpoints
      InstructionSearcher searcher =
        new InstructionSearcher( constPoolGen, mGen.getInstructionList() );
           
      // look for joinpoints
      List<CallMatch> targets =
        searcher.lookUpMethodCall( joinpointPattern );
     
      // are there any joinpoints  matching the pattern in this methdod?
      if( !targets.isEmpty() ) {
       
        // yes, so we install a wrapper here
       
        // do we need to create the advice?
        if( advice == null ) {
          advice = (BeforeAfterAdvice)factory.getAdvice();
        }
       
        // do we have a joinpointPattern-map for this method yet?
        if( !callWrappers.containsKey( methodIdx ) ) {
          // create a map for all the joinpoinPattern->wrapper mappings
          // for this method
          callWrappers.put(
            methodIdx, new HashMap<String, CallWrapper>());
        }
       
        // create a wrapper
        CallWrapper cWrapper = new CallWrapper();
       
        Logger.info(
          "installing call wrapper for method %s " +
          "with index %s and pattern %s",
          mGen.getName(), methodIdx,
          joinpointPattern.pattern() );
       
        // install the wrapper to the method
        CallWrapperMethodPatcher mp =
          new CallWrapperMethodPatcher(
            methodIdx.intValue(), mGen, constPoolGen,
            joinpointPattern.pattern(), targets );

        // install the advice to the wrapper
        cWrapper.install( (BeforeAfterAdvice)advice );
       
        // create mapping: method->joinpointPattern->wrapper
        callWrappers.get( methodIdx ).put(
            joinpointPattern.pattern(), cWrapper );
       
        // get the new method and replace the old version
        targetClass.getMethods()[methodIdx.intValue()] =
          mp.install();
          
        // and update the class because of this change
         targetClass.setConstantPool(
           constPoolGen.getFinalConstantPool() );
      }
    }
  }
}
TOP

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

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.