I want to make a REST API call which return a boolean value as a part of the custom annotation.
Sample Code :
**#CustomAnnotation
public String myMethod(){
// my implementation
}**
Only if the boolean value from the REST call is true, the method "myMethod must get triggered and implementation should happen else throw an exception similar to #NotNull .
I was wondering if this is possible, if yes someone please help me out.
you can create a simple custom annotation without worrying about code to call rest.
For executing rest call code ->
Read about how to apply aspect oriented programming.
Basically using aop (apsect oriented programming), you can write a code such that for any method which is annotated with your custom annotation, you wish to execute some piece of code before calling your method.
To do this in spring
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#interface CustomAnnotation {
String value() default "";
}
#Pointcut(value = "#annotation(CustomAnnotation)") // full path to CustomAnnotation class
public void abc() {
}
#Around("abc()")
public Object executeSomePieceOfCode(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("this executes before calling method");
// YOUR CODE TO CALL REST API
boolean responseFromRestCall = true; // this flag is set based on response from rest call
if(responseFromRestCall) {
// this excutes your method
Object obj = joinPoint.proceed();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
CustomAnnotation myAnnotation = method.getAnnotation(CustomAnnotation.class);
String value = myAnnotation.value();
System.out.println("value : + " + value);
return obj;
} else {
// currently throwing RuntimeException. You can throw any other custom exception.
throw new RuntimeException();
}
}
Related
I have an authorizaton Aspect that checks specific conditions based on method annotation.
This specific example shows annotation to mark a method that is only accessible by customer service. But unfortunately this isn't the only condition.
We have to confirm the customerServiceId that is also passed as one of method parameters. The parameter containing customerServiceId is pretty nested, so I was wondering if it's possible to get parameter value by some kind of a "path".
So let's say we have this method:
fun updateRemoteConfig(val remoteConfig: RemoteConfig) { doSomething() }
RemoteConfig class is pretty nested, so the path to customerServiceId would be something like: remoteConfig.customerService.id
What I would like to achieve is mark the method with annotation:
#CustomerServiceAccess(customerServiceIdPath = "remoteConfig.customerService.id")
And the value would then be fetched inside Aspect method. But I have no idea how to get to the specified value by path. Is it even possible?
The unknown is where arrows are in the code. Here's rest of the aspect:
#Aspect
class AuthorizationAspect {
#Pointcut("#annotation(com.my.project.annotations.CustomerServiceAccess)")
fun customerServiceAccess() = Unit
#Before("customerServiceAccess()")
fun checkAccess(joinPoint: JoinPoint) {
val methodSignature = joinPoint.signature as MethodSignature
val method = methodSignature.method
val canAccess = mutableListOf<() -> Boolean>()
.apply {
addAll(method.getAnnotationsByType(CustomerServiceAccess::class.java).map { it.canAccess(method) })
}
.any { it() }
if (!canAccess) {
throw UnauthorizedException(message = "User cannot perform this action")
}
}
private fun CustomerServiceAccess.canAccess(val method: Method): () -> Boolean = {
->> val customerServiceIdParam = method.getParameterByPath(getCustomerServiceIdPath())
SecurityContext.isCustomerService && SecurityContext.customerServiceId == customerServiceIdParam
}
private fun CustomerServiceAccess.getCustomerServiceIdPath(): String = this.customerServiceIdPath
}
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.FUNCTION)
annotation class CustomerServiceAccess(val customerServiceIdPath: String)
I would annotate a Java method in order to run some code before the method body is executed and before the method returns to the caller. I read about code generation at compile time using Java annotation.
The signature of the method I would annotate is as in the following example.
public Output process(Input arg0) {
// ...
// body that creates output object as processing result of arg0
// ...
return output;
}
I would annotate the above method in order to log the input argument before the method body is executed and to log the output data before the method returns to the caller.
I hypothesized an annotation like the following one.
#Log(input = "arg0", inputDest = "log1.txt", outputDest = "log2.txt")
The above annotation should generate (at compile time) some code like the following.
public Output process(Input arg0) {
SampleLogger.log(arg0, "log1.txt"); // <-- generated code
// ...
// body that creates Output object as processing result of arg0
// ...
SampleLogger.log(output, "log2.txt"); // <-- generated code
return output;
}
How to achieve this?
Yes, you can. But you should not use compile code generation. As the comment above mentioned, project lombok do such genration in compile period. But it is trick. It uses so many private API which let the project complex and hard to maintain.
What you really need is to enter into the method invocation so that you can enhance the method implematation. That is what AOP do.
For your requirenemnt, there are many libraries can do it. Such as Cglib, AspectJ, Spring AOP. Here give you a simple Cglib sample
public static void main(String[] arg) {
YourClass actualInstance = new YourClass();
YourClass instanceToUse = (YourClass) Enhancer.create(YourClass.class, (MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("enter the method: " + method.getName());
Object result = method.invoke(actualInstance, args);
System.out.println("exit the method: " + method.getName());
return result;
});
instanceToUse.process();
}
public static class YourClass {
public Object process() {
System.out.println("do something");
return new Object();
}
}
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)")
I have to throw an exception if a method parameter is a particular value.
The aim is to lock all the method that work with the specific value so I thought to use Spring AOP but I am new with it.
My problem is retrieve the value of method parameter, I create this sample:
Annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface TestAOPAnnotation {
}
AOP class
#Component
#Aspect
public class TestAOP {
#Before("#annotation(TestAOPAnnotation)")
public void logAOP(){
//Make some operations with database and throw exception in a specific case
throw new RuntimeException();
}
}
The method where I use the annotation
#Override
#TestAOPAnnotation
public List<Animals> findByName(String name) throws QueryException {
try{
return animalsRepository.findByName(name);
}catch(Exception e){
throw new QueryException(e);
}
}
and where I catch the exception
#Override
#RequestMapping(value="/test/{name}", method = RequestMethod.GET)
public #ResponseBody List<Animals> findByName(#PathVariable String name){
try{
return databaseAnimalsServices.findByName(name);
}catch(QueryException e){
return null;
}catch(Exception e){
//CATCH AOP EXCEPTION
List<Animals> list = new ArrayList<Animals>();
list.add(new Animals("AOP", "exception", "test"));
return list;
}
}
How can I get the name parameter? I may use another annotation on parameter (or only this annotation) but I don't know how. Can you help me?
EDIT
To catch parameter annotation I may use:
#Before("execution(* *(#Param (*),..))")
but it works only if I know the parameters order instead I need only the annotated parameter.
Otherwise , until now, the best solution is
#Before("#annotation(TestAOPAnnotation) && args(name,..)")
public void logAOP(String name){
System.out.println(name);
throw new RuntimeException("error");
}
but the parameter must be the fist in the signature
You could use an #Around advice which has access to the invocation data.
#Around("#annotation(TestAOPAnnotation)")
public Object logAOP(ProceedingJoinPoint aPoint) throws Throwable {
// First, implement your checking logic
// aPoint.getArgs() may be inspected here ...
if (...) {
throw new RuntimeException(...);
}
// now, actually proceed with the method call
return aPoint.proceed();
}
getArgs() gives you access to the real arguments passed to the method.
You can get the calling arguments from the joinPoint:
#Around(.....)
public Object check(ProceedingJoinPoint joinPoint) {
for (Object object : joinPoint.getArgs()) {
.... add your checks here.
}
return joinPoint.proceed();
}
Note you can not easily get the name of the parameter from the joinPoint (like you can in .NET) but you could check on type and value.
See https://blog.espenberntsen.net/2010/03/20/aspectj-cheat-sheet/ for valid execution patterns.
I am using Mockito for service later unit testing. I am confused when to use doAnswer vs thenReturn.
Can anyone help me in detail? So far, I have tried it with thenReturn.
You should use thenReturn or doReturn when you know the return value at the time you mock a method call. This defined value is returned when you invoke the mocked method.
thenReturn(T value) Sets a return value to be returned when the method is called.
#Test
public void test_return() throws Exception {
Dummy dummy = mock(Dummy.class);
int returnValue = 5;
// choose your preferred way
when(dummy.stringLength("dummy")).thenReturn(returnValue);
doReturn(returnValue).when(dummy).stringLength("dummy");
}
Answer is used when you need to do additional actions when a mocked method is invoked, e.g. when you need to compute the return value based on the parameters of this method call.
Use doAnswer() when you want to stub a void method with generic Answer.
Answer specifies an action that is executed and a return value that is returned when you interact with the mock.
#Test
public void test_answer() throws Exception {
Dummy dummy = mock(Dummy.class);
Answer<Integer> answer = new Answer<Integer>() {
public Integer answer(InvocationOnMock invocation) throws Throwable {
String string = invocation.getArgumentAt(0, String.class);
return string.length() * 2;
}
};
// choose your preferred way
when(dummy.stringLength("dummy")).thenAnswer(answer);
doAnswer(answer).when(dummy).stringLength("dummy");
}
doAnswer and thenReturn do the same thing if:
You are using Mock, not Spy
The method you're stubbing is returning a value, not a void method.
Let's mock this BookService
public interface BookService {
String getAuthor();
void queryBookTitle(BookServiceCallback callback);
}
You can stub getAuthor() using doAnswer and thenReturn.
BookService service = mock(BookService.class);
when(service.getAuthor()).thenReturn("Joshua");
// or..
doAnswer(new Answer() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return "Joshua";
}
}).when(service).getAuthor();
Note that when using doAnswer, you can't pass a method on when.
// Will throw UnfinishedStubbingException
doAnswer(invocation -> "Joshua").when(service.getAuthor());
So, when would you use doAnswer instead of thenReturn? I can think of two use cases:
When you want to "stub" void method.
Using doAnswer you can do some additionals actions upon method invocation. For example, trigger a callback on queryBookTitle.
BookServiceCallback callback = new BookServiceCallback() {
#Override
public void onSuccess(String bookTitle) {
assertEquals("Effective Java", bookTitle);
}
};
doAnswer(new Answer() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
BookServiceCallback callback = (BookServiceCallback) invocation.getArguments()[0];
callback.onSuccess("Effective Java");
// return null because queryBookTitle is void
return null;
}
}).when(service).queryBookTitle(callback);
service.queryBookTitle(callback);
When you are using Spy instead of Mock
When using when-thenReturn on Spy Mockito will call real method and then stub your answer. This can cause a problem if you don't want to call real method, like in this sample:
List list = new LinkedList();
List spy = spy(list);
// Will throw java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
when(spy.get(0)).thenReturn("java");
assertEquals("java", spy.get(0));
Using doAnswer we can stub it safely.
List list = new LinkedList();
List spy = spy(list);
doAnswer(invocation -> "java").when(spy).get(0);
assertEquals("java", spy.get(0));
Actually, if you don't want to do additional actions upon method invocation, you can just use doReturn.
List list = new LinkedList();
List spy = spy(list);
doReturn("java").when(spy).get(0);
assertEquals("java", spy.get(0));
The simplest answer is:
If you need a fixed return value on method call then we should use thenReturn(…)
If you need to perform some operation or the value need to be computed at run time then we should use thenAnswer(…)