I'm looking to see whether or not the following is even possible, as all preliminary searches haven't turned back anything to indicate either way.
I'd like to use Hibernate's Validator annotations to validate bean methods, and I would like to use some AOP framework (Spring, AOP Alliance, AspectJ, etc.) to intercept methods annotated with a subset of the Hibernate Validator annotations (#NotNull, #NotEmpty, #Email, etc.); I then want AOP advice to run when they are encountered.
Is this possible to do? If so, I am having a tough time visualizing how the code would work. Using Spring AOP's MethodInterceptor interface as an example:
First, the bean using Hibernate Validator:
public class SomeBean
{
private String data;
// Hibernate Validator annotation specifying that "data" cannot be an empty
// string.
#NotEmpty
public String getData() { ... } // etc.
}
Then, some code using that bean:
public void someMethod()
{
SomeBean oBean = new SomeBean();
// Validation should fail because we specified that "data" cannot be empty.
oBean.setData("");
}
Next, the AOP advice to be ran when Hibernate Validator-annotated methods are encountered.
public class ValidationInterceptor implements MethodInterceptor
{
public Object invoke(MethodInvocation invocation)
{
// Here's where we would use Hibernate's validator classes.
// My code example here is wrong, but it gets the point across.
Class targetClass = invocation.getClass(); // Should give me SomeBean.class
ClassValidator<targetClass> oValidator= new ClassValidator<targetClass>();
// Here I need to get a reference to the instance of the offending
// SomeBean object whose data has been set to empty...not sure how!
SomeBean oOffendingBean = getTheBadBeanSomehow();
InvalidValue[] badVals = oValidator.getInvalidValues(oOffendingBean);
}
}
So, not only am I choking on what the Spring AOP (pointcut definitions, etc.) configuration would look like to intercept the Hibernate Validator annotations I want, and not only do I not fully grasp how to implement the actual advice (e.g. how to instantiate the offending SomeBean from inside the advice as I mention above in the comments), but I'm not even sure if this solution is possible, Spring or otherwise.
Thanks in advance for some gentle "nudges" in the right direction!
You might be interested in the method validation feature introduced with Hibernate Validator 4.2 which provides support for validating method parameters and return values.
You then might use Seam Validation which integrates this functionality with CDI. If you want to use method validation together with Spring you could have a look this project on GitHub which shows how to integrate the method validation functionality with Spring AOP (disclaimer: I'm the author of this project as well as of Seam Validation).
To make your example working you would have to annote the parameter of the setter method with #NotEmpty like this:
public class SomeBean {
private String data;
#NotEmpty
public String getData() { return data; }
public void setData(#NotEmpty String data) { this.data = data; }
}
Related
Question: Can Spring point-cut expressions run on non-managed spring components such as domain object? From my experiments looks like it doesnt, so what is the best way to run pointcut expressions on a regular object?
I created custom annotation name #Encrypt, so that when it is used on top of a field in a domain object, the field is sent to a web service and is automatically encrypted.
I first started with method level annotation, and found that point cut expression doesn't work on Objects not managed by Spring, it has to be a spring bean.
1. Spring Aspect: Checks for custom annotation #Encrypt and prints out.
#Aspect
public class EncryptAspect {
#Around("#annotation(encrypt)")
public Object logAction(ProceedingJoinPoint pjp, Encrypt encrypt)
throws Throwable {
System.out.println("Only encrypt annotation is running!");
return pjp.proceed();
}
}
2. Custom Annotation:
#Documented
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Encrypt
{
// Handled by EncryptFieldAspect
}
3. Domain object using Annotation
public interface CustomerBo {
void addCustomerAround(String name);
}
public class CustomerBoImpl implements CustomerBo {
#Encrypt
public void addCustomerAround(String name){
System.out.println("addCustomerAround() is running, args : " + name);
}
}
4. Invocation
ApplicationContext appContext = new ClassPathXmlApplicationContext("http-outbound-config.xml");
// CustomerBoImpl customer = new CustomerBoImpl(); --> Aspect is not fired if object is created like this.
CustomerBo customer = (CustomerBo) appContext.getBean("customerBo"); // Aspect Works
customer.addCustomerAround("test");
To your first question ("Can Spring point-cut expressions run on non-managed spring components such as domain object?") the answer is no. Spring reference manual has a chapter that exactly explains how Spring AOP works and why it won't work in that case.
The options I see are (in order of how I would most likely approach this issue):
Droping the aspect and encapsulating this invariant in a service or a factory that creates CustomerBo's. It would be best, if CustomerBoImpl was immutable, so that you would not have to worry that it will be decrypted and left in that incorrect state.
If you are using Java Persistence API (JPA) to persist your domain objects, and if it is ok for the encryption to run just before saving them in the database, you may want to use listeners.(Note: the link leads to the documentation of Hibernate, which is one of the implementations of JPA)
The nuclear option - actually switching to using AspectJ which can introduce advice to constructors, field value changes etc.
I'm trying to validate my JPA Entity with #Valid like this:
public static void persist(#Valid Object o)
It worked fine for a while but now it stopped working and I'm not sure why. I tried to do it manually inside the persist method, and it works as expected:
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(o);
if (!constraintViolations.isEmpty()) {
throw new ConstraintViolationException(constraintViolations);
}
What could be happening or how can I debug this?
Method Validations is only available starting in bean validation v 1.1 (e.g. hibernate validator 5.x impl) which is part of Java EE 7 only. On top of that to have it working without extra specific BV code your method must be part of a component which is integrated with bean validation (eg. CDI Beans, JAX-RS Resource). Your custom code works because you do not use method validation but rather BV constraints which are defined directly on your object.
Won't work on arbitrary services. In Jersey it will only work for resource methods. So validate the incoming DTO in your resource method.
#POST
public Response post(#Valid SomeDTO dto) {}
See more at Bean Validation Support
UPDATE
So to answer the OP's comment about how we can make it work on arbitrary services, I created a small project that you can plug and play into your application.
You can find it on GitHub (jersey-hk2-validate).
Please look at the tests in the project. You will find a complete JPA example in there also.
Usage
Clone, build, and add it your Maven project
public interface ServiceContract {
void save(Model model);
}
public class ServiceContractImpl implements ServiceContract, Validatable {
#Override
public void save(#Valid Model model) {}
}
Then use the ValidationFeature to bind the service
ValidationFeature feature = new ValidationFeature.Builder()
.addSingletonClass(ServiceContractImpl.class, ServiceContract.class).build();
ResourceConfig config = new ResourceConfig();
config.register(feature);
The key point is to make your service implementation implement Validatable.
The details of the implementation are in the README. But the gist of it is that it makes use of HK2 AOP. So your services will need to be managed by HK2 for it to work. That is what the ValidationFeature does for you.
I have a custom annotation:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface FeatureSwitch {
String featureName();
}
I intercept this with the below aspect and use it to check if a feature is on or off. If the feature is off, then I throw an exception.
Aspect:
#Aspect
public class FeatureSwitchAspect {
private final FeatureSwitchConfigurationApi featureSwitchConfigurationApi;
public FeatureSwitchAspect(final FeatureSwitchConfigurationApi featureSwitchConfigurationApi) {
this.featureSwitchConfigurationApi = featureSwitchConfigurationApi;
}
#Before("#annotation(featureSwitch)")
public void checkFeatureSwitch(final FeatureSwitch featureSwitch) {
final String featureName = featureSwitch.featureName();
Boolean featSwitch = featureSwitchConfigurationApi.isFeatureActive(featureName);
if (!featSwitch) {
throw new FeatureSwitchOffException();
}
}
}
The problem I am having is that the behaviour seems inconsistent. This seems to do as expected when I call a method from a different class, but if I make a call to an annotated private method, no interception occurs. Have I got it configured incorrectly? Any suggestions would be appreciated.
Method calls from within classes will not work with proxy-based AOP.
Since you are using the keyword this (which is a pointer to your original object and not the proxy objects that is wrapping it), you will be calling the wrapped method directly - thus bypassing the code added as a result of your AOP.
You have tagged your question by java and aop, not by spring or spring-aop. So I assume that you are not limited to proxy-based Spring AOP but can use a full-fledged AOP solution like AspectJ (possible even within Spring or application servers). If so, there is a solution:
Use a privileged aspect. Caveat: This is supported in native AspectJ syntax, but not in #AspectJ syntax.
I have been writing an aspect that manipulates some of my JPA entities getters. It is supposed to re-format the returned text based on the clients locale. Because not all of my getters should be reformatted I introduced an annotation #ReFormat.
The problem is my aspect is never intercepted when I advise it to an JPA entity but it works fine on non JPA entities (it works when I create my own entity object via a copy constructor).
My annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface ReFormat {
}
My aspect:
#Aspect
public class ReFormatAspect {
#AfterReturning(pointcut = "#annotation(com.path.ReFormat)", returning = "response")
public Object formatter(JoinPoint joinPoint, Object response) {
return response;
}
}
Now this aspect is intercepted successfully within my MVC controllers (or at any other place except spring data) but not for my entities.
#Entity
#Table(name = "place", schema = "db")
public class TestEntity {
#Id
#Column(name = "id")
protected long id;
#Column(name = "about", columnDefinition = "TEXT DEFAULT NULL")
protected String about;
#ReFormat
public String getAbout() {
return this.about;
}
}
I expected a point cut once the getAbout method is called, but it does not work.
Given the facts above I suppose that JPA (Hibernate) is overriding any interceptor may be by CGLib or javassist.
Note: I have this inside my context
<context:annotation-config />
<context:spring-configured />
<aop:aspectj-autoproxy proxy-target-class="true" />
So what is the exact issue, and how do I intercept any method inside an entity?
I understand this should be the view layer work, but still I need to know why :D
Your Entities are not managed by Spring, they are managed by the underlying JPA implementation. Because of this, Spring cannot wrap them in proxies and provide the wanted behavior.
There's no Spring way to solve this. Hibernate might have some interceptor tool for that (that wraps entities as they are created) but I don't know it. Maybe extending EmptyInterceptor.
You can always use AspectJ and wave your aspect in compile time. But this way you will not have an access to spring DI container.
It is possible to use AspectJ aspects together with the Spring DI container. The Spring AOP documentation sais:
Domain objects often fall into this category because they are often created programmatically using the new operator, or by an ORM tool as a result of a database query.
I created a GitHub project where I use your #ReFormat annotation on a method of a bean annotated with #Entity. In order to be able to use DI within your AspectJ aspect you need to use the aspectOf() approach. This is even possible with JavaConfig. So you don't have to use XML configuration at all.
Main References for My Question:
Writing Method Interceptors for Google Guice: http://code.google.com/p/google-guice/wiki/AOP
The JavaDoc for the MethodInterceptor interface: http://aopalliance.sourceforge.net/doc/org/aopalliance/intercept/MethodInterceptor.html
General references about Java annotations: http://docs.oracle.com/javase/tutorial/java/javaOO/annotations.html and http://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html
Now My Question:
I am writing a Java application that heavily relies on Google Guice for creating objects and handling dependency injection. I am trying to use interceptors to run pre-processing code before certain annotated methods are executed. So far, I have successfully been able to execute interceptors (using the MethodInterceptor interface) on methods that have been annotated, using Guice's instructions. However, I want to now write interceptors that will execure on Parameter Annotations.
Here is an example scenario. First, I create my own annotation. For example::
#BindingAnnotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface MyParameterAnnotation {
}
Next, I write my own interceptor for this annotation:
public class MyParameterAnnotationInterceptor implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// Do some stuff
return invocation.proceed();
}
}
Here's an example of how I intend on using #MyParameterAnnotation:
public class ExampleObject {
public String foo(#MyParameterAnnotation String param) {
...
}
}
Finally, I need to create a Guice Injector and use it to create an instalce of ExampleObject, or else I cannot use a method interceptor in this project. I configure the Injector so that the MyParameterAnnotationInterceptor is bound to #MyParameterAnnotation, like so:
final MethodInterceptor interceptor = new MyParameterAnnotationInterceptor();
requestStaticInjection(MyParameterAnnotationInterceptor.class);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(MyParameterAnnotation.class), interceptor);
When I follow the above steps and execute a call to ExampleObject.foo(), unfortunately the interceptor is not executed despite the parameter being marked by #MyParameterAnnotation. Note that these similar steps will work if the annotation was placed at the method level instead.
This leads me to come up with two possible conclusions: either Guice cannot support binding an interceptor to a parameter annotation, or I am doing something completely incorrect (perhaps I should use another AOP Alliance interface for the interceptor, like FieldInterceptor, but I highly doubt it because the JavaDoc for Guice's AbstractModule suggests that the bindInterceptor() method can only use a MethodInterceptor parameter).
Nonetheless, all help us much appreciated :)
The matcher is for method annotations not method parameter annotations.
There is no matcher provided by Guice for method parameter annotations--you either have to write one yourself or use some other scheme. Note that this is a bit of an odd use case--Generally you can get away with
public class ExampleObject {
#MyAnnotation
public String foo(String param) {
...
}
}
You have the right Guice interceptor config for the above example.