001: //$Id$
002: //=====================================================================
003: //
004: //(history at end)
005: //
006:
007: package ch.ethz.prose;
008:
009: // used packages
010: import java.lang.reflect.*;
011: import junit.framework.*;
012:
013: import ch.ethz.inf.iks.jvmai.jvmdi.HotSwapAspectInterfaceImpl;
014: import ch.ethz.inf.iks.jvmai.jvmdi.HotSwapClassWeaver;
015: import ch.ethz.inf.iks.jvmai.jvmdi.HotSwapProvider;
016: import ch.ethz.jvmai.*;
017:
018: import java.io.InputStream; //import org.apache.bcel.classfile.*;
019: import org.apache.bcel.classfile.ClassParser; //import org.apache.bcel.verifier.*;
020: //import org.apache.bcel.classfile.ClassFormatException; // could not resolve this class
021: import org.apache.bcel.classfile.JavaClass; // not yet used
022: import org.apache.bcel.generic.*;
023:
024: /**
025: * JUnit testcase for class HotSwapProvider.
026: *
027: * @version $Revision$
028: * @author Gerald Linhofer
029: * @author Angela Nicoara
030: */
031: public class HotSwapTest extends TestCase {
032:
033: HotSwapAspectInterfaceImpl aspectInterface;
034:
035: //----------------------------------------------------------------------------------------------------------------------
036: private static void staticMethod() {
037: return;
038: }
039:
040: public static double staticField = 3;
041:
042: class MyDummy {
043: private int privateField = 1;
044: public int publicField = 2;
045:
046: public int publicMethod() {
047: return 1;
048: }
049:
050: private void privateMethod() {
051: return;
052: }
053: }
054:
055: class MyDummy2 {
056: private int privateField = 1;
057: public int publicField = 2;
058:
059: public int publicMethod() {
060: return privateField;
061: }
062:
063: private void privateMethod() {
064: return;
065: }
066: }
067:
068: class MyAspectInterface extends HotSwapAspectInterfaceImpl {
069:
070: private int dummy;
071:
072: /**
073: * Tests {@link ch.ethz.inf.iks.jvmai.jvmdi.HotSwapAspectInterfaceImpl#getMethodFromString
074: * getMethodFromString(java.lang.String)}.
075: *
076: */
077: void testGetMethodFromString() {
078: // do some calls with invalid parameters
079: int catcheExceptions = 0;
080: try {
081: try {
082: getMethodFromString(null);
083: } catch (NullPointerException e) {
084: catcheExceptions++;
085: }
086: try {
087: getMethodFromString("class");
088: } catch (InvalidIdException e) {
089: catcheExceptions++;
090: }
091: try {
092: getMethodFromString("class#method");
093: } catch (InvalidIdException e) {
094: catcheExceptions++;
095: }
096: try {
097: getMethodFromString("class#method#(I)Z");
098: } catch (ClassNotFoundException e) {
099: catcheExceptions++;
100: }
101: try {
102: getMethodFromString(this .getClass().getName()
103: + "#method#(I)Z");
104: } catch (NoSuchMethodError e) {
105: catcheExceptions++;
106: }
107: try {
108: getMethodFromString(this .getClass().getName()
109: + "#testGetMethodFromString#(I)Z");
110: } catch (NoSuchMethodError e) {
111: catcheExceptions++;
112: }
113: } catch (Exception e) {
114: fail("throws wrong assertion" + e.getClass().getName());
115: }
116: assertEquals("number of catched assertions", 6,
117: catcheExceptions);
118:
119: // try to get this method.
120: Member this Method = this .getClass().getDeclaredMethods()[0];
121: Member fetchedMethod = null;
122: try {
123: fetchedMethod = getMethodFromString(this .getClass()
124: .getName()
125: + '#' + this Method.getName() + "#()V");
126: } catch (Exception e) {
127: fail("exception " + e.getClass().getName()
128: + " @getMethodString(): " + e.getMessage());
129: }
130: assertEquals("could not resolve method id string",
131: this Method, fetchedMethod);
132:
133: // try to get a method from a not yet loaded class
134: try {
135: getMethodFromString("ch.ethz.prose.HotSwapTest$MyDummy#publicMethod#()I");
136: } catch (Exception e) {
137: fail("exception "
138: + e.getClass().getName()
139: + " @getMethodString() for a not loaded class: "
140: + e.getMessage());
141: }
142: // try to get a private method
143: try {
144: getMethodFromString("ch.ethz.prose.HotSwapTest$MyDummy#privateMethod#()V");
145: } catch (Exception e) {
146: fail("exception " + e.getClass().getName()
147: + " @getMethodString() for a private method: "
148: + e.getMessage());
149: }
150: // try to get a static method
151: try {
152: getMethodFromString("ch.ethz.prose.HotSwapTest#suite#()Ljunit/framework/Test;#");
153: } catch (Throwable e) {
154: fail("exception " + e.getClass().getName()
155: + " @getMethodString() for a static method: "
156: + e.getMessage());
157: }
158: }
159:
160: /**
161: * Tests {@link ch.ethz.inf.iks.jvmai.jvmdi.HotSwapAspectInterfaceImpl#getMethodFromString
162: * getMethodFromString(java.lang.String)}.
163: *
164: */
165: void testGetFieldFromString() {
166: // do some calls with invalid parameters
167: int catcheExceptions = 0;
168: try {
169: try {
170: getFieldFromString(null, true);
171: } catch (NullPointerException e) {
172: catcheExceptions++;
173: }
174: try {
175: getFieldFromString("class", false);
176: } catch (InvalidIdException e) {
177: catcheExceptions++;
178: }
179: try {
180: getFieldFromString("class#field", true);
181: } catch (InvalidIdException e) {
182: catcheExceptions++;
183: }
184: try {
185: getFieldFromString("class#method#I", false);
186: } catch (ClassNotFoundException e) {
187: catcheExceptions++;
188: }
189: try {
190: getFieldFromString(this .getClass().getName()
191: + "#field#I", false);
192: } catch (NoSuchFieldError e) {
193: catcheExceptions++;
194: }
195: try {
196: getFieldFromString(this .getClass().getName()
197: + "#dummy#Z", true);
198: } catch (NoSuchFieldError e) {
199: catcheExceptions++;
200: }
201: } catch (Exception e) {
202: fail("throws wrong assertion" + e.getClass().getName());
203: }
204: assertEquals("number of catched assertions", 6,
205: catcheExceptions);
206:
207: // try to get 'dummy'.
208: java.lang.reflect.Field this Field = this .getClass()
209: .getDeclaredFields()[0];
210: java.lang.reflect.Field fetchedField = null;
211: try {
212: fetchedField = getFieldFromString(this .getClass()
213: .getName()
214: + "#dummy#I", false);
215: } catch (Exception e) {
216: fail("exception " + e.getClass().getName()
217: + " @getFieldString(): " + e.getMessage());
218: }
219: assertEquals("could not resolve field id string",
220: this Field, fetchedField);
221:
222: // try to get a field from a not yet loaded class
223: try {
224: getFieldFromString(
225: "ch.ethz.prose.HotSwapTest$MyDummy2#publicField#I",
226: false);
227: } catch (Exception e) {
228: fail("exception " + e.getClass().getName()
229: + " @getFieldString() for a not loaded class: "
230: + e.getMessage());
231: }
232: // try to get a private field
233: try {
234: getFieldFromString(
235: "ch.ethz.prose.HotSwapTest$MyDummy2#privateField#I",
236: false);
237: } catch (Exception e) {
238: fail("exception " + e.getClass().getName()
239: + " @getFieldString() for a private field: "
240: + e.getMessage());
241: }
242: // try to get a static field
243: try {
244: getFieldFromString(
245: "ch.ethz.prose.HotSwapTest#staticField#I", true);
246: } catch (Throwable e) {
247: fail("exception " + e.getClass().getName()
248: + " @getFieldString() for a static field: "
249: + e.getMessage());
250: }
251: }
252:
253: /**
254: * Tests {@link ch.ethz.inf.iks.jvmai.jvmdi.HotSwapAspectInterfaceImpl#getBCELClassDefinition
255: * getBCELClassDefinition(java.lang.Class)}.
256: */
257: void testGetBCELClassDefinition() {
258: int exceptionCount = 0;
259: try {
260: try {
261: getBCELClassDefinition(null);
262: } catch (NullPointerException e) {
263: exceptionCount++;
264: }
265: // cannot test for NotInitializedException here, because once initialized,
266: // it's not possible to uninitialize the aspectInterface any more
267: // except by forced unloading of the class (this was done to be compatible
268: // with the debugging implementation of prose).
269: } catch (Exception e) {
270: fail("wrong exception type");
271: }
272: assertEquals("exception test", 1, exceptionCount);
273:
274: Class this Class = this .getClass();
275: JavaClass jc = null;
276: try {
277: jc = getBCELClassDefinition(this .getClass());
278: } catch (ClassNotFoundException e) {
279: fail("class not found");
280: }
281:
282: // class name
283: assertEquals("different class name", this Class.getName(),
284: jc.getClassName());
285:
286: // access flags. Note: the ACC_SUPER flag (0x020 or 32) may be set in class files
287: // but newer VMs ignores it and wont return it, when quering the flags with the
288: // reflection API.
289: int accessMask = 0x0611; // no ACC_SUPER flag, which is ignored by newer JVMs
290: int jvmAccessFlags = this Class.getModifiers();
291: int bcelAccessFlags = jc.getModifiers();
292: assertEquals("different modifier (access flags)",
293: jvmAccessFlags & accessMask, bcelAccessFlags
294: & accessMask);
295:
296: // methods
297: java.lang.reflect.Method[] methods = this Class
298: .getDeclaredMethods();
299: for (int i = 0; i < methods.length; i++) {
300: java.lang.reflect.Method method = methods[i];
301: assertNotNull("class file has no definition for "
302: + method.getName(), jc.getMethod(method));
303: }
304: }
305:
306: void testRedefineClass() {
307: }
308:
309: void testRedefineClasses() {
310: }
311:
312: }
313:
314: interface TestRedefineClasses {
315: public int method();
316: }
317:
318: class TestRedefineClasses1 {
319: public int method() {
320: return 1;
321: }
322: // static public int statMethod() { return 1; }
323: }
324:
325: class TestRedefineClasses2 {
326: public int method() {
327: return 2;
328: }
329: // static public int StatMethod() { return 2; }
330: }
331:
332: //--------------------------------------------------------------------------------------------------------------------------------------------
333:
334: /**
335: * Construct test with given name.
336: * @param name test name
337: */
338: public HotSwapTest(String name) {
339: super (name);
340: }
341:
342: /**
343: * Set up fixture.
344: */
345: protected void setUp() {
346: // String providerClassName = System.getProperty("ch.ethz.prose.JVMAIProvider","ch.ethz.inf.iks.jvmai.jvmdi.HotSwapProvider");
347: // Class providerClass = Class.forName(providerClassName);
348: // Provider provider = (Provider)providerClass.newInstance();
349: Provider provider = new HotSwapProvider();
350: aspectInterface = (HotSwapAspectInterfaceImpl) provider
351: .getAspectInterface();
352: aspectInterface.startup(new String[0], true);
353: }
354:
355: protected void teardown() {
356: aspectInterface.teardown();
357: }
358:
359: public void test_010_AspectInterface() {
360: MyAspectInterface ai = new MyAspectInterface();
361: ai.startup(new String[0], true);
362:
363: ai.testGetMethodFromString();
364: ai.testGetBCELClassDefinition();
365:
366: ai.teardown();
367: }
368:
369: public void test_020_RedefineClasses() {
370: Class cl1 = null, cl2 = null;
371: // InputStream is1=null, is2=null;//, is3=null, is4=null;
372: JavaClass jc1 = null, jc2 = null;
373:
374: // get classes
375: try {
376: cl1 = Class.forName(this .getClass().getName()
377: + "$TestRedefineClasses1");
378: cl2 = Class.forName(this .getClass().getName()
379: + "$TestRedefineClasses2");
380: } catch (ClassNotFoundException nfe) {
381: fail("Class not found: " + nfe.getMessage()
382: + " (internal error in the test case)");
383: }
384: Class[] cls1 = { cl1 };
385: Class[] cls2 = { cl2 };
386:
387: // Check if could get unmodified class definitions (contents of class files)
388: try {
389: jc1 = HotSwapAspectInterfaceImpl
390: .getBCELClassDefinition(cl1);
391: } catch (ClassNotFoundException ie) {
392: fail("getOriginalClassDefinition could not read class file (IOException)");
393: } catch (Exception fe) {
394: fail("getOriginalClassDefinition malformed class file");
395: }
396:
397: /*
398: // full verification
399: Verifier veri1 = VerifierFactory.getVerifier("ch.ethz.prose.JVMAspectInterfaceTest$TestRedefineClasses2");
400: VerificationResult vr1 = veri1.doPass1();
401: assertEquals( VerificationResult.VERIFIED_OK, vr1.getStatus() );
402: VerificationResult vr2 = veri1.doPass2();
403: assertEquals( VerificationResult.VERIFIED_OK, vr2.getStatus() );
404: VerificationResult vr3a = veri1.doPass3a(1);
405: assertEquals( VerificationResult.VERIFIED_OK, vr3a.getStatus() );
406: VerificationResult vr3b = veri1.doPass3b(1);
407: assertEquals( VerificationResult.VERIFIED_OK, vr3b.getStatus() );
408: */
409:
410: try {
411: jc2 = HotSwapAspectInterfaceImpl
412: .getBCELClassDefinition(cl2);
413: } catch (ClassNotFoundException ie) {
414: fail("getClassDefinition could not read unmodified class file (IOException)");
415: } catch (Exception fe) {
416: fail("getClassDefinition malformed unmodified class file");
417: }
418:
419: // test original methods
420: TestRedefineClasses1 test1 = new TestRedefineClasses1();
421: assertEquals(
422: "unmodified test1.method() failed (internal error in the test)",
423: 1, test1.method());
424:
425: //
426: // Redefine cl1 using cl2
427: //
428:
429: // change the behavour of cl1
430: ClassGen cgen = new ClassGen(jc1);
431: ConstantPoolGen cpgen = cgen.getConstantPool();
432: org.apache.bcel.classfile.Method meth = cgen.getMethodAt(1);
433: MethodGen mgen = new MethodGen(meth, cgen.getClassName(), cpgen);
434: cgen.removeMethod(meth);
435: InstructionList il = mgen.getInstructionList();
436: InstructionHandle ih = il.findHandle(0);
437: Instruction in = new ICONST(2);
438: ih.setInstruction(in);
439: cgen.addMethod(mgen.getMethod());
440: il.dispose();
441:
442: byte[] klassDef = cgen.getJavaClass().getBytes();
443: byte[][] defs = { klassDef };
444:
445: // redefine cls1 using cls2
446: try {
447: HotSwapClassWeaver.redefineClasses(cls1, defs);
448: } catch (RuntimeException re) {
449: fail("redefineClasses could not redefine class (RuntimeException) "
450: + re.getMessage());
451: }
452:
453: // test replaced methods
454: assertEquals("redefineClasses modified test1.method() failed",
455: 2, test1.method());
456: }
457:
458: /**
459: * Test suite.
460: * @return test instance
461: */
462: public static Test suite() {
463: return new TestSuite(HotSwapTest.class);
464: }
465:
466: }
467:
468: //======================================================================
469: //
470: // $Log$
471: //
|