Call a setter to a field which is annotated with AspectJ - java

I want to intercept all assignments to a field which is annotated with MyAnnotation in this case. If it works when the value is assigned with reflction is much better.
This is what I tried, but it does not run, and I think that something else might be wrong:
public privileged aspect MyAnnotationAspect {
pointcut hasAnnotation(MyAnnotation annotation) : #annotation(annotation);
pointcut methodExecution() : execution(* *(..));
Object around(MyAnnotation annotation) : set(String word) && methodExecution() && hasAnnotation(annotation) {
Object result = null;
try {
result = proceed(annotation, "new"); //Just to try I want to assign "new" instead of the variable word
} catch (Throwable ex) {
throw new RuntimeException(ex);
}
return result;
}
}
It says that are too many arguments for the method proceed. Can anyone help me? Thank you!
EDIT
Now it throws "Warning:(10, 0) ajc: advice defined in aspects.AnnotationAspect has not been applied [Xlint:adviceDidNotMatch]"
This is my aspect:
public aspect AnnotationAspect {
pointcut hasAnnotation(Annotation annotation) : #annotation(annotation);
Object around(Annotation annotation, String word) : hasAnnotation(annotation) && set(String *) && args(word) {
Object result = null;
System.out.println(thisJoinPoint);
try {
result = proceed(annotation, "intercepted");
} catch (RuntimeException ex) {
throw ex;
}
return result;
}
}
This is the annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Annotation {
}
I have a dummy in order to test it:
class DummyEntity{
#Annotation
var name: String =_
def setName(n: String): Unit ={
name = n
}
}
And this is the test where I'm testing it:
public class AnnotationAspectTest {
private DummyEntity dummyEntity;
#Before
public void setUp(){
dummyEntity = new DummyEntity();
}
#Test
public void testing(){
dummyEntity.setName("newName");
Assert.assertEquals("intercepted", dummyEntity.name());
}
}

The methodExecution() is counter-productive here because you do not want to capture method executions, but field write access. Because set(..) && execution(..) are mutually exclusive, this makes no logical sense.
Furthermore, you need to bind the assigned value to a parameter via args() in order to be able to modify it.
package de.scrum_master.aspect;
import de.scrum_master.app.MyAnnotation;
public aspect MyAnnotationAspect {
Object around(MyAnnotation annotation, String word) :
#annotation(annotation) && set(String *) && args(word)
{
System.out.println(thisJoinPoint);
Object result = null;
try {
result = proceed(annotation, "altered value");
} catch (Throwable ex) {
throw new RuntimeException(ex);
}
return result;
}
}

Related

why is my custom annotation on interface not available through reflection

I am using Spring Data to access a Mongo database in a Spring Boot application. I'm doing this by extending MongoRepository.
#CrudPublishingRepository
public interface ProfileRepository extends MongoRepository<Profile, String> {
Optional<Profile> findByUserName(String userName);
#Query("{'products.contracts.contractId': ?0}")
List<Profile> findByContractId(String contractId);
}
For some of my repositories (including the ProfileRepository above) I need to perform some actions every time a save or delete action is performed, which is why I created the #CrudPublishingRepository annotation.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Documented
public #interface CrudPublishingRepository {}
I have used Spring AOP to intercept all save and delete methods on Spring's CrudRepository, with the following advices:
#AfterReturning("execution(public * delete(..)) && this(org.springframework.data.repository.CrudRepository)")
public void onDeleteExecuted(JoinPoint pjp) {
onDelete(pjp);
}
#AfterReturning("execution(public * deleteById(..)) && this(org.springframework.data.repository.CrudRepository)")
public void onDeleteByIdExecuted(JoinPoint pjp) {
onDeleteById(pjp);
}
#AfterReturning("execution(public * deleteAll(..)) && this(org.springframework.data.repository.CrudRepository)")
public void onDeleteAllExecuted(JoinPoint pjp) {
onDeleteAll(pjp);
}
#Around(value = "execution(public * save(..)) && this(org.springframework.data.repository.CrudRepository)")
public Object onSaveExecuted(ProceedingJoinPoint pjp) throws Throwable {
return onSave(pjp);
}
the onSave method looks like this:
private Object onSave(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(getTopicName(pjp));
try {
Object result = pjp.proceed();
return result;
} catch (Throwable t) {
throw t;
}
}
And at last, the getTopicName(JoinPoint pjp) function, which is where the problem is:
private Optional<String> getTopicName(JoinPoint pjp) {
Class clazz = pjp.getTarget().getClass();
while (clazz != null) {
for (Class i : pjp.getTarget().getClass().getInterfaces()) {
if (i.getAnnotation(CrudPublishingRepository.class) != null) {
return Optional.of("found it!");
}
}
clazz = clazz.getSuperclass();
}
return Optional.empty();
}
The implementation obviously isn't finished yet, but I would expect it to return Optional.of("found it!"), but it doesn't.
When I debug, I can see that ProfileRepository is one of the interfaces, as expected, but getAnnotations() returns an empty array.
Can anyone offer me a solution or an explanation as to why this is not working?

EasyMock still goes into implementation of a method mocked with addMockedMethod

I am trying to write a unit-test in Java. Due to the structure of Java class, I have to come up with a way in which whenever a constructor of a class is called, a mocked object of that class is created. I want to mock 2 methods of that class.
CallToBeMocked mockCallToBeMocked = EasyMock.createMockBuilder(CallToBeMocked.class)
.withConstructor(ArgumentA.class, ArgumentB.class, ArgumentC.class)
.withArgs(mockArgumentA, mockArgumentB, mockArgumentC)
.addMockedMethod("isProxied", ArgumentA.class)
.addMockedMethod("remoteCall", String.class, ArgumentA.class, Object.class)
.createMock();
EasyMock.expect(mockCallToBeMocked.isProxied(mockArgumentA)).andReturn(false);
EasyMock.expect(mockCallToBeMocked.remoteCall("ip-address", mockArgumentA, null)).andThrow(new Exception()).times(3);
The problem is that although I have clearly specified that isProxied and remoteCall methods have to be mocked and I have given appropriate expectations for those methods, it still starts going into the actual implementations of these methods.
It should perfectly work. So something else is going on. Here is a working example based on your code. What's different from your actual implementation?
public class PartialTest {
public interface ArgumentA { }
public interface ArgumentB { }
public interface ArgumentC { }
public static class CallToBeMocked {
public CallToBeMocked(ArgumentA a, ArgumentB b, ArgumentC c) {
}
public boolean isProxied(ArgumentA a) {
return true;
}
public int remoteCall(String ip, ArgumentA a, Object any) throws Exception {
return 0;
}
}
#Test
public void test() throws Exception {
ArgumentA mockArgumentA = createNiceMock(ArgumentA.class);
ArgumentB mockArgumentB = createNiceMock(ArgumentB.class);
ArgumentC mockArgumentC = createNiceMock(ArgumentC.class);
CallToBeMocked mockCallToBeMocked = createMockBuilder(CallToBeMocked.class)
.withConstructor(ArgumentA.class, ArgumentB.class, ArgumentC.class)
.withArgs(mockArgumentA, mockArgumentB, mockArgumentC)
.addMockedMethod("isProxied", ArgumentA.class)
.addMockedMethod("remoteCall", String.class, ArgumentA.class, Object.class)
.createMock();
expect(mockCallToBeMocked.isProxied(mockArgumentA)).andReturn(false);
expect(mockCallToBeMocked.remoteCall("ip-address", mockArgumentA, null)).andThrow(new Exception()).times(3);
replay(mockCallToBeMocked);
assertFalse(mockCallToBeMocked.isProxied(mockArgumentA));
try {
mockCallToBeMocked.remoteCall("ip-address", mockArgumentA, null);
fail("Should throw");
} catch (Exception e) { }
try {
mockCallToBeMocked.remoteCall("ip-address", mockArgumentA, null);
fail("Should throw");
} catch (Exception e) { }
try {
mockCallToBeMocked.remoteCall("ip-address", mockArgumentA, null);
fail("Should throw");
} catch (Exception e) { }
verify(mockCallToBeMocked);
}
}
If we aren't tied to EasyMock, here's a way in which the same functionality can be achieved using Mockito.
import static org.mockito.Mockito.verify;
import org.mockito.Mockito;
....
ClassToBeMocked myMock = Mockito.mock(ClassToBeMocked.class);
Mockito
.when(myMock.isProxied(any(ArgumentA.class)))
.thenReturn(false);
Mockito
.when(myMock.remoteCall(any(String.class), any(ArgumentA.class), any(Object.class)))
.thenThrow(new Exception("monkeys"));
<USE ``myMock``>
verify(myMock, times(1)).isProxied(mockArgumentA);
verify(myMock, times(3)).remoteCall("ip-address", mockArgumentA, null);

Get custom method annotation value from junit test

I have a junit test where I'd like to use an annotation on methods to define test settings.
I have a super class of the test class where I have abstracted some processing and where I'd like to read the method annotation values.
I have seen examples of reading method annotations by looping over a class. I'm not sure this will work for what I need. How do I find which test method was called and then read those specific annotation values (TrialMethod.name)?
public class MyUTest extends Processor{
#Test
#TrialMethod(name = "methodToBeTested")
public void testMethod() throws Exception {
//assert stuff
}
}
public class Processor extends TestCase{
private TrialMethodModel trialMethodModel = new TrialMethodModel();
private void setMethodNameByAnnotation() {
Class<?> clazz = this.getClass();
Class<TrialMethod> trialMethodClass = TrialMethod.class;
for (Method method : clazz.getDeclaredMethods()){
if (method.isAnnotationPresent(trialMethodClass)){
trialMethodModel.setName(method.getAnnotation(trialMethodClass).name());
}
}
}
}
#Documented
#Target(ElementType.METHOD)
#Retention(value=RetentionPolicy.RUNTIME)
public #interface TrialMethod {
String name();
}
I learned that you can access the junit method through the junit class. Then getting the annotation value is trivial.
private void setTrialMethodByAnnotation() {
Class<?> clazz = this.getClass();
Class<TrialMethod> trialMethod = TrialMethod.class;
Method method = null;
try {
method = clazz.getMethod(this.getName(),null);
} catch (SecurityException e) {
logger.error(e.getMessage());
} catch (NoSuchMethodException e) {
logger.error(e.getMessage());
}
if(method.isAnnotationPresent(trialMethod)){
trialMethodModel.setName(method.getAnnotation(trialMethod).name());
...
}
}

Design Question : How can I refactor a validation check into another method

I have a huge method of validate that I want to refactor. Here is an example. I have provided the initial validation, and my own suggestion. Do you think it's a good way to refactor it or there are better ways to refactor this?
Thanks,
public void validate() {
MyException myException= new MyException();
try {
if (!isRule1()) {
throw MyException.invalidRule(Rule1);
}
}
catch (MyException e) {
myException.addWrappedException(e);
}
try{
f (!isRule2()) {
throw MyException.invalidRule(Rule2);
}
}
catch (MyException e) {
myException.addWrappedException(e);
}
////continue checking...
}
and here is how I want to refactor them:
public void validate() {
MyException myException= new MyException();
validateRule1(myException);
validateRule2(myException);
//continue validating....
}
private void validateRule1(myException){
try {
if (!isRule1()) {
throw MyException.invalidRule(Rule1);
}
}
catch (MyException e) {
myException.addWrappedException(e);
}
}
private void validateRule2(myException){
try {
if (!isRule2()) {
throw MyException.invalidRule(Rule2);
}
}
catch (MyException e) {
myException.addWrappedException(e);
}
}
You could use the Decorator pattern here. Create an interface that has a single method validate() and then create different classes implementing this interface containing the validation code. Then you can call validate on the object you build and this will unwind through all the other validations.
This has the added benefit of improving the modularity of the code, enabling you to unit test the validations independently. You can also easily add or remove validations from the chain as needed.
A simple example is coded below.
public interface Validator {
void validate(Object input) throws ValidationException
}
public class ValidationOne implements Validator {
protected Validator validator;
public ValidationOne(Validator validator) {
this.validator = validator;
}
public void validate(Object input) throws ValidationException {
if (validator != null)
validator.validate(input);
// do specific ValidationOne checks
if (!isValid(input)) throw new ValidationException()
}
}
public class ValidationTwo implements Validator {
protected Validator validator;
public ValidationTwo(Validator validator) {
this.validator = validator;
}
public void validate(Object input) throws ValidationException {
if (validator != null)
validator.validate(input);
// do specific ValidationTwo checks
if (!isValid(input)) throw new ValidationException()
}
}
public class Tester {
public void runValidations(Object obj) {
Validator validator = new ValidationOne(null);
validator = new ValidationTwo(validator);
// continue adding validations as needed
try {
validator.validate(obj);
} catch (ValidationException e) {
System.err.println("Validation error occurred");
}
}
}
What about trying
MyException myException= new MyException();
if (!isRule1())
myException.addWrappedException(MyException.invalidRule(Rule1));
or
MyException myException= new MyException();
myException.addWrappedException(checkRule1());
where checkRule1() returns an exception or null and addWrappedException ignores nulls.
or
MyException myException= new MyException();
checkRule1(myException);
Definitely start with Replace Method with Method Object. It's not risky and you shouldn't be afraid of it with such a huge method. Then your approach seems to be good... just be sure to rename things like validateRule1 ;)

Dynamic variables with given type

I have written a small class, which reads out annotation from methods.
Now I want to extend that class to make it more dynamic.
My class uses at the moment following code for reading out the annotation:
ExtendedCommandAnnotation e = foo.getClass()
.getAnnotation(ExtendedCommandAnnotation.class);
String startTag = e.annoPropStartTag();
That is the simple case with fixed annotation.
In the new version I haven't any fixed annotation. I will get the annotation 'ExtendedCommandAnnotation' in a variable.
So the code above will be edited to:
String className= "ExtendedCommandAnnotation";
??? e = foo.getClass().getAnnotation(Class.forName(className));
String startTag = e.annoPropStartTag();
I don't know what I shall put instead of the ???. I tried it with Annotation, but then I can't get the properties with the defined methods.
Is there any way to get this working?
My annotation "class":
#Retention( RetentionPolicy.RUNTIME )
public #interface ExtendedCommandAnnotation
{
String annoPropUseTab() default "0";
String annoPropStartTag() default "";
String annoPropEndTag() default "";
}
EDIT:
Finally I get something like that:
String[] cmdMethNames = this.getAvailableCommandNames();
Class<?> annotationClass = Class.forName(this.annotationClassName);
for( Method meth : cmdMeth )
{
HashMap<String, String> tempAnno = new HashMap<String, String>();
if (meth.isAnnotationPresent((Class<? extends Annotation>) annotationClass))
{
Annotation anno = meth.getAnnotation((Class<? extends Annotation>) annotationClass);
[...]
}
[...]
}
But the cast to (Class<? extends Annotation>) make following warning: "Type safety: Unchecked cast from Class< capture#4-of ? > to Class< ? extends Annotation >"
If you don't know the annotation in advance, you can't know that it's got an annoPropStartTag() method, can you? So you can't tell the compiler how to bind to that method...
If you want to basically find a method with that name at execution time, you'll currently need to use reflection.
You might want to consider having some sort of "base" annotation type which contains all the methods you need in the general case, and then derive all the other annotation types from that.
/* Foo.java */
#ExtendedCommandAnnotation(annoPropStartTag = "hello")
public class Foo {
}
/* ExtendedCommandAnnotation.java */
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention( RetentionPolicy.RUNTIME )
public #interface ExtendedCommandAnnotation {
String annoPropUseTab() default "0";
String annoPropStartTag() default "";
String annoPropEndTag() default "";
}
/* Main.java */
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
public class Main {
public static void main(String[] args) {
doOriginalImplementation(); // Prints "hello"
doReflectionImplementation(); // Prints "hello"
}
public static void doOriginalImplementation() {
Foo foo = new Foo();
ExtendedCommandAnnotation e = foo.getClass().getAnnotation(ExtendedCommandAnnotation.class);
String startTag = e.annoPropStartTag();
System.out.println(startTag);
}
public static void doReflectionImplementation() {
Foo foo = new Foo();
Annotation[] annotations = foo.getClass().getAnnotations();
// or the statement below, depends on what you intent to do:
// Annotation[] annotations = foo.getClass().getDeclaredAnnotations();
Class classOfExtendedCommandAnnotation = null;
Annotation annotationOnClassFoo = null;
for (Annotation a : annotations) {
Class classA = a.annotationType();
if ("ExtendedCommandAnnotation".equals(classA.getName())) {
classOfExtendedCommandAnnotation = classA;
annotationOnClassFoo = a;
break;
}
}
Method methodAnnoPropStartTag = null;
if (classOfExtendedCommandAnnotation != null) {
try {
methodAnnoPropStartTag = classOfExtendedCommandAnnotation.getMethod("annoPropStartTag");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
if (methodAnnoPropStartTag != null) {
try {
String startTag = (String) methodAnnoPropStartTag.invoke(annotationOnClassFoo);
System.out.println(startTag);
} catch (ClassCastException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}
In my solution, the class ExtendedCommandAnnotation need not to be present at compile time. However, the class Foo must be present. The solution could be modified a little bit so that the class Foo need not to be present too.

Categories