001: //
002: // This file is part of the prose package.
003: //
004: // The contents of this file are subject to the Mozilla Public License
005: // Version 1.1 (the "License"); you may not use this file except in
006: // compliance with the License. You may obtain a copy of the License at
007: // http://www.mozilla.org/MPL/
008: //
009: // Software distributed under the License is distributed on an "AS IS" basis,
010: // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
011: // for the specific language governing rights and limitations under the
012: // License.
013: //
014: // The Original Code is prose.
015: //
016: // Contributor(s):
017: // $Id$
018: // =====================================================================
019: //
020: // (history at end)
021: //
022:
023: package ch.ethz.inf.iks.jvmai.jvmdi;
024:
025: import java.lang.reflect.Field; //import java.lang.reflect.Method;
026: import java.lang.reflect.Modifier;
027: import java.lang.reflect.Member;
028: import java.util.*;
029:
030: import ch.ethz.jvmai.MethodWeaver;
031:
032: /**
033: * Remembers the fields where a callback should be inserted. For each method
034: * that references the specified field, a callback is woven using the
035: * MethodWeaver class.
036: *
037: * @author Angela Nicoara
038: * @author Johann Gyger
039: * @version $Id$
040: */
041: public class HotSwapFieldWeaver {
042:
043: // Codes used for {@link #status}
044: protected final static int FW_UNCHANGED = 0x000;
045: protected final static int FW_ACCESS_ENABLED = 0x001;
046: protected final static int FW_MODIFICATION_ENABLED = 0x002;
047: protected final static int FW_MODIFIED = 0x004;
048: protected final static int FW_WOVEN = 0x008;
049: protected final static int FW_REGISTERED = 0x010;
050:
051: /**
052: * Holds informations about knonw field accesses and modifications
053: * and gathers this informations.
054: */
055: private static HotSwapClassRegister classRegister = HotSwapClassRegister
056: .getInstance();
057:
058: /**
059: * Collection of all field weavers. For each field there exists exactly one
060: * field weaver in the system.
061: */
062: protected static Map weavers = new HashMap();
063:
064: /**
065: * Maps the string identifier for each field weaver to the weaver.
066: */
067: protected static Map weaverNames = new HashMap();
068:
069: /**
070: * Maps unique field ids to the field objects.
071: */
072: protected static Field fieldMap[] = new Field[1024];
073: /**
074: * Manages unique method ids.
075: */
076: protected static UniqueIdArrayManager idMgr = new UniqueIdArrayManager();
077:
078: /**
079: * Get a unique field weaver for `target'.
080: *
081: * @param target field that will be woven
082: */
083: public static synchronized HotSwapFieldWeaver getWeaver(Field target) {
084: if (target == null)
085: throw new NullPointerException(
086: "Parameter `target' must not be null.");
087:
088: HotSwapFieldWeaver result;
089: synchronized (weavers) {
090: result = (HotSwapFieldWeaver) weavers.get(target);
091: if (result == null) {
092: result = new HotSwapFieldWeaver(target);
093: weavers.put(target, result);
094: weaverNames.put(result.key, result);
095: }
096: }
097: return result;
098: };
099:
100: /**
101: * Re-weave all modified f and activate them in the VM.
102: */
103: public static void commit() {
104: synchronized (weavers) {
105: // The Collection is copied to an array to allow removal of entries from the collection
106: // while weaving.
107: HotSwapFieldWeaver[] fweavers = new HotSwapFieldWeaver[weavers
108: .size()];
109: weavers.values().toArray(fweavers);
110: for (int i = 0; i < fweavers.length; i++) {
111: HotSwapFieldWeaver fw = fweavers[i];
112: if ((FW_MODIFIED & fw.status) > 0)
113: try {
114: fw.weave();
115: } catch (ClassNotFoundException e) {
116: System.err
117: .println("can not load class file for "
118: + fw.target.getDeclaringClass()
119: .getName());
120: }
121: // TODO: remove old (inactive) weavers
122: }
123: }
124: }
125:
126: /**
127: * Reset all field weavers.
128: */
129: public static void resetAll() {
130: synchronized (weavers) {
131: weavers = new HashMap();
132: weaverNames = new HashMap();
133: fieldMap = new Field[1024];
134: idMgr.reset();
135: }
136: }
137:
138: /**
139: * Returns an identifier for <CODE>method</CODE> and
140: * stores the association, so that {@link #idToMethod(int) idToMethod()}
141: * may be used to get the <CODE>method</CODE> using the id.
142: *
143: * Note: only the id is unique, if <CODE>createNewMethodId(Method)
144: * </CODE> is called more than one time with the same argument,
145: * each call will create a new id and a new map entry.
146: *
147: * @param field that will be associated with a new identifier.
148: * @return new identifier for <CODE>method</CODE>.
149: */
150: static int createNewFieldId(Field field) {
151: if (null == field)
152: throw new NullPointerException();
153:
154: int result;
155:
156: synchronized (fieldMap) {
157: result = idMgr.newId();
158: // check if map must be expanded
159: int mapLength = fieldMap.length;
160: if (mapLength <= result) {
161: Field newMap[] = new Field[2 * mapLength];
162: System.arraycopy(fieldMap, 0, newMap, 0, mapLength);
163: fieldMap = newMap;
164: }
165: // add method to the map
166: fieldMap[result] = field;
167: }
168: return result;
169: }
170:
171: /**
172: * Returns the Method object associated to <CODE>methodId</CODE>
173: * or <CODE>null</CODE>, if <CODE>methodID</CODE> is not a valid
174: * id.
175: * The ids must be created with {@link #createNewFieldId createNewFieldId()}.
176: *
177: * @param fieldId id for the Method that will be returned.
178: * @return Method associated to <CODE>methodID<CODE> or <CODE>null</CODE>.
179: * @throws ArrayOutOfBoundException
180: */
181: public static Field idToField(int fieldId) {
182: return fieldMap[fieldId];
183: }
184:
185: /**
186: * Field bound to this weaver.
187: */
188: protected Field target;
189:
190: /**
191: * Unique numerical identifier for {#link: #target target}, created with
192: * {@link HotSwapFieldWeaver#createNewFieldId createNewFieldId()}.
193: */
194: protected int targetId;
195:
196: /**
197: * Holds the state of the weaver.
198: */
199: protected int status = FW_UNCHANGED;
200:
201: /**
202: * Unique identifiere string for the target of this weaver. The format is
203: * <CODE>ClassName#FieldName#JNISignature</CODE>.
204: */
205: protected String key;
206:
207: /**
208: * Create a new field weaver.
209: *
210: * @param target target field for which callbacks will be woven
211: */
212: protected HotSwapFieldWeaver(Field target) {
213: this .target = target;
214: targetId = createNewFieldId(target);
215: key = target.getDeclaringClass().getName() + '#'
216: + target.getName() + '#'
217: + JNIUtil.jniSignature(target.getType());
218: }
219:
220: /**
221: * Enable field access join point.
222: *
223: * @param flag enable/disable
224: */
225: public void setFieldAccessEnabled(boolean flag) {
226: //System.out.println("HotSwapFieldWeaver.setFieldAccessEnabled(" + String.valueOf(flag) + ")");
227: if (flag != (0 < (FW_ACCESS_ENABLED & status))) {
228: status = FW_MODIFIED
229: | (flag ? status | FW_ACCESS_ENABLED : status
230: & ~FW_ACCESS_ENABLED);
231: }
232: }
233:
234: /**
235: * Enable field modification join point.
236: *
237: * @param flag enable/disable
238: */
239: public void setFieldModificationEnabled(boolean flag) {
240: //System.out.println("HotSwapFieldWeaver.setFieldModificationEnabled(" + String.valueOf(flag) + ")");
241: if (flag != (0 < (FW_MODIFICATION_ENABLED & status))) {
242: status = FW_MODIFIED
243: | (flag ? status | FW_MODIFICATION_ENABLED : status
244: & ~FW_MODIFICATION_ENABLED);
245: }
246: }
247:
248: /**
249: * Returns the target of this FieldWeaver.
250: *
251: * @return Field
252: */
253: public Field getTarget() {
254: return target;
255: }
256:
257: /**
258: * Returns the unique id of the target of this field.
259: * This is used as parameter for the advice callback
260: * function.
261: *
262: * @return int target's id
263: */
264: public int getTargetId() {
265: return targetId;
266: }
267:
268: public String debugString() {
269: StringBuffer sb = new StringBuffer();
270:
271: sb.append("FieldWeaver for: ");
272: sb.append(target);
273: sb.append("\n\tStatus: ");
274: sb.append(status);
275: sb.append("\n\tString id: ");
276: sb.append(key);
277: sb.append("\n\tint id: ");
278: sb.append(targetId);
279:
280: return sb.toString();
281: }
282:
283: /**
284: * Returns a unique String identifier for the target field.
285: * The format is <CODE>ClassName#FieldName#JNISignature</CODE>.
286: *
287: * @return String identifier for the target field.
288: */
289: public String getKey() {
290: return key;
291: }
292:
293: /**
294: * Weave advice that are associated with the field in this weaver. The
295: * weaving is actually done by the MethodWeaver. This method only informs the
296: * MethodWeaver instances that are involved.
297: *
298: * @throws java.lang.ClassNotFoundException can not load the class defining
299: * the field (this will only be thrown for private fields).
300: */
301: protected void weave() throws ClassNotFoundException {
302: //System.out.println("HotSwapFieldWeaver.weave() for " + target.getName());
303:
304: // first the already known accessors and/or modifiers will be woven,
305: // afterwards the loaded classes will be scanned.
306: //
307:
308: // Make shure that all methods of the classes, which have references to 'target'
309: // are scanned for accesses to it.
310: List targetClasses = (List) HotSwapClassRegister.knownFieldReferences
311: .get(key);
312: classRegister.scanClasses(targetClasses);
313:
314: // 1. Set the method weavers for all accessors
315: // 1.a Get the registered entries
316: List accessors = (List) HotSwapClassRegister.knownFieldAccesses
317: .get(key);
318: if (null != accessors) {
319: Iterator iter = accessors.iterator();
320: while (iter.hasNext()) {
321: String str = (String) iter.next();
322: // 1.b Convert each entry in a reflected method object
323: Member member = HotSwapAspectInterfaceImpl
324: .getMethodFromString(str);
325: // 1.c Get the method weaver for the method
326: MethodWeaver mw = HotSwapClassWeaver.getWeaver(member);
327: // 1.d Register (or unregister) 'target'
328: if ((FW_ACCESS_ENABLED & status) > 0)
329: mw.addFieldAccessor(this );
330: else
331: mw.removeFieldAccessor(this );
332: //System.out.println("FieldWeaver field access changed: " + mw.getTarget().getName() );
333: }
334: }
335:
336: // 2. Set the method weavers for all modifiers
337: // 2.a Get the registered entries
338: List modifiers = (List) HotSwapClassRegister.knownFieldModifiers
339: .get(key);
340: if (null != modifiers) {
341: Iterator iter = modifiers.iterator();
342: while (iter.hasNext()) {
343: String str = (String) iter.next();
344: // 2.b Convert each entry in a reflected method object
345: Member member = HotSwapAspectInterfaceImpl
346: .getMethodFromString(str);
347: // 2.c Get the method weaver for the method
348: MethodWeaver mw = HotSwapClassWeaver.getWeaver(member);
349: // 2.d Register (or unregister) 'target'
350: if ((FW_MODIFICATION_ENABLED & status) > 0)
351: mw.addFieldModifier(this );
352: else
353: mw.removeFieldModifier(this );
354: //System.out.println("FieldWeaver field modification changed: " + mw.getTarget().getName() );
355: }
356: }
357:
358: if (((FW_ACCESS_ENABLED | FW_MODIFICATION_ENABLED) & status) == 0) {
359: // 3. Remove the weaver, if there is no enabled watch,
360: // since only modified weavers get woven, this means
361: // that the watch was disabled.
362: remove();
363: return;
364: }
365:
366: // 4. Scan all loaded classes to which 'target' is visible.
367: scanPotentialAccesses();
368: // 5. Change the state of this weaver.
369: status = (status & ~FW_MODIFIED) | FW_WOVEN;
370: }
371:
372: /**
373: * Scanns all classes to which {@link #target target} is visible. The results
374: * will be addes to {@link HotSwapClassRegister.knownFieldAccesses
375: * HotSwapClassRegister.knownFieldAccesses} and {@link
376: * HotSwapClassRegister.knownFieldModifications
377: * HotSwapClassRegister.knownFieldModifications}. Classes that
378: * will be loaded later, get scanned at loading.
379: *
380: * @throws ClassNotFoundException can not load the class that defines
381: * the field (this will only be thrown if the field is private).
382: */
383: private void scanPotentialAccesses() throws ClassNotFoundException {
384: // prevent repeated registration for the same weaver.
385: if ((FW_REGISTERED & status) > 0)
386: return;
387: status |= FW_REGISTERED;
388:
389: // determine which classes should get scanned for this weaver.
390: Class declaringClass = target.getDeclaringClass();
391: switch (target.getModifiers()
392: & (Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PUBLIC)) {
393: case Modifier.PRIVATE:
394: // scan the class declaring the field.
395: classRegister.registrationRequest(declaringClass);
396: // scan inner classes
397: Class[] innerClasses = declaringClass.getDeclaredClasses();
398: for (int i = 0; i < innerClasses.length; i++) {
399: classRegister.registrationRequest(innerClasses[i]);
400: }
401: break;
402:
403: case Modifier.PROTECTED:
404: // scan all classes belonging to the same package
405: classRegister.registrationRequest(declaringClass
406: .getPackage());
407: // scan all subclasses
408: classRegister.registrationRequestSubClasses(declaringClass);
409: break;
410:
411: case Modifier.PUBLIC:
412: // check class modifier (if the class is not public, it's only
413: // accessible for classes from the same package).
414: if (Modifier.isPublic(declaringClass.getModifiers())) {
415: classRegister.registrationRequestAll();
416: break;
417: }
418: // if the class is not public, handle the field like
419: // a field without modifiers.
420: default:
421: // scan all classes in the same package
422: classRegister.registrationRequest(declaringClass
423: .getPackage());
424: }
425: }
426:
427: /**
428: * Removes this field weaver.
429: * Removes also all registrations in HotSwapClassRegister, which was done for it
430: * and the fieldMap entry for it's target.
431: */
432: private void remove() {
433: // 1. remove the weaver from the field weaver map.
434: weavers.remove(target);
435: weaverNames.remove(target.getDeclaringClass().getName() + '#'
436: + target.getName() + '#'
437: + JNIUtil.jniSignature(target.getType()));
438: // 1a. remove the field map entry
439: idMgr.releaseId(targetId);
440: fieldMap[targetId] = null;
441:
442: // 2. remove registrations, done for this weaver.
443: if ((FW_REGISTERED & status) > 0)
444: // If this field join point was never woven, it was also never registered.
445: return;
446:
447: Class declaringClass = target.getDeclaringClass();
448: switch (target.getModifiers()
449: & (Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PUBLIC)) {
450: case Modifier.PRIVATE:
451: classRegister.removeRequest(declaringClass);
452: Class[] innerClasses = declaringClass.getDeclaredClasses();
453: for (int i = 0; i < innerClasses.length; i++) {
454: classRegister.removeRequest(innerClasses[i]);
455: }
456: break;
457:
458: case Modifier.PROTECTED:
459: // scan all classes belonging to the same package
460: classRegister.removeRequest(declaringClass.getPackage());
461: // scan all subclasses
462: classRegister.removeRequestSubClass(declaringClass);
463: break;
464:
465: case Modifier.PUBLIC:
466: // check class modifier (if the class is not public, it's only
467: // accessible for classes from the same package).
468: if (Modifier.isPublic(declaringClass.getModifiers())) {
469: classRegister.removeRequestAll();
470: break;
471: }
472: // if the class is not public, handle the field like
473: // a field without modifiers.
474: default:
475: // scan all classes in the same package
476: classRegister.removeRequest(declaringClass.getPackage());
477: }
478: status &= ~FW_REGISTERED;
479: }
480:
481: }
482:
483: //======================================================================
484: //
485: // $Log$
486: //
|