Spring AOP custom annotation, getting Null with annotation arguments - java

I am using this custom annotation for logging execution time, annotation could be present on method or class in which all public methods have it. Everything works fine, except in case of method level "LogExecutionTime logExecutionTime" comes null. This throws an NPE.
#Around("#annotation(logExecutionTime) || #within(logExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint, LogExecutionTime logExecutionTime) throws Throwable {
final Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass());
final String name = joinPoint.toShortString();
final StopWatch stopWatch = new StopWatch(name);
stopWatch.start(name);
try {
return joinPoint.proceed();
} finally {
stopWatch.stop();
if (logExecutionTime.value()) {
logger.info(joinPoint.getSignature().getName() + ".time=", stopWatch.getTotalTimeSeconds());
}
}
}
if I reverse the order-
#Around("#within(logExecutionTime) || #annotation(logExecutionTime)")
the behavior reverses and I get a valid object at method level and null at class level annotated methods.
I have worked around this by having 2 explicit methods and separating the two-
#Around("#within(logExecutionTime)")
public Object logExecutionTimeClassLevel(ProceedingJoinPoint joinPoint, LogExecutionTime logExecutionTime) throws Throwable {
return logExecutionTimeMethodLevel(joinPoint, logExecutionTime);
}
#Around("#annotation(logExecutionTime)")
public Object logExecutionTimeMethodLevel(ProceedingJoinPoint joinPoint, LogExecutionTime logExecutionTime) throws Throwable {
final Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass());
final String name = joinPoint.toShortString();
final StopWatch stopWatch = new StopWatch(name);
stopWatch.start(name);
try {
return joinPoint.proceed();
} finally {
stopWatch.stop();
if (logExecutionTime.value()) {
logger.info(joinPoint.getSignature().getName() + ".time=", stopWatch.getTotalTimeMillis());
}
}
Was hoping to understand this behavior, when we use OR '||' with two pointcuts.
class level
#LogExecutionTime
#Component
public class CleanUpService implements ICleanUpService { ... }
method level
#Scheduled(fixedDelay = 100)
#LogExecutionTime(false)
public void processMessageQueue() { ... }

I came to run you example, and reproduce the same example as yours, when it come to runtime expression is same weird because when you specify the annotation on class level and you write this expression
#Around(" #within(logExecutionTime) || #annotation(logExecutionTime) ")
The point cut will evaluate to true for you class (event you annotation its available in joinPoint.getTarget().getClass().getAnnotations(), )
Now when it come to binding the variable the compiler check all your expressions that mean binding #within(logExecutionTime) to variable logExecutionTime and #annotation(logExecutionTime) to the same variable , if the method is not annotated it will ge null, => override the initial with, that cause all senarios you mention.
Try to put this expression #within(logExecutionTime) || #annotation(logExecutionTime) || #within(logExecutionTime)
and you'll get you variable not null which prove what i said, last #within(logExecutionTime) override what precedent
The key here is that the logic applied to select the point cut matching not the same when it come context-binding
Now when it come to AOP point-cut you must be careful and follow best practice as the spring team they mention here to avoid weird runtime results
Cheers

This cannot work, it does not even compile with the AspectJ compiler. Maybe in your IDE and with Spring AOP you do not see any warnings or errors, but I see:
ambiguous binding of parameter(s) logExecutionTime across '||' in pointcut
This means that it is not clear which annotation should be selected if e.g. both the class and the method contain an instance of that annotation. It is, as the error message said, ambiguous. But ambiguous parameter bindings across || are not permitted. They can also happen if you try to bind values from different "or" branches to a single parameter in an args() list.

I had the same problem. What you want is exactly same as Spring #Transcriptional behaves (I mean, class level or method level annotation with parameters). I used your solution but to get the class level parameter value (as the annotation object received null), I used reflection. I know it is a dirty solution! But I tried other solutions and couldn't find!
Her is the full code. This will call the advice code either the annotation is used on a class or a method. If the annotation is placed on both (class and method), the method takes the precedence.
#Aspect
#Configurable
#Component
public class DynamicValueAspect {
#Around(" #within(dynamicValueAnnotation) || #annotation(dynamicValueAnnotation))")
public Object process(ProceedingJoinPoint joinPoint, DynamicValue dynamicValueAnnotation) throws Throwable {
String annotationParameter;
if (dynamicValueAnnotation == null) { //class level annotation
annotationParameter = extractClassLevelAnnotationParameterValue(joinPoint);
} else {
annotationParameter = dynamicValueAnnotation.myAnnotationParameter();
}
System.out.println(" " + annotationParameter);//get the annotation parameter value
return joinPoint.proceed();
}
private String extractClassLevelAnnotationParameterValue(ProceedingJoinPoint joinPoint) {
Annotation[] classAnnotations = joinPoint.getTarget().getClass().getAnnotations();
for (Annotation annotation : classAnnotations) {
if (annotation.annotationType() == DynamicValue.class) {
return ((DynamicValue) annotation).myAnnotationParameter();
}
}
throw new RuntimeException("No DynamicValue value annotation was found");
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface DynamicValue {
String myAnnotationParameter();
}
Let's know if you got a cleaner solution!

The problem with your workaround appears when you annotate both a class and a method with the annotation, resulting in triggering both of them.
To prevent it declare the class level advice as:
#Around("!#annotation(LogExecutionTime) && #within(logExecutionTime)")

Related

How to write class for processing an annotation for fields?

I've created the annotation I want to put on some fields of a class.
I want the annotation to check one of two or more fields:
#Documented
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface OneOfFields {
}
For example - the class:
public class MyClassRq {
#OneOfFields
private String stringOne;
#OneOfFields
private String stringTwo;
}
If I create an HttpRequest with the body and set both fields, I want to get an exception, javax.validation exception is also possible.
What is the best way to write the validator?
Annotations can be processed in two phases:
At compile time (in this case through an Annotation Processor)
At runtime (in this case through reflection)
It depends on when you want to perform the check. Considering that it seems you want to check this at runtime (i.e. when you receive the object), then you could create a sample method that takes an object, scans all the fields of the object for the annotation #OneOfFields and if more than one is not null, then it throws an exception:
public static <T> T validate(T input) {
try {
int numberOfAnnotatedNonNull = 0;
for (Field field : input.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(OneOfFields.class) && (field.get(input) != null)) {
numberOfAnnotatedNonNull++;
if (numberOfAnnotatedNonNull > 1) {
throw new IllegalStateException("More than one field annotated by #OneOfFields has been set for this object");
}
}
}
return input;
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not validate input object", e);
}
}
Sample usage:
MyClassRq myClassRq = validate(yourInput);
If the above yourInput of type MyClassRq is valid, then it will simply return the same object. Else, it will throw an exception.
Some notes:
Here I'm throwing as soon as I find more than one field which is non null. You may of course create a cleaner error message (for example by collecting all the fields which are illegally set and returning their names)
Here I'm throwing a standard IllegalStateException but you can (you should probably) create your own custom exception
Don't forget to check that T input is not null (if it is, my code will crash).
This is a sample usage of the standard Java Reflect API, there are several ways of reaching the same purpose (I've just shown you the most "readable")

Java reflection: annotation not found even if it's present

I'm using reflection for find a method in a class and the get the annotation "PermessiNecessari" that describe the type of the operation (CREATE, DELETE,...) that a method implements.
This is a piece of authorization check implementing a picketlink class PathAuthorizer.
I get a called url, i split it and from the url i find the class that implements the web service. Then I search for the method that it will be called and read what type of operation it use.
This is a piece of the searching-method :
Class facadeinterface = Class.forName(pm); // get the interface
Method metodo = getMetodoClasse(facadeinterface, metodoRest); // find method with #Path annotation
if(metodo != null){
PermessiNecessari rp = metodo.getAnnotation(PermessiNecessari.class);
if(rp != null){ // caso metodo con permessi
return checkPermessiModulo(m, rp);
}
if(metodo.isAnnotationPresent(NonProtetto.class)){
return true;
}
LOG.log(Level.WARNING, "Metodo trovato : {0}/{1}, ma non annotato!", new Object[]{metodoRest,metodo});
For istance, this is the checked class:
public interface VlpgmoduliManagerFacadeRemote
extends InterfacciaFacadeRemote<Vlpgmoduli>{
#POST
#javax.ws.rs.Path("getpermessimoduligruppo")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
#PermessiNecessari(operation = STANDARD_OP.READ)
public GridResponse<Vlpgmoduli> getPermessiModuliGruppo(MultivaluedMap<String, String> formParams,
String callback)
throws BadRequestException;
...
The method is found via #javax.ws.rs.Path annotation, but when i want to get "PermessiNecessari" annotation, this annotation is not found!
PS : in other classes this system works fine.
The method in the parent interface are not found too!
Tryed with another interface that extends the same interface and all the methods (the inherited too) are found.
This is my annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface PermessiNecessari {
Class resourceClass() default void.class;
String operation() default "";
String modulo() default "";
}
This is the method for searching the method that implement the web service:
private Method getMetodoClasse(Class facade, String metodo){
for(Method method : facade.getMethods()){
Path p = method.getAnnotation(Path.class);
if(p != null && ( p.value().equals(metodo) || p.value().equals("/"+metodo) )){
return method;
}
}
for (Class c : facade.getInterfaces()) {
for (Method method : c.getDeclaredMethods()) {
Path p = method.getAnnotation(Path.class);
if(p != null && ( p.value().equals(metodo) || p.value().equals("/"+metodo) )){
return method;
}
}
}
return null;
}
EDIT :
It's not a problem of annotation. I tryed with this check:
public boolean haAnnotazione(Method method, Class annotationType){
Annotation annotazioni[] = method.getAnnotations();
for(Annotation a : annotazioni){
if(a.annotationType().getName().equals(annotationType.getName())){
return true;
}
}
return false;
}
if I use a.annotationType().equals(annotationType) it returns false even if they are the same; if i use the name of the class, it works!
Maybe a classloader problem? The software run in Wildfly.
Solved!
It was a dependency error. There were 2 different dependencies version in the project, so the loaded annotations were really two different classes.
For the next time: check in your pom.xml for different dependencies if you have some cast exception or if two equal Class are not equal.
...really not solved, there is a classloader problem between class in EAR/lib and in WAR/lib, but this is another question
#PermessiNecessari must be of Runtime retention because compiler will remove not incluide information about that annotation into the bytecode if retention will be different.
That is why your annotation is not found upon runtime - it is not there.

How can I make IntelliJ IDEA understand my null-checking method?

I have a method where a parameter is marked with the #Nonnull annotation. The code which calls the method has to check whether the value is null. Rather than just a straight x != null check, it is calling a utility method on another class. (In the real code, the utility method also checks whether it is a blank String).
My problem is that Intellij Idea is showing an inspection warning on the Nonnull method call, saying that my variable "might be null". I know it cannot be null because of the utility method check - how can I tell the inspector that?
Since that is a bit abstract, here's a minimal example of what I mean:
package org.ethelred.ideatest;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* tests annotations
*/
public class AnnotationChecker
{
public static void main(String[] args)
{
String x = null;
if(args.length > 0)
{
x = args[0];
}
if(!isNull(x))
{
useObject(x);
}
if(x != null)
{
useObject(x);
}
}
public static boolean isNull(#CheckForNull Object o)
{
return o == null;
}
public static void useObject(#Nonnull Object o)
{
System.out.println(o);
}
}
This uses the JSR 305 annotations.
In this example, in the first call to useObject Intellij puts a warning on the x parameter saying "Argument 'x' might be null". In the second call, there is no warning.
In IDEA 13 very fancy feature was added, called Method Contracts. For example, you could have a method, that throws validation exception if it encounters null:
#Contract("null -> fail")
public static void validateNull(#Nullable final Object object) {
if (object == null) {
throw new ValidationException();
}
}
IDEA will analyze the annotation and won't show up warnings, if you call it before possible NPE:
validateNull(user);
user.setSomething("something"); // no warning
You have full documentation in IDEA's settings (just search for Method Contract). For this to work you need additional dependency on jetbrain's annotations jar:
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>13.0</version>
</dependency>
With IDEA 12 you can configure the NotNull-check methods:
http://youtrack.jetbrains.com/issue/IDEA-35808#tab=Comments
I don't believe there's any way to resolve the warning with the code written as it is. I was hoping to find that IntelliJ supports a value for #SuppressWarnings that you could use on the useObject(x) statement, but according to this source it does not. You may just have to bite the bullet and change your code to something like the following:
if (x != null && !isBlank(x)) {
useObject(x);
}
Notice that I renamed the method isNull to isBlank since it is my understanding that the actual method you're calling that does the check for null checks other conditions as well.
I've dealt with this issue by using a static method to whitelist object calls:
/** Wrapper to mask NullPointerException warnings. */
private static <T> T _(T obj) {
return obj;
}
The method name is intended to not interfere with readability. Thus,
mObject.method();
becomes
_(mObject).method();
Furthermore, if a NullPointerException does occur it will still refer to the same line number.

Disabling certain aspects during unit test runs

I have integration tests (load context) and unit tests running together. My code does aspectj compile time weaving using spring.
My problem is that my declared advises also run during some of my unit tests. This kills the notion of a unit test, which is why I would like to disable them.
Is there something I can put on the pointcut declaration, some method I can call, some spring configuration, or maven command that disables these advises for something like all *UnitTest.java?
Thanks for the help.
example:
I have the following unit test:
#RunWith(MockitoJUnitRunner.class)
public class CompanyServiceImplTest {
#Test
public void createCampaignTest() throws Exception {
when(companyDaoMock.saveCompany(any(Campaign.class))).thenReturn(77L);
Long campaignId = companyService.createCampaign(campaignMock);
assertEquals(Long.valueOf(77L), Long.valueOf(campaignId));
}
}
and following service method:
#Override
#Transactional
#EventJournal(type = EventType.CAMPAIGN_CREATE, owner = EventOwner.TERMINAL_USER)
public Long createCampaign(Campaign campaign) {
return companyDao.saveCompany(campaign);
}
aspect:
#Aspect
public class EventJournalAspect {
#Autowired
private EventJournalService eventJournalService;
#Pointcut(value="execution(public * *(..))")
public void anyPublicMethod() {}
#Pointcut("within(com.terminal.service..*)")
private void inService() {}
#AfterReturning(pointcut = "anyPublicMethod() && inService() && #annotation(eventJournal) && args(entity,..)", returning = "id")
public void process(Object id, EventJournal eventJournal, AbstractDomainEntity entity)
throws Throwable {
if (eventJournal.type() != EventType.CAMPAIGN_PAYMENT || id != null) {
saveEvent(eventJournal, EventStatus.SUCCESS, entity, (Long) id);
}
}
#AfterThrowing(pointcut = "anyPublicMethod() && inService() && #annotation(eventJournal) && args(entity,..)", throwing="ex")
public void processException(EventJournal eventJournal, AbstractDomainEntity entity, Exception ex) throws Throwable {
saveEvent(eventJournal, EventStatus.FAILURE, entity, null);
}
private void saveEvent(EventJournal eventJournal, EventStatus status, AbstractDomainEntity entity, Long persistentId) {
EventType type = eventJournal.type();
EventOwner owner = eventJournal.owner();
eventJournalService.saveEvent(type, owner, EventStatus.SUCCESS, entity, persistentId);
}
}
When test executes - eventJournalService is null. Thus I see NullPointerException
The answer is simple: You want to use an if() pointcut expression.
Update (after the question has also been updated): The originally provided link above should contain enough information, but for what it is worth, a short explanation and a simple example:
An if() pointcut is a static aspect method returning a boolean. If the return value is true, it means that any combined pointcut like myPointcut() && if() matches as long as myPointcut() matches. For a return value of false the whole combined pointcut does not match, effectively deactivating any advice connected to the pointcut.
So what can you do in a static if() pointcut?
evaluate a static boolean member of some tool class like TestMode.ACTIVE which is only true during unit or integration testing
evaluate an environment variable which is only set during testing
evaluate a Java system property which is only set during testing
and many more things
If you want to do something fancier (and trickier) and performance is not so important, you can also try to dynamically determine whether the auto-wired aspect member variable equals null or not and only activate your pointcuts if the injected object is actually present. The only problem here is how to determine a member variable from a static method. I have no idea about Spring AOP, but in plain AspectJ there is the helper class Aspects with several overloaded methods named aspectOf(..). Assuming that your aspect is instantiated as a singleton, you could do something like this:
#Pointcut("if()")
public static boolean isActive() {
return Aspects.aspectOf(PerformanceMonitorAspect.class).eventJournalService != null;
}
// ...
#AfterReturning(pointcut = "isActive() && anyPublicMethod() && inService() && #annotation(eventJournal) && args(entity,..)", returning = "id")
// ...
#AfterThrowing(pointcut = "isActive() && anyPublicMethod() && inService() && #annotation(eventJournal) && args(entity,..)", throwing="ex")
// ...
I can only guess:
The first thing is to have a separate Spring applicationContext-test.xml,
without component-scan;
In maven you can add a phase runtime excluding weaving jars for test.
Compile time weaving would inline the advice calls in the targeted methods identified by the pointcuts that you have. I personally feel that it is good to unit test with the compile time weaving in place, because at runtime your unit does include the class with the advice inlined?
The thought I have to not include advice would be to have two different compile targets, one with compile time weaving, and one without, you should be able to do this through maven profiles, a dev profile not weaving advice in and a prod profile to weave the aspects in.
You can write a method that returns if current execution was launched using JUnit framework.
The method can check stack trace with Thread.currentThread().getStackTrace() and search for MockitoJUnitRunner presence.
I tested this solution using a SpringJUnit4ClassRunner but I think could work with MockitoJUnitRunner.
Also, you can have got a static boolean field like:
private static boolean TEST_ENVIRONMENT = false;
In a class present in your project (not in your tests) and check the value in the control method instead of use stack trace.
When you run your tests, you can use #BeforeClass annotation to set TEST_ENVIRONMENT = true.
This solution only gives you a way to know if your code is running from a test or not.

Need help creating a specific pointcut that utilizes a value from a method annotation

I have the following method
#AutoHandling(slot = FunctionalArea.PRE_MAIN_MENU)
#RequestMapping(method = RequestMethod.GET)
public String navigation(ModelMap model) {
logger.debug("navigation");
...
//First time to the Main Menu and ID-Level is ID-1 or greater
if (!callSession.getCallFlowData().isMainMenuPlayed()
&& callSession.getCallFlowData().getIdLevel() >= 1) {
// Call Auto Handling
logger.info("Call AutoHandling");
autoHandlingComponent.processAutoHandling();
}
...
return forward(returnView);
}
Basically what I want to do, is have a pointcut on processAutoHandling()
But in the #After, I need to use the slot() for #AutoHandling
I tried this, but it does not get called
#Pointcut("execution(* *.processAutoHandling())")
public void processAutoHandleCall() {
logger.debug("processAutoHandleCall");
}
#Around("processAutoHandleCall() &&" +
"#annotation(autoHandling) &&" +
"target(bean) "
)
public Object processAutoHandlingCall(ProceedingJoinPoint jp,
AutoHandling autoHandling,
Object bean)
throws Throwable {
...
You can use the wormhole design pattern for this. I am illustrating using AspectJ byte-code based approach and syntax, but you should be able to get the same effect using an explicit ThreadLocal if you are using Spring's proxy-based AOP.
pointcut navigation(AutoHandling handling) : execution(* navigation(..))
&& #annotation(handling);
// Collect whatever other context you need
pointcut processAutoHandleCall() : execution(* *.processAutoHandling());
pointcut wormhole(AutoHandling handling) : processAutoHandleCall()
&& cflow(navigation(handling));
after(AutoHandling handling) : wormhole(hanlding) {
... you advice code
... access the slot using handling.slot()
}
a) It can't work, you are trying to match two different things:
#Around("processAutoHandleCall() &&" +
"#annotation(autoHandling) &&" +
"target(bean) "
)
processHandleCall() matches the inner method execution autoHandlingComponent.processAutoHandling() while #annotation(autoHandling) matches the outer method execution navigation(ModelMap model)
b) since you are obviously trying to advise a Controller, there are a few caveats:
if you use proxy-target-class=true everything should work as is, just make sure you don't have any final methods
if you don't, all your controller methods must be backed by an interface and the #RequestMapping etc annotations must be on the interface, not the implementing class as described in this section of the Spring MVC reference docs

Categories