CSC 123 : AspectJ Assignment Assignment Designation "aopasn" Due Wednesday 12/8 (last day of class) 0. A lot of programs are written that do not check for bounds and exceptions. For example: //////////////// public class fun { public static int factorial(int n, int ax) // tail-recursive factorial { if (n==0) return ax; else return factorial(n-1,ax*n) } public static void main(String[] args) { int x = factorial(5,4); // wrong answer x = factorial(-1,1); // infinite loop } } //////////////// There are two potential problems here. First, if n is accidentally passed a negative value, it will never return. Secondly, the *initial* value passed to ax must be 1, otherwise the wrong answer may be returned. Write aspects that cause an Error to be thrown when n is passed a negative value, and FORCE the initial value of ax to 1 if it's not. This is a bit more tricky than it looks, since your pointcut will have to distinguish between the initial call and subsequent, recursive calls. Demonstrate with a complete program. Preventing negative n's is easier and you should do that first as a way to warm up to aspectJ. a. write a before advice to throw an error (look at my sample programs) when factorial is passed a negative value for n. b. write an around advice that forces the initial ax parameter to be 1. Do NOT change any java code - the original program should stay in tact. 1. The following pretty looking program takes too long to run: public class fibonacci { public static long fib(int n) { if (n<2) return 1; else return fib(n-1)+fib(n-2); } public static void main(String[] args) { System.out.println("please wait for a couple of million years..."); System.out.println(fib(100)); } } We know how such programs can be optimized through dynamic programming: define an array to memorize all values of fib already computed so that the recursive calls will not cause redundant computation. That is, use an array of longs with values initialized to 0. Whenever fib(x) is computed, store the answer in array[x] so that next time fib(x) is called, we can just look up the answer. Only problem is, you don't want to mess up the pretty program above because then your math professor won't know what to do with it. So you need to write an aspect that adds this refinement to your program without changing what's already written. 2. Do the problem described in the "aopbank.java" (write an aspect that records customer transactions). For this problem, you may consider using a pertarget aspect, which means that a new aspect is created for each instance of bank account. You need to record at least all deposits and withdraws. Thinking point: how would you record invalid transactions - such as those rejected by the "auditor" and "passwordsecurity" aspects? Can you integrate the aspects together? Extra credit will be given if you figure this out. 3. (Optional - for extra credit) For this problem of the assignment, randomly select at least 4 Java programs of decent size. You can download some of the programs I have for my courses, some of your own (or your friends') past programs, as well as others you might find on the web. Don't choose ones all written by the same programmer. Please provide a brief description of each test program. Use aspectJ to record certain statistics about these programs, including: 1. number of times a variable within an object is 'get' versus number of times a variable is 'set'. Speculation has it that 85% of the time when a variable is accessed, it's a read access. Don't worry about local vars within functions. 2. number of times a method is called within the same object versus number of times it's called externally (a similar stat is the number of public v.s. private calls). 3. How often are functions called that return values (versus void) 4. How many of the programs you tested made recursive calls? (since if there's recursion then the recursive calls will dominate, it makes no sense to measure recursive vs. non-recursive calls). To do this problem, you can call thisJoinPoint.getSignature().toString() which returns a string representation of the function being called. For example, calls to the "fib" function in the above problem have signature "long fibonacci.fib(int)". You also need to keep track of all function calls using a *stack* of such call signatures. You push a new signature on the stack before each function call, and pop the stack afterwards. In fact, you can even detect mutual recursion with this technique. For example, in visitor pattern programs, accept calls visit, which in turn may call accept. Your program should be able to detect such cycles in call structure. Also, measure the depth of recursive calls (levels from top). What is the maximum depth of recursion exhibited by the test programs? Here's a stack class you can use (write your own if you don't like mine): class sigstack { private static final int size = 100000; private String[] S = new String[size]; private int tos = -1; // top of stack, -1 means empty stack void push(String N) { if (tos=0) return S[tos--]; else throw new Error("sigstack underflow"); } boolean found(String N) // is N anywhere on the stack { boolean ax = false; for(int i=tos;i>=0 && !ax;i--) ax = S[i].equals(N); return ax; } } // sigstack You need to write a bunch of before advice on appropriate point cuts, and collect the data. Note that all these stats are *runtime* stats. You can print the stats after join point execution(static void *.main(..)) or before call(* System.exit(..)). Another possibly tricky thing is to DISCOUNT calls from within your aspect and the stack class. Please give data in terms of percentages as well as raw numbers. Organize your data in some table or chart. Do you notice any common trends in your data?