Check the arguments for all annotated methods at runtime - java

How can you perform a check at startup-time on all the usages of an annotation?
For instance, I have this aspect, that is applied to the methods annotated with #Protect, that applies some security policy. Across the system, we have methods annotated with #Protect("valid-operation-1"), #Protect("valid-operation-2") or #Protect("INVALID-operation"). As soon as the application starts up, I'd like to check the arguments provided for all these annotations in order to detect such misconfigurations.
In particular, I'll check that we have a bean defined in the Spring application context whose ID matches the argument of the annotation. That means, to protect the method void drive(), I'll annotate with #Protect("drive"), and expect a bean protect_drive to be present in the application context.
You can easily just wait until the method is invoked, then the advice is called, and you check the argument. Then you'll see that INVALID-operation is wrongly defined. But this is too late.
Is it possible to have this checked for all annotated methods when the application starts?

If the Classes you want to check are Spring Beans, then you can use a BeanPostProcessor.
public class OnlyAScratchForAnPostProcessor {
#Inject
private ApplicationContext context;
#Override
public Object postProcessAfterInitialization(final Object bean,
final String beanName) throws BeansException {
ReflectionUtils.doWithMethods(bean.getClass(), new MethodCallback() {
#Override
public void doWith(Method method) throws IllegalArgumentException,
IllegalAccessException {
String expecedNameFromAnnotation = scanAnnotation(method);
if(expecedNameFromAnnotation != null) {
if(context.beanByName(expecedNameFromAnnotation) != null) {
throw new RuntimeException("illegal configuration");
}
}
}
String scanAnnotation(Method method){...}
}, ReflectionUtils.USER_DECLARED_METHODS);
}

Related

AOP and annotation 'inheritance'

let's consider the following situation.
#interface LoggedMethodInvocation{}
#LoggedMethodInvocation
#interface MonitoredMethodInvocation{}
I would like the #MonitoredMethodInvocation annotation implying the #LoggedMethodInvocation annotation.
class LoggingAOPConfig {
#Pointcut("#annotation(LoggedMethodInvocation)")
public void servicePointcut() {
}
#Around("servicePointcut()")
public Object logMethodInvocation(ProceedingJoinPoint pjp) throws Throwable {
// log the method invocation...
}
}
class MonitoringAOPConfig {
#Pointcut("#annotation(MonitoredMethodInvocation)")
public void servicePointcut() {
}
#Around("servicePointcut()")
public Object monitorResponseTime(ProceedingJoinPoint pjp) throws Throwable {
// add some meters to the method invocation
}
}
Now I would like to introduce some method, which shall be both monitored and logged. And I would like to annotate the method only with one annotation, namely #MonitoredMethodInvocation.
class SomeService {
#MonitoredMethodInvocation
Object someMethod(Object requestPayload) {
// ...
return responsePayload;
}
}
However it doesn't play, the logging aspect is not taken into the account.
There is spring's AnnotationUtils.findAnnotation which offers the needed functionality (of recognizing, whether the #LoggedMethodInvocation shall be considered). However, I don't know how to put this into the pointcut configuration.
How shall I modify the logging AOP config so it will recognize the logging annotation even if it is hidden behind the #MonitoredMethodInvocation?

How do you provide a pointcut bound annotation to an advice method?

So I've defined pointcuts for all methods and all classes of a particular annotation... what I want to do is retrieve the annotation value on every method call. Here is what I have so far
#Aspect
public class MyAspect {
#Pointcut("execution(* my.stuff..*(..))")
private void allMethods(){}
#Pointcut("within(#my.stuff.MyAnnotation*)")
private void myAnnotations(){}
#Pointcut("allMethods() && myAnnotations()")
private void myAnnotatedClassMethods(){}
#Before("myAnnotatedClassMethods()")
private void beforeMyAnnotatedClassMethods(){
System.out.println("my annotated class method detected.");
// I'd like to be able to access my class level annotation's value here.
}
}
Yes, you can have Spring AOP supply the value of the annotation annotating your target object's class.
You'll have to use the binding forms documented in the specification and propagate an argument across your #Pointcut methods.
For example
#Pointcut("execution(* my.stuff..*(..))")
private void allMethods() {
}
#Pointcut("#within(myAnnotation)")
private void myAnnotations(MyAnnotation myAnnotation) {
}
#Pointcut("allMethods() && myAnnotations(myAnnotation)")
private void myAnnotatedClassMethods(MyAnnotation myAnnotation) {
}
#Before("myAnnotatedClassMethods(myAnnotation)")
private void beforeMyAnnotatedClassMethods(MyAnnotation myAnnotation){
System.out.println("my annotated class method detected: " + myAnnotation);
}
Spring, starting from the myAnnotations pointcut, will match the name given in #within with a method parameter and use that to determine the annotation type. That is then propagated down to the beforeMyAnnotatedClassMethods advice through the myAnnotatedClassMethods pointcut.
The Spring AOP stack will then lookup the annotation value before invoking your #Before method and pass it as an argument.
An alternative, if you don't like the solution above, is to simply provide a JoinPoint parameter in your advice method. You can use it to resolve the target instance with getTarget and use that value to get the class annotation. For example,
#Before("myAnnotatedClassMethods()")
private void beforeMyAnnotatedClassMethods(JoinPoint joinPoint) {
System.out.println("my annotated class method detected: " + joinPoint.getTarget().getClass().getAnnotation(MyAnnotation.class));
}
I'm unsure how this will behave if the target is further wrapped in other proxies. The annotation might be "hidden" behind proxy classes. Use it carefully.

Discovering annotated methods

In my Spring application, I have components that use Spring's caching mechanism. Each #Cacheable annotation specifies the cache that is to be used. I'd like to autodiscover all the caches that are needed at startup so that they can be automatically configured.
The simplest approach seemed to create a marker interface (ex: CacheUser) to be used by each caching component:
#Component
public class ComponentA implements CacheUser {
#Cacheable("dictionaryCache")
public String getDefinition(String word) {
...
}
}
I would then have Spring autodiscover all the implementations of this interface and autowire them to a configuration list that can be used when configuring the cache manager(s). This works.
#Autowired
private Optional<List<CacheUser>> cacheUsers;
My plan was to take each discovered class and find all methods annotated with #Cacheable. From there I would access the annotation's properties and obtain the cache name. I'm using AnnotationUtils.findAnnotation() to get the annotation declaration.
That's where the plan falls apart. Spring actually wires proxies instead of the raw component, and the annotations aren't copied over to the proxies' methods. The only workaround I've found exploits the fact that the proxy implements Advised which provides access to the proxied class:
((Advised)proxy).getTargetSource().getTargetClass().getMethods()
From there I can get the original annotations, but this approach is clearly brittle.
So two questions, really:
Is there a better way to get to the annotations defined by the proxied class?
Can you suggest any other way to discover all uses of #Cacheable in my project? I'd love to do without a marker interface.
Thanks!
Spring has a lot of infrastructure interfaces which can help you tap into the lifecycle of the container and/or beans. For your purpose you want to use a BeanPostProcessor and the SmartInitializingSingleton.
The BeanPostProcessor will get a callback for all the beans constructed, you will only need to implement the the postProcessAfterInitialization method. You can in that method detect the annotations and fill a list of caches.
Then in the SmartInitializingSingletons afterSingletonsInstantiated method you use this list to bootstrap/init your caches.
Something like the following (it is untested but should give you an idea).
public class CacheInitialingProcessor implements BeanPostProcessor, SmartInitializingSingleton {
private final Set<String> caches = new HashSet<String>();
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class<?> targetClass = AopUtils.getTargetClass(bean);
ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
#Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Cacheable cacheable = AnnotationUtils.getAnnotation(method, Cacheable.class);
if (cacheable != null) {
caches.addAll(Arrays.asList(cacheable.cacheNames()));
}
}
});
return bean;
}
#Override
public void afterSingletonsInstantiated() {
for (String cache : caches) {
// inti caches.
}
}
}

Inject based on the package of the class

I have 2 modules containing classes:
blog.model.ArticleDAO
blog.model.CategoryDAO
users.model.UserDAO
users.model.UserGroupDAO
All these DAOs have a dependency on the same service, but I need to inject a different instance based on the package.
I mean the module blog should have a specific instance of MyService, and the module users should have another instance of MyService.
I don't want to create 2 named services because some day I may want to use the same service for all DAOs. Or I could also want to inject another specific instance for a specific class...
Is there a way to inject a service based on the package of a class?
A way to say:
inject foo (instance of MyService) into classes that are in blog.*
inject bar (instance of MyService) into classes that are in users.*
but keeping all my classes unaware of that! Their configuration should only state "Inject an instance of MyService".
First I want to say, I find this a strange requirement. I am also wondering why your DAOs need a Service. In a normal layered design, this is the opposite (the Service uses the DAO).
However I find the challenge interesting, I tried to use a FactoryBean to create a Java Proxy class which would redirect at runtime to the correct instance of MyService depending of the caller package. Here is the code:
public class CallerPackageAwareProxyFactoryBean implements
FactoryBean<MyService>, ApplicationContextAware {
private Class<?> targetServiceType;
private ApplicationContext applicationContext;
private InvocationHandler invocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (ReflectionUtils.isEqualsMethod(method)) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
} else if (ReflectionUtils.isHashCodeMethod(method)) {
// Use hashCode of service locator proxy.
return System.identityHashCode(proxy);
} else if (ReflectionUtils.isToStringMethod(method)) {
return "Service dispatcher: " + targetServiceType.getName();
} else {
String callerPackageFirstLevel = getCallerPackageFirstLevel();
Map<String, ?> beans = applicationContext
.getBeansOfType(targetServiceType);
for (Map.Entry<String, ?> beanEntry : beans.entrySet()) {
if (beanEntry.getKey().startsWith(callerPackageFirstLevel)) {
return method.invoke(beanEntry.getValue(), args);
}
}
throw new IllegalArgumentException(
String.format(
"Could not find any valid bean to forward call for method %s.",
method.getName()));
}
}
private String getCallerPackageFirstLevel() {
Throwable t = new Throwable();
StackTraceElement[] elements = t.getStackTrace();
String callerClassName = elements[3].getClassName();
return callerClassName.split("\\.")[0];
}
};
public MyService getObject() throws Exception {
return (MyService) Proxy.newProxyInstance(Thread.currentThread()
.getContextClassLoader(), new Class<?>[] { MyService.class },
invocationHandler);
}
public Class<?> getObjectType() {
return MyService.class;
}
public boolean isSingleton() {
return true;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void setTargetServiceType(Class<?> targetServiceType) {
this.targetServiceType = targetServiceType;
}
}
I didn't had to change anything to the Dao or Service configuration. I just had to add the creation of the FactoryBean in the Spring context:
<bean id="myService" class="stackoverflow.CallerPackageAwareProxyFactoryBean">
<property name="targetServiceType" value="a.b.c.MyService" />
</bean>
Maybe a few comments:
The caller package can only be get by creating an exception and looking at the stacktrace.
The code of the InvocationHandler is inspired from ServiceLocatorFactoryBean.
I am still wondering if there is an easier way but I think there is not.
You could replace part of the InvocationHandler to use a configuration Map (package => MyService bean name)
I would not recommend using such code in a productive environment.

How to get the set of beans that are to be created in Spring?

So here's the scenario:
I have a Spring XML configuration with some lazy-beans, some not lazy-beans and some beans that depend on other beans. Eventually Spring will resolve all this so that only the beans that are meant to be created are created.
The question: how can I programmatically tell what this set is?
When I use context.getBean(name) that initializes the bean. BeanDefinition.isLazyInit() will only tell me how I defined the bean.
Any other ideas?
ETA:
In DefaultListableBeanFactory:
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isInfoEnabled()) {
this.logger.info("Pre-instantiating singletons in " + this);
}
synchronized (this.beanDefinitionMap) {
for (Iterator it = this.beanDefinitionNames.iterator(); it.hasNext();) {
String beanName = (String) it.next();
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
if (factory instanceof SmartFactoryBean && ((SmartFactoryBean) factory).isEagerInit()) {
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}
}
}
The set of instantiable beans is initialized. When initializing this set any beans not in this set referenced by this set will also be created. From looking through the source it does not look like there's going to be any easy way to answer my question.
Perhaps
ApplicationContext.getBeanDefinitionNames()
Note that there is no (decent) way to determine which beans will be instantiated and which won't. If you are using ApplicationContextAware, you get access to all the beans at runtime, which makes this unpredictable.
So far my solution is:
Create ExtendedApplicationContext implementing ApplicationContextAware
Have the beans call initialized(this) on a static instance of ExtendedApplicationContext
Use this set plus the set of all bean definitions that are not singletons, abstract or lazy-initialized to create the set of intialized beans in ExtendedApplicationContext
Any better suggestions are welcome.
This is probably the best way, using a BeanPostProcessor:
public class IsIntializedBeanPostProcessor implements BeanPostProcessor {
private Set<String> initializedBeanNames = new HashSet<String>();
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
initializedBeanNames.add(beanName);
return bean;
}
public Set<String> getInitializedBeanNames() {
return initializedBeanNames;
}
}
Then all you have to do is include this as a bean somewhere in the Spring config in order for it to work.

Categories