Call monitoring is annotation processor used for allowing/disallowing certain callers for some particular method in runtime.
- Check out the project from git repository and open it.
- Right click on
Processor
module and Build it (it's always better to use Rebuild). - Open
Build | Build artifacts
- Rebuild Processor artifact
- Compile and run Main.java
Call monitoring generates subclass that must be always returned as an instance of monitored class. Generated sublcass verifies caller and then delegates to superclass.
The way monitoring is performed creates some limitation on the way we write the class.
- Class should not be instantiated directly. Instead use static method which will return instance of subclass.
- Class cannot be final. We should be able to inherit from it.
- Monitored methods should not be abstract, static or final.
- There's no support for generics (for now).
Call monitoring provides three annotations for use. They are:
@Monitored
. Used for classes that contain methods to be monitored.@CalledBy(<list of several method's FQN>)
. Used to allow only given callers for annotated method.@CallStack(<list of call stack items>)
. Allow only given chain of callers at the moment of call.
package com.example.test;
import kz.edu.nu.monitored.annotations.CalledBy;
import kz.edu.nu.monitored.annotations.Monitored;
@Monitored
class MonitoredClass {
static MonitoredClass getInstance() {
return new MonitoredClass_Monitored();
}
@CalledBy("com.example.test.Main# allowedCaller")
int call(String s, int q) {
System.out.println(s);
return q;
}
}
package com.example.test;
public static void main(String args[]) {
allowedCaller();
try {
MonitoredClass cl = MonitoredClass.getInstance();
System.out.println(cl.call("Hello", 1));
} catch (Throwable t) {
System.out.println(t.getMessage());
}
}
static void allowedCaller() {
MonitoredClass cl = MonitoredClass.getInstance();
System.out.println(cl.call("Hello", 1));
}
package com.example.test;
import kz.edu.nu.monitored.Monitoring;
import java.util.Arrays;
import java.util.List;
class MonitoredClass_Monitored extends MonitoredClass {
MonitoredClass_Monitored() {
super();
}
int call(java.lang.String s, int q) {
String[] callerList = {
"com.example.test.Main# allowedCaller"
};
StackTraceElement[] trace = (new Throwable()).getStackTrace();
if (!Monitoring.verifyCallerList(callerList, trace)) {
throw new Error("Caller " + trace[1].getMethodName() + " is not allowed for call");
}
return super.call(s, q);
}
}
- Create annotation for your monitoring type.
- Create subclass of MonitoringInfo.java, add Type enum (which is used as factory).
- Create template for your monitoring body.
- Add some code to Monitoring.java (optional).
- Templates are written using Freemarker.
MonitoredProcessor.java
contains the logic of extraction data from source code.- Data model for constructors and methods is stored in
ExecutableModel
class.