AspectJ declaremixin with class level annotation not matching - java

Good morning,
I have defined a custom annotation that I want to use to mark some classes as Auditable
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Auditable {
}
And have declared an aspect to handle all classes marked with this annotation. I am using AspectJ with Eclipse
#Aspect
public class AuditableClassAspect extends AbstractAuditableAspect {
#Autowired
private ApplicationContext applicationContext;
// Makes the bean auditable
#DeclareMixin(value="#Auditable *")
public Auditable auditableBeanFactory(Object instance) {
// Use a bean delegator as a mixin object
Auditable mixin = applicationContext.getBean(PersistentDelegate.class);
mixin.setObject(instance);
return mixin;
}
#Pointcut("get(* (#Auditable *).*) && this(object)")
public void executionGet(Object object) {}
#Pointcut("set(* (#Auditable *).*) && this(object)")
public void executionSet(Object object) {}
}
Then I have marked a class:
#Auditable
public class Report implements Serializable {
private static final long serialVersionUID = 4746372287860486647L;
private String companyName;
private Date reportingDate;
I am using AspectJ with Spring, and have defined the Aspect in applicationContext.xml
<bean class="com.xxx.aop.AuditableClassAspect" factory-method="aspectOf" />
My issue is that there is no matching happening. The Aspect doesn't "detect" the Report class as an Auditable class.
In eclipse it doesn't show any matching. At run time I have an exception when I am casting my report in an Auditable interface.
Do you have any idea what there is no match?
For information, if in my code I write
#DeclareMixin(value="com.xxx.Report")
Then I have a matching and the Aspect works.
Is there something missing with the annotation?
Thanks
Gilles

Related

Running aspect on an Annotated Class using AspectJ [duplicate]

I'm writing an aspect to log Request and Response of each API call in a controller.
I want to be able to use this annotation on a class, hence used #Target(ElementType.TYPE)
Previously I had added #Target(ElementType.Method) and I was using this annotation on methods and it was working fine.
Now I want to change it to #Target(ElementType.TYPE)
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface ReLogger {}
#Aspect
#Component
public class ReLoggerAspect {
public static final Logger log = LoggerFactory.getLogger("ReLoggerAspect");
#PostConstruct
private void postConstruct() {
log.info("ReLoggerAspect Created");
}
#Around("#annotation(ReLogger)")
private Object reqLoggingAspect(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("Request {}",jointPoint.getArgs()[0);
}
}
Using #ReLoggerAspect on a class
#RestController
#RequestMapping(value = "....", produces = { "application/json" })
#ReLogger
public class Samplecontroller {
/** Some logic here**/.....
}
It doesn't print the Request when an API SampleController is invoked
Your premise that #annotation would match type annotations is wrong, see (Spring AOP manual](https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-pointcuts-designators):
#within: Limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP).
#annotation: Limits matching to join points where the subject of the join point (the method being executed in Spring AOP) has the given annotation.
Thus, you ought to use #within(fully.qualified.AnnotationType).

Spring + AspectJ pointcut on CrudRepository and Annotation

I have #Tenantable annotation to decide for pointCut :
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Inherited
public #interface Tenantable {
}
this my aspect :
#Slf4j
#Aspect
#Configuration
public class TenancyAspect {
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Around("publicMethod() && #within(com.sam.example.aspect.aspectexample.model.Tenantable)")
public Object tenatable(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("my operations ...");
return joinPoint.proceed();
}
}
This is working without any problem for this service class :
#Tenantable
#Service
public class MyService(){
public void doSomething(){
...
}
}
my aspect is running when I call doSomething() method, It is ok but I want to implement aspect for CrudRepository interface that belongs spring data.
I have changed my Aspect to achieve this like below :
#Slf4j
#Aspect
#Configuration
public class TenancyAspect {
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Pointcut("this(org.springframework.data.repository.Repository)")
public void repositoryExec(){}
#Around("publicMethod() && repositoryExec() && #within(com.sam.example.aspect.aspectexample.model.Tenantable)")
public Object tenatable(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("my operations ...");
return joinPoint.proceed();
}
}
this is repository :
#Tenantable
#Repository
public interface MyRepository extends CrudRepository{
}
But it doesn't work when I call any method inside of MyRepository.
Is there anyway to do this?
Edit :
It works for all repositories when I apply these :
#Pointcut("execution(public * org.springframework.data.repository.Repository+.*(..))")
and exclude this :
#within(com.sam.example.aspect.aspectexample.model.Tenantable)
But I need this anotation to apply it for specific repositories.
Having taken another look, I think I know what is going on here: You are assuming that just because you made your annotation #Inherited, it will be inherited by implementing classes if you annotate an interface. But this assumption is wrong. #Inherited only works in exactly one case: when extending an annotated base class. It does not work for annotated interfaces, methods etc. This is also documented here:
Note that this meta-annotation type has no effect if the annotated type is used to annotate anything other than a class. Note also that this meta-annotation only causes annotations to be inherited from superclasses; annotations on implemented interfaces have no effect.
As soon as you annotate your implementing class, it works.
Your repositoryExec pointcut should end with + to advice all subclass of Repository
#Pointcut("this(org.springframework.data.repository.Repository+)")

Custom field uniqness validation for classes implementing common interface Hibernate + Spring

I'm trying to make a custom validation (checking if an email is already present in the database). For single class my annotation is working fine but I need to make this validation work for two objects implementing common interface. I have User interface and Visitor and Exhibitor classes which are implementing it.
Here is my annotation:
#Documented
#Constraint(validatedBy = UniqueEmailValidator.class)
#Target({ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface UniqueEmail {
String message() default "Email is already existing!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Here is Validator class
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
#Autowired
private ApplicationContext applicationContext;
private UserService userService;
#Override
public void initialize(UniqueEmail uniqueEmail) {
}
#Override
public boolean isValid(String email, ConstraintValidatorContext constraintValidatorContext) {
return !userService.isEmailPresent(email);
}
}
UserService is a common interface of VisitorService and ExhibitorService
public interface UserService {
boolean isEmailPresent(String email);
}
And it's implementation...
#Service
public class VisitorService implements UserService {
#Autowired
VisitorDao visitorDao;
#Override
public boolean isEmailPresent(String email) {
try {
return !visitorDao.findAllByEmail(email).isEmpty();
} catch (NullPointerException e) {
return false;
}
}
}
Currently I'm getting NullPointerException
java.lang.NullPointerException: null
at pl.com.sremski.testapp.validators.UniqueEmailValidator.isValid(UniqueEmailValidator.java:24) ~[classes/:na]
at pl.com.sremski.testapp.validators.UniqueEmailValidator.isValid(UniqueEmailValidator.java:10)
Any ideas what's the reason? I was trying to debug and UserService is null... but I'm trying to add a new visitor so it should use VisitorService. Please help.
I managed to solve my problem by specifing exact implementation in annotation's parameter
#UniqueEmail(service = VisitorService.class)
and then creating it's instance in
#Override
public void initialize(UniqueEmail uniqueEmail) {
}
However I had to separate all Spring-based validation from Hibernate's entity to make it work.
If you don't want to have two different annotations with different #Qualifier() marked beans, you can consider to choose that bean in runtime using application context. But injecting whole Spring context to your business logic is considered as a very bad practice:
1) Mixing infrastructure (your WHOLE infrastructure) with business processes makes your code hard to understand and decouple.
2) It is hard to unit test this, need to mock the context object instead of your services.
But after using Google Guice DI I found myself in using Provider<Service> pattern, because injecting spring into spring is OK. So you can create a class like:
#Service
class UserServiceProvider<T extends UserService> implements Provider<T> {
#Autowired private ApplicationContext context;
public UserService get(Class<T> exactServiceType) {
return (UserService) context.getBean(exactServiceType);
}
}
Maybe there is a much better "Spring way" to do this, but this code is easy to understand and maintain. Works lile a Scope.Prototype bean, but a bit more flexible.
Google Guice Provider tutorial
EDIT:
Spring has a similar interface FactoryBean<T> and it has an enhanced handling, because DI will inject not the factory, but what factory provides.
But the one problem is you can't do it with some condition.
Simple example

#Producer annotation in Java EE

I am learning Java EE CDI, dependency injection, and #Produces in particular. I am wondering why in getGreedingCard() method, it needs a #Produces annotation at all, since the two classes GreetingCardImpl and AnotherGreetingCardImpl are already imported into the space. This is just like the regular package/class dependency and a simple import solves the problem. Why does it need a dependency injection through a #producer annotation?
Thanks for explanation in advance.
public interface GreetingCard {
void sayHello();
}
public class GreetingCardImpl implements GreetingCard {
public void sayHello() {
System.out.println("Hello!!!");
}
}
public class AnotherGreetingCardImpl implements GreetingCard {
public void sayHello() {
System.out.println("Have a nice day!!!");
}
}
import com.javacodegeeks.snippets.enterprise.cdibeans.impl.AnotherGreetingCardImpl;
import com.javacodegeeks.snippets.enterprise.cdibeans.impl.GreetingCardImpl;
#SessionScoped
public class GreetingCardFactory implements Serializable {
private GreetingType greetingType;
#Produces
public GreetingCard getGreetingCard() {
switch (greetingType) {
case HELLO:
return new GreetingCardImpl();
case ANOTHER_HI:
return new AnotherGreetingCardImpl();
default:
return new GreetingCardImpl();
}
}
}
I am wondering why in getGreedingCard() method, it needs a #Produces
annotation at all, since the two classes GreetingCardImpl and
AnotherGreetingCardImpl are already imported into the space.
Well, it's not that getGreetingCard needs the #Produces annotation. The point is to enable other classes to recieve GreetingCards via Dependency Injection.
public class Foo {
#Inject // <--- will invoke #Producer method
GreetingCard foosGreetingCard
...
}
See here for more details:
A producer method is a method that acts as a source of bean instances.
The method declaration itself describes the bean and the container
invokes the method to obtain an instance of the bean when no instance
exists in the specified context.
In your case it doesn't need #Produces as you will be injecting factory bean and using its method directly to create instances, and not injecting the greetingCard beans themseleves.
#Inject
GreetingCardFactory factory;
...
GreetingCard card = factory.getGreetingCard();
If you would define it as #Produces method, and the try to inject GreetingCard, then you would get exception that I've described in the comment.
However, if you would additionally create qualifier, like this:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public #interface ProducedCard {}
and add it to the producer method:
#Produces #ProducedCard
public GreetingCard getGreetingCard() {
...
then you would be able to inject just GreetingCard beans using your producer method like this:
#Inject #ProducedCard
GreetingCard card;
since now there is no ambiguity, as there is only one place to create greeting cards marked with #ProducedCard :-)

Using #Autowired component in custom JSR-303 validator

I'm trying to implement a custom validator for my model classes that autowires a custom bean of mine (declared via #Component).
In this, I followed the Spring documentation on that topic. My AuthenticationFacade object is implemented according to this tutorial.
When running my tests, however, the autowired attribute in the Validator object is always null. Why is that?
Here are the relevant parts of my code:
My custom bean, AuthenticationFacadeImpl.java
#Component
public class AuthenticationFacadeImpl implements AuthenticationFacade {
boolean hasAnyRole(Collection<String> roles) {
// checks currently logged in user roles
}
}
My custom constraint, HasAnyRoleConstraint.java
#Constraint(validatedBy = HasAnyRoleConstraintValidator.class)
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.FIELD})
public #interface HasAnyRole {
String[] value();
String message() default "{HasAnyRole}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
My custom validator, HasAnyRoleConstraintValidator.java
#Component
public class HasAnyRoleConstraintValidator implements ConstraintValidator<HasAnyRole, Object> {
#Autowired
AuthenticationFacade authenticationFacade;
private String[] roles;
#Override
public void initialize(HasAnyRole hasAnyRole) {
this.roles = hasAnyRole.value();
}
#Override
public boolean isValid(Object target, ConstraintValidatorContext constraintValidatorContext) {
return target == null || authenticationFacade.hasAnyRole(Arrays.asList(this.roles));
}
}
The model class, Article.java
#Entity
public class Article {
// ...
#HasAnyRole({"EDITOR", "ADMIN"})
private String title;
// ...
}
The service object, ArticleServiceImpl.java
#Service
public class ArticleServiceImpl implements ArticleService {
#Autowired
private ArticleRepository articleRepository;
#Autowired
private AuthenticationFacade authenticationFacade;
#Autowired
private Validator validator;
#Override
#PreAuthorize("hasAnyRole('ADMIN', 'EDITOR')")
public boolean createArticle(Article article, Errors errors) {
articleRepository.save(article);
return true;
}
The Errors object that gets fed into the createArticle method is intended to come from the Spring controller, which gets fed a model object with the #Valid annotation.
The repository, ArticleRepository.java, uses Spring Data JPA's JpaRepository
public interface ArticleRepository extends JpaRepository<Article, Long> {
}
I solved this for now by ditching Dependency Injection for the Validator class, instead instantiating an instance of AuthenticationFacadeImpl in the constructor.
Would still be interesting, though how to combine the use of #Valid in the Controllers with custom validators + #Autowired attributes in the Model without explicitely calling the Validator in the code...
If your validator is instantiated outside the Spring context, then you can use Spring’s AOP #Configurable magic to register it in context and get autowiring work. All what you need is to annotate HasAnyRoleConstraintValidator with #Configurable and enable compile time, or load time aspects weaving.

Categories