Package ch.ethz.inf.iks.jvmai.jvmdi

Source Code of ch.ethz.inf.iks.jvmai.jvmdi.HotSwapFieldWeaver

//
//  This file is part of the prose package.
//
//  The contents of this file are subject to the Mozilla Public License
//  Version 1.1 (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.mozilla.org/MPL/
//
//  Software distributed under the License is distributed on an "AS IS" basis,
//  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
//  for the specific language governing rights and limitations under the
//  License.
//
//  The Original Code is prose.
//
//  The Initial Developer of the Original Code is Angela Nicoara. Portions
//  created by Angela Nicoara are Copyright (C) 2004 Angela Nicoara.
//  All Rights Reserved.
//
//  Contributor(s):
//  $Id: HotSwapFieldWeaver.java,v 1.2 2008/11/18 11:09:31 anicoara Exp $
// =====================================================================
//

package ch.ethz.inf.iks.jvmai.jvmdi;

import java.lang.reflect.Field;
//import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Member;
import java.util.*;

import ch.ethz.jvmai.MethodWeaver;

/**
* Remembers the fields where a callback should be inserted. For each method
* that references the specified field, a callback is woven using the
* MethodWeaver class.
*
* @author  Angela Nicoara
* @author  Johann Gyger
* @version $Revision: 1.2 $
*/
public class HotSwapFieldWeaver {

  // Codes used for {@link #status}
  protected final static int FW_UNCHANGED        = 0x000;
  protected final static int FW_ACCESS_ENABLED    = 0x001;
  protected final static int FW_MODIFICATION_ENABLED  = 0x002;
  protected final static int FW_MODIFIED        = 0x004;
  protected final static int FW_WOVEN          = 0x008;
  protected final static int FW_REGISTERED      = 0x010;

  /**
   * Holds informations about knonw field accesses and modifications
   * and gathers this informations.
   */
  private static HotSwapClassRegister classRegister = HotSwapClassRegister.getInstance();

  /**
   * Collection of all field weavers. For each field there exists exactly one
   * field weaver in the system.
   */
  protected static Map weavers = new HashMap();

  /**
   * Maps the string identifier for each field weaver to the weaver.
   */
  protected static Map weaverNames = new HashMap();

  /**
   *  Maps unique field ids to the field objects.
   */
  protected static Field fieldMap[] = new Field[1024];
  /**
   *  Manages unique method ids.
   */
  protected static UniqueIdArrayManager idMgr = new UniqueIdArrayManager();

  /**
   * Get a unique field weaver for `target'.
   *
   * @param target field that will be woven
   */
  public static synchronized HotSwapFieldWeaver getWeaver(Field target) {
    if (target == null)
      throw new NullPointerException("Parameter `target' must not be null.");

    HotSwapFieldWeaver result;
    synchronized(weavers) {
      result = (HotSwapFieldWeaver) weavers.get(target);
      if (result == null) {
        result = new HotSwapFieldWeaver(target);
        weavers.put(target, result);
        weaverNames.put(result.key, result);
      }
    }
    return result;
  };

  /**
   * Re-weave all modified f and activate them in the VM.
   */
  public static void commit() {
    synchronized(weavers) {
      // The Collection is copied to an array to allow removal of entries from the collection
      // while weaving.
      HotSwapFieldWeaver[] fweavers = new HotSwapFieldWeaver[weavers.size()];
      weavers.values().toArray( fweavers );
      for( int i = 0; i < fweavers.length; i++ ) {
        HotSwapFieldWeaver fw = fweavers[i];
        if( (FW_MODIFIED & fw.status) > 0 )
          try{ fw.weave(); }
        catch( ClassNotFoundException e)
        { System.err.println( "can not load class file for " + fw.target.getDeclaringClass().getName() ); }
        // TODO: remove old (inactive) weavers
      }
    }
  }

  /**
   * Reset all field weavers.
   */
  public static void resetAll() {
    synchronized(weavers) {
      weavers = new HashMap();
      weaverNames = new HashMap();
      fieldMap = new Field[1024];
      idMgr.reset();
    }
  }

  /**
   * Returns an identifier for <CODE>method</CODE> and
   * stores the association, so that {@link #idToMethod(int) idToMethod()}
   * may be used to get the <CODE>method</CODE> using the id.
   *
   * Note: only the id is unique, if <CODE>createNewMethodId(Method)
   * </CODE> is called more than one time with the same argument,
   * each call will create a new id and a new map entry.
   *
   * @param field that will be associated with a new identifier.
   * @return new identifier for <CODE>method</CODE>.
   */ 
  static int createNewFieldId( Field field ) {
    if( null == field )
      throw new NullPointerException();

    int result;

    synchronized( fieldMap ) {
      result = idMgr.newId();
      // check if map must be expanded
      int mapLength = fieldMap.length;
      if( mapLength <= result ) {
        Field newMap[] = new Field[ 2 * mapLength ];
        System.arraycopy( fieldMap, 0, newMap, 0, mapLength );
        fieldMap = newMap;
      }
      // add method to the map
      fieldMap[result] = field;
    }
    return result;
  }

  /**
   * Returns the Method object associated to <CODE>methodId</CODE>
   * or <CODE>null</CODE>, if <CODE>methodID</CODE> is not a valid
   * id.
   * The ids must be created with {@link #createNewFieldId createNewFieldId()}.
   *
   * @param fieldId id for the Method that will be returned.
   * @return Method associated to  <CODE>methodID<CODE> or <CODE>null</CODE>.
   * @throws ArrayOutOfBoundException
   */ 
  public static Field idToField( int fieldId ) {
    return fieldMap[fieldId];
  }

  /**
   * Field bound to this weaver.
   */
  protected Field target;

  /**
   * Unique numerical identifier for {#link: #target target}, created with
   * {@link HotSwapFieldWeaver#createNewFieldId createNewFieldId()}.
   */
  protected int targetId;

  /**
   * Holds the state of the weaver.
   */
  protected int status = FW_UNCHANGED;

  /**
   * Unique identifiere string for the target of this weaver. The format is
   * <CODE>ClassName#FieldName#JNISignature</CODE>.
   */
  protected String key;

  /**
   * Create a new field weaver.
   *
   * @param target target field for which callbacks will be woven
   */
  protected HotSwapFieldWeaver(Field target) {
    this.target = target;
    targetId = createNewFieldId( target );
    key = target.getDeclaringClass().getName() + '#' + target.getName() + '#' + JNIUtil.jniSignature( target.getType() );
  }

  /**
   * Enable field access join point.
   *
   * @param flag enable/disable
   */
  public void setFieldAccessEnabled(boolean flag) {
    //System.out.println("HotSwapFieldWeaver.setFieldAccessEnabled(" + String.valueOf(flag) + ")");
    if( flag != (0 < (FW_ACCESS_ENABLED & status)) ) {
      status = FW_MODIFIED | (flag ? status | FW_ACCESS_ENABLED : status & ~FW_ACCESS_ENABLED);
    }
  }

  /**
   * Enable field modification join point.
   *
   * @param flag enable/disable
   */
  public void setFieldModificationEnabled(boolean flag) {
    //System.out.println("HotSwapFieldWeaver.setFieldModificationEnabled(" + String.valueOf(flag) + ")");
    if( flag != (0 < (FW_MODIFICATION_ENABLED & status)) ) {
      status = FW_MODIFIED | (flag ? status | FW_MODIFICATION_ENABLED : status & ~FW_MODIFICATION_ENABLED);
    }
  }

  /**
   * Returns the target of this FieldWeaver.
   *
   * @return Field
   */
  public Field getTarget() {
    return target;
  }

  /**
   * Returns the unique id of the target of this field.
   * This is used as parameter for the advice callback
   * function.
   *
   * @return int target's id
   */
  public int getTargetId() {
    return targetId;
  }

  public String debugString() {
    StringBuffer sb = new StringBuffer();

    sb.append("FieldWeaver for: ");
    sb.append(target);
    sb.append("\n\tStatus: ");
    sb.append(status);
    sb.append("\n\tString id: ");
    sb.append(key);
    sb.append("\n\tint id: ");
    sb.append(targetId);

    return sb.toString();
  }

  /**
   * Returns a unique String identifier for the target field.
   * The format is <CODE>ClassName#FieldName#JNISignature</CODE>.
   *
   * @return String identifier for the target field.
   */
  public String getKey() {
    return key;
  }

  /**
   * Weave advice that are associated with the field in this weaver. The
   * weaving is actually done by the MethodWeaver. This method only informs the
   * MethodWeaver instances that are involved.
   *
   * @throws java.lang.ClassNotFoundException can not load the class defining
   *           the field (this will only be thrown for private fields).
   */
  protected void weave() throws ClassNotFoundException{
    //System.out.println("HotSwapFieldWeaver.weave() for " + target.getName());

    // first the already known accessors and/or modifiers will be woven,
    // afterwards the loaded classes will be scanned.
    //

    // Make shure that all methods of the classes, which have references to 'target'
    // are scanned for accesses to it.
    List targetClasses = (List) HotSwapClassRegister.knownFieldReferences.get( key );
    classRegister.scanClasses( targetClasses );

    // 1. Set the method weavers for all accessors
    // 1.a Get the registered entries
    List accessors = (List) HotSwapClassRegister.knownFieldAccesses.get( key );
    if( null != accessors ) {
      Iterator iter = accessors.iterator();
      while( iter.hasNext() ) {
        String str = (String) iter.next();
        // 1.b Convert each entry in a reflected method object
        Member member = HotSwapAspectInterfaceImpl.getMethodFromString( str );
        // 1.c Get the method weaver for the method
        MethodWeaver mw = HotSwapClassWeaver.getWeaver( member );
        // 1.d Register (or unregister) 'target'
        if( (FW_ACCESS_ENABLED & status) > 0 )
          mw.addFieldAccessor( this );
        else
          mw.removeFieldAccessor( this );
        //System.out.println("FieldWeaver field access changed: " + mw.getTarget().getName() );
      }
    }

    // 2. Set the method weavers for all modifiers
    // 2.a Get the registered entries
    List modifiers = (List) HotSwapClassRegister.knownFieldModifiers.get( key );
    if( null != modifiers ) {
      Iterator iter = modifiers.iterator();
      while( iter.hasNext() ) {
        String str = (String) iter.next();
        // 2.b Convert each entry in a reflected method object
        Member member = HotSwapAspectInterfaceImpl.getMethodFromString( str );
        // 2.c Get the method weaver for the method
        MethodWeaver mw = HotSwapClassWeaver.getWeaver( member );
        // 2.d Register (or unregister) 'target'
        if( (FW_MODIFICATION_ENABLED & status) > 0 )
          mw.addFieldModifier( this );
        else
          mw.removeFieldModifier( this );
        //System.out.println("FieldWeaver field modification changed: " + mw.getTarget().getName() );
      }
    }

    if( ((FW_ACCESS_ENABLED | FW_MODIFICATION_ENABLED) & status) == 0 ) {
      // 3. Remove the weaver, if there is no enabled watch,
      //     since only modified weavers get woven, this means
      //     that the watch was disabled.
      remove();
      return;
    }

    // 4. Scan all loaded classes to which 'target' is visible.
    scanPotentialAccesses();
    // 5. Change the state of this weaver.
    status = (status & ~FW_MODIFIED) | FW_WOVEN;
  }


  /**
   * Scanns all classes to which {@link #target target} is visible. The results
   * will be addes to {@link HotSwapClassRegister.knownFieldAccesses
   * HotSwapClassRegister.knownFieldAccesses} and {@link
   * HotSwapClassRegister.knownFieldModifications
   * HotSwapClassRegister.knownFieldModifications}. Classes that
   * will be loaded later, get scanned at loading.
   *
   * @throws ClassNotFoundException can not load the class that defines
   *           the field (this will only be thrown if the field is private).
   */
  private void scanPotentialAccesses() throws ClassNotFoundException {
    // prevent repeated registration for the same weaver.
    if( (FW_REGISTERED & status) > 0 )
      return;
    status |= FW_REGISTERED;

    // determine which classes should get scanned for this weaver.
    Class declaringClass = target.getDeclaringClass();
    switch( target.getModifiers() & (Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PUBLIC) ) {
    case Modifier.PRIVATE:
      // scan the class declaring the field.
      classRegister.registrationRequest( declaringClass );
      // scan inner classes
      Class[] innerClasses = declaringClass.getDeclaredClasses();
      for( int i = 0; i < innerClasses.length; i++ ) {
        classRegister.registrationRequest( innerClasses[i]);
      }
      break;

    case Modifier.PROTECTED:
      // scan all classes belonging to the same package
      classRegister.registrationRequest( declaringClass.getPackage() );
      // scan all subclasses
      classRegister.registrationRequestSubClasses( declaringClass );
      break;

    case Modifier.PUBLIC:
      // check class modifier (if the class is not public, it's only
      // accessible for classes from the same package).
      if( Modifier.isPublic( declaringClass.getModifiers() ) ) {
        classRegister.registrationRequestAll();
        break;
      }
      // if the class is not public, handle the field like
      // a field without modifiers.
    default:
      // scan all classes in the same package
      classRegister.registrationRequest( declaringClass.getPackage() );
    }
  }

  /**
   * Removes this field weaver.
   * Removes also all registrations in HotSwapClassRegister, which was done for it
   * and the fieldMap entry for it's target.
   */
  private void remove() {
    // 1. remove the weaver from the field weaver map.
    weavers.remove( target );
    weaverNames.remove( target.getDeclaringClass().getName() + '#' + target.getName() + '#' + JNIUtil.jniSignature( target.getType() ) );
    // 1a. remove the field map entry
    idMgr.releaseId( targetId );
    fieldMap[targetId] = null;

    // 2. remove registrations, done for this weaver.
    if( (FW_REGISTERED & status) > 0 )
      // If this field join point was never woven, it was also never registered.
      return;

    Class declaringClass = target.getDeclaringClass();
    switch( target.getModifiers() & (Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PUBLIC ) ) {
    case Modifier.PRIVATE:
      classRegister.removeRequest( declaringClass );
      Class[] innerClasses = declaringClass.getDeclaredClasses();
      for( int i = 0; i < innerClasses.length; i++ ) {
        classRegister.removeRequest( innerClasses[i]);
      }
      break;

    case Modifier.PROTECTED:
      // scan all classes belonging to the same package
      classRegister.removeRequest( declaringClass.getPackage() );
      // scan all subclasses
      classRegister.removeRequestSubClass( declaringClass );
      break;

    case Modifier.PUBLIC:
      // check class modifier (if the class is not public, it's only
      // accessible for classes from the same package).
      if( Modifier.isPublic( declaringClass.getModifiers() ) ) {
        classRegister.removeRequestAll();
        break;
      }
      // if the class is not public, handle the field like
      // a field without modifiers.
    default:
      // scan all classes in the same package
      classRegister.removeRequest( declaringClass.getPackage() );
    }
    status &= ~FW_REGISTERED;
  }

}
TOP

Related Classes of ch.ethz.inf.iks.jvmai.jvmdi.HotSwapFieldWeaver

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.