0001: //
0002: // This file is part of the prose package.
0003: //
0004: // The contents of this file are subject to the Mozilla Public License
0005: // Version 1.1 (the "License"); you may not use this file except in
0006: // compliance with the License. You may obtain a copy of the License at
0007: // http://www.mozilla.org/MPL/
0008: //
0009: // Software distributed under the License is distributed on an "AS IS" basis,
0010: // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0011: // for the specific language governing rights and limitations under the
0012: // License.
0013: //
0014: // The Original Code is prose.
0015: //
0016: // Contributor(s):
0017: // $Id$
0018: // =====================================================================
0019: //
0020: // (history at end)
0021: //
0022: package ch.ethz.inf.iks.jvmai.jvmdi;
0023:
0024: import java.lang.reflect.Field;
0025: import java.lang.ref.WeakReference;
0026: import java.util.Collection;
0027: import java.util.Map;
0028: import java.util.List;
0029: import java.util.Set;
0030: import java.util.HashMap;
0031: import java.util.LinkedList;
0032: import java.util.HashSet;
0033: import java.util.Iterator;
0034: import java.util.ListIterator;
0035: import java.util.Arrays;
0036: import java.util.WeakHashMap;
0037:
0038: import org.apache.bcel.classfile.JavaClass;
0039: import org.apache.bcel.classfile.Constant;
0040: import org.apache.bcel.classfile.ConstantFieldref;
0041: import org.apache.bcel.classfile.ConstantCP;
0042: import org.apache.bcel.classfile.ConstantPool;
0043: import org.apache.bcel.classfile.ConstantNameAndType;
0044: import org.apache.bcel.generic.*;
0045:
0046: import ch.ethz.jvmai.JVMAIRuntimeException;
0047: import ch.ethz.jvmai.MethodWeaver;
0048:
0049: /**
0050: * Analyses classes and holds a register all found field
0051: * accesses, field modifications, method calls, exception
0052: * throws and exception catches.
0053: *
0054: * @author Angela Nicoara
0055: * @author Gerald Linhofer
0056: * @version $Revision: 1.1.2.2 $
0057: *
0058: */
0059: public class HotSwapClassRegister {
0060:
0061: /**
0062: * The one and only instance of this class.
0063: * (Singelton Pattern)
0064: */
0065: private static HotSwapClassRegister instance;
0066: /**
0067: * The JVMAspectInterface of this system or
0068: * <CODE>null</CODE> if not initialized.
0069: */
0070: private static HotSwapAspectInterfaceImpl aspectInterface;
0071: /**
0072: * Prefixes defining the scope of the prose system.
0073: * (Defines which packages should be affected by
0074: * Aspects, and which not.)
0075: */
0076: private static Set worldPrefixes;
0077: /**
0078: * <CODE>false</CODE> if only classes matching
0079: * the prefixes in {@link #worldPrefixes worldPrefixes}
0080: * should be scanned. Otherwise classes matching
0081: * the prefixes will be ignored.
0082: */
0083: private static boolean openWorldAssumption;
0084: /**
0085: * Holds the names of internal (Prose and Java) packages to
0086: * prevent them, and there sub packages, from beeing scanned.
0087: */
0088: private static Set internalPackages;
0089:
0090: static {
0091: internalPackages = new HashSet(6);
0092: // fill internalPackages with all packages which may cause
0093: // trouble during scanning or weaving.
0094: internalPackages.add("org.apache.bcel"); // some classes causes problems
0095: // add also all classes in the package 'java'
0096: // just to make shure there will be no problems.
0097: internalPackages.add("java"); // recommended by BCEL
0098: internalPackages.add("javax"); // recommended by BCEL
0099: internalPackages.add("sun"); // recommemded by BCEL
0100: }
0101:
0102: /**
0103: * Maps field names to collection of classes, which holds references to them.
0104: * This Map stores results from <CODE>scanConstantPool</CODE>.
0105: * The field name is in the form <CODE>Classname#FieldName#JNISignature</CODE>.
0106: */
0107: public static Map knownFieldReferences = new HashMap(1024);
0108:
0109: // /**
0110: // * Maps method names to collection of classes, which holds references to them.
0111: // * This Map stores results from <CODE>scanConstantPool</CODE>.
0112: // * The method name is in the form <CODE>Classname#MethodName#JNISignature</CODE>.
0113: // */
0114: // public static Map knownMethodReferences = new HashMap( 1024 );
0115:
0116: /**
0117: * Maps fields to all methods known to access them.
0118: * Identifier for the fields are the keys, the value
0119: * is a Collection, holding identifiers for the method.
0120: * <P>
0121: * The field identifier has the form <CODE>Classname#FieldName#JNISignature</CODE>
0122: * and the method identifier <CODE>Classname#MethodName#JNISignature#isStatic</CODE>.
0123: */
0124: public static Map knownFieldAccesses = new HashMap(1024);
0125: /**
0126: * Maps fields to all methods known to modifie them.
0127: * Identifier for the fields are the keys, the value
0128: * is a Collection, holding identifiers for the method.
0129: * <P>
0130: * The field identifier has the form <CODE>Classname#FieldName#JNISignature</CODE>
0131: * and the method identifier <CODE>Classname#MethodName#JNISignature#isStatic</CODE>.
0132: */
0133: public static Map knownFieldModifiers = new HashMap(1024);
0134: // /**
0135: // * Maps methods to all methods known to access them.
0136: // * Identifier for the called methods are the keys, the value
0137: // * is a Collection, holding identifiers for the caller.
0138: // */
0139: // public static Map knownMethodCalls = new HashMap( 1024 );
0140: // /**
0141: // * Maps exceptions to all methods known to throw them.
0142: // * Identifier for the exceptions are the keys, the value
0143: // * is a Collection, holding identifiers for the method.
0144: // *
0145: // * Note: This are the 'declared' exceptions.
0146: // */
0147: // public static Map knownExceptionThrows = new HashMap( 1024 );
0148: // /**
0149: // * Maps exceptions to all methods that may throw them.
0150: // * Identifier for the exceptions are the keys, the value
0151: // * is a Collection, holding identifiers for the method.
0152: // *
0153: // * This are potential exceptions, so they may be called
0154: // * or not. to set an watch for such an exception, an
0155: // * exception handler must be woven around the instruction,
0156: // * which may throw the exception. (ignore method calls, to
0157: // * avoid repeated advice calling for the same exception).
0158: // */
0159: // public static Map knownPotentialExceptionThrows = new HashMap( 1024 );
0160: // /**
0161: // * Maps exceptions to all methods known to catch them.
0162: // * Identifier for the exceptions are the keys, the value
0163: // * is a Collection, holding identifiers for the method.
0164: // */
0165: // public static Map knownExceptionCatches = new HashMap( 1024 );
0166:
0167: /**
0168: * <CODE>true</CODE> if all loaded classes should be scanned.
0169: * The value of this variable triggers if all new loaded classes
0170: * should be scanned or not.
0171: */
0172: private boolean registerAll = false;
0173: /**
0174: * Number of active weavers requested registerAll.
0175: */
0176: private int registerAllCount = 0;
0177: /**
0178: * Holds packages, which contains classes that should be
0179: * scanned.
0180: */
0181: private Map registeredPackages = new HashMap();
0182: /**
0183: * Holds classes for which all direct or indirect sub classes
0184: * should be scanned.
0185: */
0186: private Map registeredSuperClasses = new HashMap();
0187: /**
0188: * Remembers the scanned classes and maps them to
0189: * a collection with all <CODE>known...</CODE> map
0190: * entries, pointing to methods of this class.
0191: *
0192: * This is also used as monitor object to synchronize
0193: * modification of this map or any of the static known...
0194: * maps defined above.
0195: */
0196: private Map scannedClasses = new WeakHashMap(1024);
0197:
0198: /// Dummy value for using WeakHashMap as HashSet
0199: private static Object dummyValue = new Object();
0200:
0201: /**
0202: * Remembers the classes, which constant pools where scanned for
0203: * field and method references.
0204: */
0205: private Map scannedConstantPools = new WeakHashMap(1024);
0206:
0207: //--------------------------------------------------------------------------------------------------------------
0208: // Internal method to determine if a class should be scanned
0209: //--------------------------------------------------------------------------------------------------------------
0210:
0211: /**
0212: * Determines if a class may be scanned. This depends
0213: * on static constraints, which are hard coded or where
0214: * set at startup of prose ({@link #worldPrefixes worldPrefixes}
0215: * and {@link #internalPackages internalPackages}).
0216: *
0217: * @param className name of the class.
0218: * @return <CODE>true</CODE> if scanning of the class is
0219: * allowed.
0220: */
0221: private boolean isScanningAllowed(Class cls) {
0222: // Note: check for array class is only required for JVMDI not for JVMTI
0223: if (null == aspectInterface || cls.isInterface()
0224: || cls.isArray() /*|| cls.isPrimitive()*//*|| ch.ethz.prose.DefaultAspect.class.isAssignableFrom(cls)*/
0225: || null == cls)
0226: return false;
0227:
0228: String className = cls.getName();
0229: int pos = className.indexOf('.');
0230:
0231: if (!openWorldAssumption) {
0232: // check if any super package or the package it self
0233: // is in 'worldPrefixes'.
0234: while (-1 < pos) {
0235: String packageName = className.substring(0, pos);
0236: // Check if it's an internal class
0237: if (internalPackages.contains(packageName))
0238: return false;
0239:
0240: // Check if it 'part of our world'
0241: if (worldPrefixes.contains(packageName))
0242: return true;
0243:
0244: pos = className.indexOf('.', pos + 1);
0245: }
0246: return false;
0247: } else {
0248: // check if any super package or the package it self
0249: // is in 'internalPackage' or 'worldPrefixes'.
0250: while (-1 < pos) {
0251: String packageName = className.substring(0, pos);
0252: if (internalPackages.contains(packageName)
0253: || worldPrefixes.contains(packageName))
0254: return false;
0255:
0256: pos = className.indexOf('.', pos + 1);
0257: }
0258: return true;
0259: }
0260: }
0261:
0262: /**
0263: * Determines if a class should be scanned or not.
0264: * checks also if scanning is allowed. This checks
0265: * for 'dynamic' constraints, which may change at
0266: * each invocation of a <CODE>register...()<CODE>
0267: * or <CODE>unregister..()<CODE> method.
0268: *
0269: * @param cls Class that may be scanned.
0270: * @return <CODE>true</CODE> if <CODE>cls</CODE>
0271: * should be scanned.
0272: */
0273: private boolean isScanningRequired(Class cls) {
0274: return (!scannedConstantPools.containsKey(cls))
0275: && isScanningAllowed(cls)
0276: && (registerAll
0277: || registeredPackages.containsKey(cls
0278: .getPackage()) || hasRegisteredSuperClass(cls));
0279: }
0280:
0281: /**
0282: * Returns true if {@link #registeredSuperClasses registeredSuperClasses}
0283: * contains a super class of <CODE>cls</CODE>.
0284: *
0285: * @param cls
0286: * @return boolean
0287: */
0288: private boolean hasRegisteredSuperClass(Class cls) {
0289: // check for all super classes of cls,
0290: // if it's in registeredSuperClasses.
0291:
0292: // the problem with checking the super classes is, that
0293: // it ignores interfaces. so either an additional check
0294: // for interfaces, or a painfull slow checking of all
0295: // registered super classes is required.
0296:
0297: for (Class subClass = cls; null != subClass; subClass = subClass
0298: .getSuperclass())
0299: if (registeredSuperClasses.containsKey(subClass))
0300: return true;
0301:
0302: Class[] interfaces = cls.getInterfaces();
0303: for (int i = 0; i < interfaces.length; i++)
0304: if (hasRegisteredSuperClass(interfaces[i]))
0305: return true;
0306:
0307: return false;
0308: }
0309:
0310: //-------------------------------------------------------------------------------------------------------
0311: //
0312: //-------------------------------------------------------------------------------------------------------
0313:
0314: /**
0315: * Creates an uninitialized instance of this class.
0316: * private to force use of {@link #getInstance
0317: * getInstance()} (Singelton Pattern).
0318: */
0319: private HotSwapClassRegister() {
0320: rmv = new RegisterMethodVisitorImpl();
0321: }
0322:
0323: /**
0324: * Returns the instance of this class (this is a
0325: * 'singelton', so there's only one instance).
0326: *
0327: * @return HotSwapClassRegister instance.
0328: */
0329: public static HotSwapClassRegister getInstance() {
0330: if (null == instance)
0331: instance = new HotSwapClassRegister();
0332: return instance;
0333: }
0334:
0335: /**
0336: * Registers the JVMAspectInterface (must be called before the first
0337: * call to {@link #scan scan(java.lang.Class)}.
0338: *
0339: * @param aspectInterface
0340: * @param packagePrefixes
0341: * @param openWorldAssumption
0342: */
0343: protected static void setAspectInterface(
0344: HotSwapAspectInterfaceImpl aspectInterface,
0345: String[] packagePrefixes, boolean openWorldAssumption) {
0346: HotSwapClassRegister.aspectInterface = aspectInterface;
0347: HotSwapClassRegister.openWorldAssumption = openWorldAssumption;
0348: worldPrefixes = new HashSet(Arrays.asList(packagePrefixes));
0349: }
0350:
0351: //--------------------------------------------------------------------------------------------------
0352: // Public Methods to trigger scanning of classes (register them for scanning)
0353: //--------------------------------------------------------------------------------------------------
0354: /**
0355: * Notification that the class <code>cls</code> will be loaded.
0356: * Scanns the cls if required, and triggers weaving of the class
0357: * if it contains any join point.
0358: *
0359: * @param cls the class that has been prepared
0360: */
0361: public void classLoaded(Class cls) {
0362: synchronized (scannedClasses) {
0363: if (isScanningRequired(cls)) {
0364: try {
0365: //System.out.println("on class load: Scanning: " + cls.getName() );
0366: instance.scanConstantPool(cls);
0367: // redefine methods if required
0368: // this must be done after the class was
0369: // loaded!
0370: if (instance.weave) {
0371: aspectInterface.resumeNotification(Thread
0372: .currentThread());
0373: //System.out.println("on class load: Weaving: " + cls.getName() );
0374: }
0375: } catch (ClassNotFoundException e) {
0376: System.err.println("can not load classfile for "
0377: + cls.getName());
0378: System.err.println(e.getMessage());
0379: }
0380: }
0381: }
0382: }
0383:
0384: /**
0385: * Registers a class, which may hold a field, method call or
0386: * exception join point. This class will be scanned immediatly
0387: * and all potential join points will be registered.
0388: *
0389: * @param cls class that may contain join points.
0390: * @throws java.io.IOException can not read the class file.
0391: */
0392: public void registrationRequest(Class cls)
0393: throws ClassNotFoundException {
0394: synchronized (scannedClasses) {
0395: if ((!scannedConstantPools.containsKey(cls))
0396: && isScanningAllowed(cls)) {
0397: scanConstantPool(cls);
0398: }
0399: }
0400: }
0401:
0402: /**
0403: * Registers a package, which may contain classes that may
0404: * hold a field, method call or exception join point. This classes
0405: * will be scanned and all potential join points will be registrated.
0406: * <p>
0407: * New loaded classes will alse be scaned until {@link #removeRequestAll
0408: * removeRequestAll()} is called.
0409: *
0410: * @param pkg
0411: */
0412: public void registrationRequest(Package pkg) {
0413: if (null == aspectInterface)
0414: throw new JVMAIRuntimeException(this .getClass().getName()
0415: + ".setAspectInterface() must be called first");
0416:
0417: Integer regCount = (Integer) registeredPackages.get(pkg);
0418: if (null == regCount) {
0419: if (!registerAll) {
0420: String packageName = pkg.getName();
0421:
0422: // get all loaded classes
0423: List loadedClasses = aspectInterface.getLoadedClasses();
0424: Iterator iter = loadedClasses.iterator();
0425: while (iter.hasNext()) {
0426: Class loadedClass = (Class) iter.next();
0427: // look if the class belongs to pkg
0428: if (loadedClass.getName().startsWith(packageName)) {
0429: try {
0430: registrationRequest(loadedClass);
0431: } catch (ClassNotFoundException e) {
0432: // just report the error
0433: System.err
0434: .println(this .getClass().getName()
0435: + ".registrationRequest(Package): Could not read class file "
0436: + loadedClass.getName());
0437: System.err.println("-> " + e.getMessage());
0438: }
0439: }
0440: }
0441: }
0442: registeredPackages.put(pkg, new Integer(1));
0443: } else
0444: registeredPackages.put(pkg, new Integer(
0445: regCount.intValue() + 1));
0446: }
0447:
0448: /**
0449: * Scans all loaded subclasses of <CODE>cls</CODE> for
0450: * potential join points.
0451: * <P>
0452: * New loaded classes will also be scanned.
0453: *
0454: * @param cls
0455: */
0456: public void registrationRequestSubClasses(Class cls) {
0457: if (null == aspectInterface)
0458: throw new JVMAIRuntimeException(this .getClass().getName()
0459: + ".setAspectInterface() must be called first");
0460:
0461: Integer regCount = (Integer) registeredSuperClasses.get(cls);
0462: if (null == regCount) {
0463: if (!registerAll) {
0464: List loadedClasses = aspectInterface.getLoadedClasses();
0465: Iterator iter = loadedClasses.iterator();
0466: while (iter.hasNext()) {
0467: Class loadedClass = (Class) iter.next();
0468: if (cls.isAssignableFrom(loadedClass))
0469: try {
0470: registrationRequest(loadedClass);
0471: } catch (ClassNotFoundException e) {
0472: // just report the error
0473: System.err
0474: .println(this .getClass().getName()
0475: + ".registrationRequestSubClasses(Class): Could not read class file "
0476: + loadedClass.getName());
0477: System.err.println("-> " + e.getMessage());
0478: }
0479: }
0480: }
0481: registeredSuperClasses.put(cls, new Integer(1));
0482: } else {
0483: registeredSuperClasses.put(cls, new Integer(regCount
0484: .intValue() + 1));
0485: }
0486: }
0487:
0488: /**
0489: * Scans all loaded classes for potential field, method call and exception
0490: * join points.
0491: * <P>
0492: * New loaded classes will alse be scaned until {@link #removeRequestAll
0493: * removeRequestAll()} is called.
0494: */
0495: public void registrationRequestAll() {
0496: if (null == aspectInterface)
0497: throw new JVMAIRuntimeException(this .getClass().getName()
0498: + ".setAspectInterface() must be called first");
0499:
0500: registerAllCount++;
0501:
0502: if (!registerAll) {
0503: registerAll = true;
0504:
0505: // registrationRequest all loaded classes
0506: List loadedClasses = aspectInterface.getLoadedClasses();
0507: Iterator iter = loadedClasses.iterator();
0508: while (iter.hasNext()) {
0509: Class loadedClass = (Class) iter.next();
0510: try {
0511: registrationRequest(loadedClass);
0512: } catch (ClassNotFoundException e) {
0513: // just report the error
0514: //System.err.println(this.getClass().getName() + ".registrationRequestAll(): Could not read class file " + loadedClass.getName() );
0515: //System.err.println( "-> " + e.getMessage() );
0516: }
0517: }
0518: }
0519: }
0520:
0521: /**
0522: * Just for convinience, this method does nothing.
0523: * @param cls
0524: */
0525: public void removeRequest(Class cls) {
0526: // Nothing to do
0527: }
0528:
0529: /**
0530: * Stops scanning and registration of new loaded classes
0531: * belonging to Package <CODE>pkg</CODE>. If {@link #registrationRequestAll
0532: * registrationRequestAll()} is set, the classes will still be scanned until
0533: * {@link #removeRequestAll removeRequestAll()} is called.
0534: *
0535: * @param pkg
0536: */
0537: public void removeRequest(Package pkg) {
0538: Integer regCount = (Integer) registeredPackages.get(pkg);
0539: if (null != regCount && (1 < regCount.intValue()))
0540: registeredPackages.put(pkg, new Integer(
0541: regCount.intValue() - 1));
0542: else
0543: registeredPackages.remove(pkg);
0544: }
0545:
0546: /**
0547: * Stops scanning and registration of new loaded sub classes
0548: * of <CODE>cls</CODE>. If {@link #registrationRequestAll registrationRequestAll()}
0549: * is set, the classes will still be scanned until {@link
0550: * #removeRequestAll removeRequestAll()} is called.
0551: *
0552: * @param cls
0553: */
0554: public void removeRequestSubClass(Class cls) {
0555: Integer regCount = (Integer) registeredSuperClasses.get(cls);
0556: if (null != regCount && (1 < regCount.intValue()))
0557: registeredSuperClasses.put(cls, new Integer(regCount
0558: .intValue() - 1));
0559: else
0560: registeredSuperClasses.remove(cls);
0561: }
0562:
0563: /**
0564: * Stops scanning and registration of all new loaded classes.
0565: */
0566: public void removeRequestAll() {
0567: if (1 < registerAllCount) {
0568: registerAllCount = 0;
0569: registerAll = false;
0570: } else
0571: registerAllCount--;
0572: }
0573:
0574: /**
0575: * Clears all registrations and registration requests
0576: */
0577: public void reset() {
0578: // 1. Stop scanning of new loaded classes
0579: registerAll = false;
0580: registerAllCount = 0;
0581: registeredPackages.clear();
0582: registeredSuperClasses.clear();
0583: // 2. Clear informations
0584: synchronized (scannedClasses) {
0585: knownFieldReferences.clear();
0586: //knownMethodReferences.clear();
0587: //knownExceptionCatches.clear();
0588: //knownExceptionThrows.clear();
0589: knownFieldAccesses.clear();
0590: knownFieldModifiers.clear();
0591: //knownMethodCalls.clear();
0592: scannedClasses.clear();
0593: scannedConstantPools.clear();
0594: }
0595: }
0596:
0597: //---------------------------------------------------------------------------------------------------------------------------
0598: // Fields and Methods for scanning class files
0599: //---------------------------------------------------------------------------------------------------------------------------
0600:
0601: private boolean weave;
0602:
0603: /**
0604: * This object does the work: Scanning the methods and reporting
0605: * the mappings.
0606: */
0607: private RegisterMethodVisitorImpl rmv;
0608:
0609: /**
0610: * BCEL constant pool object for the class.
0611: */
0612: private ConstantPoolGen cpGen;
0613:
0614: /**
0615: * Scans the constant pool of class <CODE>cls</CODE> for method and field
0616: * references.
0617: *
0618: * @param cls the class that's constant pool should get scanned.
0619: * @throws ClassNotFoundException can not find the file defining the class.
0620: */
0621: protected void scanConstantPool(Class cls)
0622: throws ClassNotFoundException {
0623: if (scannedClasses.containsKey(cls))
0624: return;
0625:
0626: String className = cls.getName();
0627: // 0. Get the BCEL constant pool object
0628: JavaClass bcelClass = HotSwapAspectInterfaceImpl
0629: .getBCELClassDefinition(cls);
0630: ConstantPool constantPool = bcelClass.getConstantPool();
0631: Constant[] constants = constantPool.getConstantPool();
0632:
0633: // Remember the class, so it doesn't get scanned again.
0634: scannedConstantPools.put(cls, dummyValue);
0635:
0636: // 1. Iterate through all constant pool entries
0637: for (int i = 0; i < constants.length; i++) {
0638: Constant con = constants[i];
0639:
0640: // 2. Get field and method references
0641: if (con instanceof ConstantCP) {
0642: ConstantCP cp = (ConstantCP) con;
0643: ConstantNameAndType nt = (ConstantNameAndType) constants[cp
0644: .getNameAndTypeIndex()];
0645: // 3. Create a unique key (string) for the field or method
0646: String key = cp.getClass(constantPool) + '#'
0647: + nt.getName(constantPool) + '#'
0648: + nt.getSignature(constantPool);
0649:
0650: if (con instanceof ConstantFieldref) {
0651: // Constant is a field reference
0652:
0653: // 4. Look if the field must be woven
0654: if (HotSwapFieldWeaver.weaverNames.containsKey(key)) {
0655: // Scan all methods and return
0656: //System.out.println("ClassRegister.scanConstantPool: found key: " + key);
0657: scan(cls);
0658: return;
0659: }
0660: // 5. Get the entry for the field
0661: Collection col = (Collection) knownFieldReferences
0662: .get(key);
0663: if (null == col) {
0664: // 5a. Create a new entry for the field
0665: col = new LinkedList();
0666: knownFieldReferences.put(key, col);
0667: }
0668: // 6. Add a reference to cls to the entry
0669: col.add(new WeakReference(cls));
0670: }
0671:
0672: // The lines below will be used for the implementation of
0673: // Method call join points
0674: //
0675: // else {
0676: // // Constant is a method reference (ConstantMethodref or ConstantInterfaceMethodref)
0677: // // 4. Get the entry for the method
0678: // Collection col = (Collection) knownMethodReferences.get( key );
0679: // if( null == col ) {
0680: // // 4a. Create a new entry for the method
0681: // col = new LinkedList();
0682: // knownMethodReferences.put( key, col );
0683: // }
0684: // // 5. Add cls to the entry
0685: // col.add( new WeakReference( cls ) );
0686: // }
0687: }
0688:
0689: }
0690: }
0691:
0692: /**
0693: * Scans the method byte codes of a Collection of classes. Already scanned classes
0694: * will be ignored.
0695: *
0696: * @param cls Collection holding {@link java.lang.Class Class} objects, for the classes
0697: * that will be scanned.
0698: */
0699: public void scanClasses(List cls) {
0700: if (null == cls)
0701: return;
0702:
0703: ListIterator iter = cls.listIterator();
0704: while (iter.hasNext()) {
0705: Class klass = (Class) ((WeakReference) iter.next()).get();
0706: if (null == klass)
0707: iter.remove();
0708: else if (!scannedClasses.containsKey(klass))
0709: try {
0710: scan(klass);
0711: } catch (ClassNotFoundException e) {
0712: System.err
0713: .println("HotSwapClassRegister.scanClasses: "
0714: + e.getMessage());
0715: }
0716: }
0717: }
0718:
0719: /**
0720: * Scans the target method for field accesses, field modifications,
0721: * catched exceptions, method calls and method returns, and registers
0722: * all found actions to the according map.
0723: *
0724: * @param cls
0725: * @throws java.io.IOException can not read the class file
0726: */
0727: protected void scan(Class cls) throws ClassNotFoundException {
0728: weave = false;
0729:
0730: String className = cls.getName();
0731:
0732: // 1. Get the BCEL JavaClass object for cls
0733: JavaClass bcelClass;
0734: bcelClass = HotSwapAspectInterfaceImpl
0735: .getBCELClassDefinition(cls);
0736: // The constant pool is common for all methods belonging to a class, so we store it
0737: // here to make it accessible to all RegisterMethodVisitors
0738: cpGen = new ConstantPoolGen(bcelClass.getConstantPool());
0739:
0740: // 2. Scan each method defined in the class file
0741: org.apache.bcel.classfile.Method[] methods = bcelClass
0742: .getMethods();
0743:
0744: for (int j = 0; j < methods.length; j++) {
0745: org.apache.bcel.classfile.Method method = methods[j];
0746: if ((!method.isAbstract()) && (!method.isNative())) {
0747: MethodGen methodGen = new MethodGen(methods[j],
0748: className, cpGen);
0749: rmv.init(methodGen/*, entries, declMembers[j]*/);
0750: rmv.go();
0751: }
0752: }
0753: cpGen = null;
0754: scannedClasses.put(cls, dummyValue);
0755: }
0756:
0757: //------------------------------------------------------------------------------------------------------------
0758: // Inner class for scaning the method bodies.
0759: //------------------------------------------------------------------------------------------------------------
0760:
0761: /**
0762: * BCEL InstructionList visitor implementation, which registers all
0763: * field accesses, field modifications, potetial exception throws
0764: * (except for JVMExceptions, which may be thrown anywhere)
0765: * and method calls found in the InstructionList.
0766: */
0767: class RegisterMethodVisitorImpl extends EmptyVisitor {
0768: /**
0769: * List of instructions (method body)
0770: */
0771: private InstructionList instList;
0772: /**
0773: * String holding the class name and the method name of the
0774: * analyzed method separated by an # and the signature.
0775: * Used as key for registering actions.
0776: */
0777: private String key;
0778: /**
0779: * Temporary instruction handle. References the actual
0780: * instruction while iterating through instList.
0781: */
0782: private InstructionHandle handle;
0783:
0784: /**
0785: * Initialzes this object. The same object may be initialzed
0786: * multiple times.
0787: *
0788: * @param mg MethodGen for the target method that should be
0789: * scaned.
0790: */
0791: void init(MethodGen mg) {
0792: instList = mg.getInstructionList();
0793:
0794: key = mg.getClassName() + "#" + mg.getName() + '#'
0795: + mg.getSignature() + (mg.isStatic() ? "#" : "");
0796: }
0797:
0798: /**
0799: * Scan the target method, and registers any occourences of
0800: * field manipulations, method calls and exception throws
0801: */
0802: public synchronized void go() {
0803: if (null == instList)
0804: return;
0805:
0806: handle = instList.getStart();
0807: while (null != handle) {
0808: handle.getInstruction().accept(this );
0809: handle = handle.getNext();
0810: }
0811:
0812: instList.dispose();
0813: }
0814:
0815: // Exceptions and method calls are not yet implemented
0816:
0817: // public void visitATHROW(ATHROW obj) {
0818: // Class[] exceptions = obj.getExceptions();
0819: // for( int i = 0; i < exceptions.length; i++ ) {
0820: // Class exc = exceptions[i];
0821: // // String key = exc.getName();
0822: // List mws = (List) knownExceptionThrows.get( exc );
0823: // if( null == mws ) {
0824: // mws = new LinkedList();
0825: // knownExceptionThrows.put( exc, mws );
0826: // }
0827: // mws.add( key );
0828: // entries.add( mws );
0829: // }
0830: // }
0831:
0832: // /**
0833: // * Instruction may throw an exception.
0834: // *
0835: // * Note: this registers all potential exception throws, not
0836: // * only the declared throw instructions.
0837: // */
0838: // public void visitExceptionThrower(ExceptionThrower obj) {
0839: // // Excepetions thrown by InvokeInstruction mostly are thrown at
0840: // // an other method.
0841: // if( obj instanceof InvokeInstruction ) {
0842: // // TODO: implemention that avoid repeated calls
0843: // }
0844: //
0845: // Class[] exceptions = obj.getExceptions();
0846: // if( obj instanceof ATHROW)
0847: // // Explicite exception throws
0848: // for( int i = 0; i < exceptions.length; i++ ) {
0849: // Class exc = exceptions[i];
0850: // // String key = exc.getName();
0851: // List mws = (List) knownExceptionThrows.get( exc );
0852: // if( null == mws ) {
0853: // mws = new LinkedList();
0854: // knownExceptionThrows.put( exc, mws );
0855: // }
0856: // mws.add( key );
0857: // entries.add( mws );
0858: // }
0859: // else
0860: // // Instructions that may throw an exception
0861: // for( int i = 0; i < exceptions.length; i++ ) {
0862: // Class exc = exceptions[i];
0863: // List mws = (List) knownPotentialExceptionThrows.get( exc );
0864: // if( null == mws ) {
0865: // mws = new LinkedList();
0866: // knownPotentialExceptionThrows.put( exc, mws );
0867: // }
0868: // mws.add( key );
0869: // entries.add( mws );
0870: // }
0871: // }
0872: /**
0873: * Instruction accesses a member field.
0874: */
0875: public void visitGETFIELD(GETFIELD obj) {
0876: doVisitFieldAccess(obj, false);
0877: }
0878:
0879: /**
0880: * Instruction accesses a static field.
0881: */
0882: public void visitGETSTATIC(GETSTATIC obj) {
0883: doVisitFieldAccess(obj, true);
0884: }
0885:
0886: /**
0887: * Instruction modifies a member field.
0888: */
0889: public void visitPUTFIELD(PUTFIELD obj) {
0890: doVisitFieldModification(obj, false);
0891: }
0892:
0893: /**
0894: * Instruction modifies a static field.
0895: */
0896: public void visitPUTSTATIC(PUTSTATIC obj) {
0897: doVisitFieldModification(obj, true);
0898: }
0899:
0900: // /**
0901: // * Instruction calls a method.
0902: // */
0903: // public void visitInvokeInstruction(InvokeInstruction obj) {
0904: // Member method;
0905: // try{ method = aspectInterface.getMethodFromString( obj.getReferenceType(cpGen).toString(), obj.getName(cpGen), obj.getSignature(cpGen), obj instanceof INVOKESTATIC ); }
0906: // catch( Exception e ) { throw new JVMAIRuntimeException("can not resolve method: " + obj.getReferenceType(cpGen).toString() + "." + obj.getName(cpGen)); }
0907: // List mws = (List) knownMethodCalls.get( method );
0908: // if( null == mws ) {
0909: // mws = new LinkedList();
0910: // knownMethodCalls.put( method, mws );
0911: // }
0912: // mws.add( key );
0913: // entries.add( mws );
0914: //
0915: // // TODO: register also the method call weaver
0916: // }
0917:
0918: // /**
0919: // * Instruction may call a constructor.
0920: // */
0921: // public void visitNEW(NEW obj) {
0922: // }
0923:
0924: private void doVisitFieldAccess(FieldInstruction obj,
0925: boolean isStatic) {
0926: String fkey = obj.getReferenceType(cpGen).toString() + '#'
0927: + obj.getFieldName(cpGen) + '#'
0928: + obj.getSignature(cpGen);
0929: // 1. Register the field to knownFieldAccesses
0930:
0931: List mws = (List) knownFieldAccesses.get(fkey);
0932: if (null == mws) {
0933: mws = new LinkedList();
0934: knownFieldAccesses.put(fkey, mws);
0935: }
0936: mws.add(key);
0937:
0938: // 2. Register the field to the MethodWeaver for this method,
0939: // if there's a watch for it.
0940: HotSwapFieldWeaver fw = (HotSwapFieldWeaver) HotSwapFieldWeaver.weaverNames
0941: .get(fkey);
0942: if (null != fw
0943: && (HotSwapFieldWeaver.FW_ACCESS_ENABLED & fw.status) > 0) {
0944: // Weave method
0945: MethodWeaver mw;
0946: try {
0947: mw = HotSwapClassWeaver
0948: .getWeaver(HotSwapAspectInterfaceImpl
0949: .getMethodFromString(key));
0950: } catch (ClassNotFoundException e) {
0951: throw new JVMAIRuntimeException(
0952: "scanning a class file, but can not find the class: "
0953: + e.getMessage());
0954: }
0955: mw.addFieldAccessor(fw);
0956: weave = true;
0957: }
0958: }
0959:
0960: private void doVisitFieldModification(FieldInstruction obj,
0961: boolean isStatic) {
0962: // 1. Register the field to knownFieldModifications
0963: String fkey = obj.getReferenceType(cpGen).toString() + '#'
0964: + obj.getFieldName(cpGen) + '#'
0965: + obj.getSignature(cpGen);
0966:
0967: List mws = (List) knownFieldModifiers.get(fkey);
0968: if (null == mws) {
0969: mws = new LinkedList();
0970: knownFieldModifiers.put(fkey, mws);
0971: }
0972: mws.add(key);
0973:
0974: // 2. Register the field to the MethodWeaver for this method,
0975: // if there's a watch for it.
0976: HotSwapFieldWeaver fw = (HotSwapFieldWeaver) HotSwapFieldWeaver.weaverNames
0977: .get(fkey);
0978: if (null != fw
0979: && (HotSwapFieldWeaver.FW_MODIFICATION_ENABLED & fw.status) > 0) {
0980: // Weave method
0981: MethodWeaver mw;
0982: try {
0983: mw = HotSwapClassWeaver
0984: .getWeaver(HotSwapAspectInterfaceImpl
0985: .getMethodFromString(key));
0986: } catch (ClassNotFoundException e) {
0987: throw new JVMAIRuntimeException(
0988: "scanning a class file, but can not find the class: "
0989: + e.getMessage());
0990: }
0991: mw.addFieldModifier(fw);
0992: weave = true;
0993: }
0994: }
0995:
0996: private Field resolveFieldName(FieldInstruction obj,
0997: boolean isStatic) {
0998: try {
0999: return HotSwapAspectInterfaceImpl.getFieldFromString(
1000: obj.getReferenceType(cpGen).toString(), obj
1001: .getName(cpGen), obj
1002: .getSignature(cpGen), isStatic);
1003: } catch (Exception e) {
1004: throw new JVMAIRuntimeException(
1005: "can not resolve field "
1006: + obj.getReferenceType(cpGen)
1007: .toString() + "."
1008: + obj.getName(cpGen) + ": "
1009: + e.getClass().getName() + ": "
1010: + e.getMessage());
1011: }
1012: }
1013: }
1014:
1015: }
1016:
1017: //======================================================================
1018: //
1019: // $Log$
1020: //
|