How to register validator for Spring Data Rest? - spring

There are two ways to register a Validator instance in Spring Data REST: wire it by bean name or register the validator manually. For the
majority of cases, the simple bean name prefix style is sufficient.
In order to tell Spring Data REST you want a particular Validator
assigned to a particular event, prefix the bean name with the event in
question. For example, to validate instances of the Person class
before new ones are saved into the repository, you would declare an
instance of a Validator in your ApplicationContext with a bean
name of beforeCreatePersonValidator. Since the beforeCreate prefix
matches a known Spring Data REST event, that validator is wired to the
correct event.
I tried creating this bean but it doesn't register
#Component("beforeCreateOrderValidator")
public class BeforeCreateOrderValidator extends BaseValidator {
#Override
public boolean supports(Class<?> clazz) {
return Order.class.equals(clazz);
}
#Override
public void validate(Object obj, Errors errors) {
}
}
But if I do manual registration it works
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener v) {
v.addValidator("beforeCreate", new BeforeCreateOrderValidator());
}

Related

How to get request in MyBatis Interceptor

I want to measure time of sql execution which will be run by MyBatis (Spring Boot project) and bind that with other request parameters, so I can get full info about performance issues regarding specific requests. For that case I have used MyBatis Interceptor on following way:
#Intercepts({
#Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
#Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class QueryMetricsMybatisPlugin implements Interceptor {
#Override
public Object intercept(Invocation invocation) throws Throwable {
Stopwatch stopwatch = Stopwatch.createStarted();
Object result = invocation.proceed();
stopwatch.stop();
logExectionTime(stopwatch, (MappedStatement) invocation.getArgs()[0]);
return result;
}
}
Now when it come to binding with request, I want to store those metrics in request as attribute. I have tried this simple solution to get request, but that was not working since request was always null (I have read that this solution won't work in async methods, but with MyBatis Interceptor and its methods I think that's not the case):
#Autowired
private HttpServletRequest request;
So, the question is how properly get request within MyBatis interceptor?
One important note before I answer your question: it is a bad practice to access UI layer in the DAO layer. This creates dependency in the wrong direction. Outer layers of your application can access inner layers but in this case this is other way round. Instead of this you need to create a class that does not belong to any layer and will (or at least may) be used by all layers of the application. It can be named like MetricsHolder. Interceptor can store values to it, and in some other place where you planned to get metrics you can read from it (and use directly or store them into request if it is in UI layer and request is available there).
But now back to you question. Even if you create something like MetricsHolder you still will face the problem that you can't inject it into mybatis interceptor.
You can't just add a field with Autowired annotation to interceptor and expect it to be set. The reason for this is that interceptor is instantiated by mybatis and not by spring. So spring does not have chance to inject dependencies into interceptor.
One way to handle this is to delegate handling of the interception to a spring bean that will be part of the spring context and may access other beans there. The problem here is how to make that bean available in interceptor.
This can be done by storing a reference to such bean in the thread local variable. Here's example how to do that. First create a registry that will store the spring bean.
public class QueryInterceptorRegistry {
private static ThreadLocal<QueryInterceptor> queryInterceptor = new ThreadLocal<>();
public static QueryInterceptor getQueryInterceptor() {
return queryInterceptor.get();
}
public static void setQueryInterceptor(QueryInterceptor queryInterceptor) {
QueryInterceptorRegistry.queryInterceptor.set(queryInterceptor);
}
public static void clear() {
queryInterceptor.remove();
}
}
Query interceptor here is something like:
public interface QueryInterceptor {
Object interceptQuery(Invocation invocation) throws InvocationTargetException, IllegalAccessException;
}
Then you can create an interceptor that will delegate processing to spring bean:
#Intercepts({
#Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class }),
#Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}) })
public class QueryInterceptorPlugin implements Interceptor {
#Override
public Object intercept(Invocation invocation) throws Throwable {
QueryInterceptor interceptor = QueryInterceptorRegistry.getQueryInterceptor();
if (interceptor == null) {
return invocation.proceed();
} else {
return interceptor.interceptQuery(invocation);
}
}
#Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
#Override
public void setProperties(Properties properties) {
}
}
You need to create an implementation of the QueryInterceptor that does what you need and make it a spring bean (that's where you can access other spring bean including request which is a no-no as I wrote above):
#Component
public class MyInterceptorDelegate implements QueryInterceptor {
#Autowired
private SomeSpringManagedBean someBean;
#Override
public Object interceptQuery(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
// do whatever you did in the mybatis interceptor here
// but with access to spring beans
}
}
Now the only problem is to set and cleanup the delegate in the registry.
I did this via aspect that was applied to my service layer methods (but you can do it manually or in spring mvc interceptor). My aspect looks like this:
#Aspect
public class SqlSessionCacheCleanerAspect {
#Autowired MyInterceptorDelegate myInterceptorDelegate;
#Around("some pointcut that describes service methods")
public Object applyInterceptorDelegate(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
QueryInterceptorRegistry.setQueryInterceptor(myInterceptorDelegate);
try {
return proceedingJoinPoint.proceed();
} finally {
QueryInterceptorRegistry.clear();
}
}
}

Camel HeaderFilterStrategy Bean Registration in Spring

I am having difficulty with Spring-Camel getting a HeaderFilterStrategy class registered as a Bean so it can be found by the Camel Route. My attempts to annotate the HeaderFilterStrategy custom class seem futile... so how do I register this thing so it gets found at run time?
I have a camel application with a route utilizing a custom HeaderFilterStrategy
The Strategy Class looks like :
public class HeaderFilter implements HeaderFilterStrategy {
#Override
public boolean applyFilterToCamelHeaders(String s, Object o, Exchange exchange) {
return false;
}
#Override
public boolean applyFilterToExternalHeaders(String s, Object o, Exchange exchange) {
return true;
}
}
I register it with camel using a simple registry:
SimpleRegistry registry = new SimpleRegistry();
registry.put("HeaderFilter" ,new HeaderFilter());
.
.
final CamelContext ctx = new DefaultCamelContext(registry);
And I reference it in my Route in
.to("https://myhost/endpoint&headerFilterStrategy=#HeaderFilter")
And all like Ralphy on Christmas night with his trusty Red Rider BB Gun, all is right with the world.
So, now I am trying to take this pure camel app and put it under Spring. I make sure all the appropriate Camel, and Spring-Camel and Spring things are imported.. However, when I attempt to annotate my HeaderStrategy as a Bean for Spring and it fails:
#Component
public class HeaderFilter implements HeaderFilterStrategy {
#Bean
#Override
public boolean applyFilterToCamelHeaders(String s, Object o, Exchange exchange) {
return false;
}
#Override
public boolean applyFilterToExternalHeaders(String s, Object o, Exchange exchange) {
return true;
}
}
Now when I do this, the IDE basically tells me it can't autowire any of the parameters in the method calls becaue there is more than one bean of type String or Object and no beans of type Exchange found..
At Runtime, Camel does attempt to interpret the route, but throws a failure with "No Qualifying bean type of "java.lang.String" available, since this is the first parameter in the method call...
So, How do I get this thing to be able register with annotations correctly? Or manually register this bean without it attempting to autowire? All I need is the class to be registered as a BEAN so it can be found by camel at runtime... Or at least that is what I understand needs to happen... so how the heck to I do this?
I figured it out, I was not properly using the annotationsI added the following to my AppConfig class:
#Configuration
public class AppConfig{
#Bean
public HeaderFilter HeaderFilter(){
return new HeaderFilter();
}
}
I am not sure if the suggestion above will work, but this clearly does.

Run Validation before #HandleBeforeCreate

I'm having some problem with validation with Spring Rest, the #HandleBeforeCreate event handler is running before the validation. I was expecting it to run after the validation.
In my test application I have a transaction, which has two fields to store the transaction value, one for the real transaction currency and another for the final value converted to the user currency. In my handle before create I'm dealing with that conversion, but I want the request to stop in the validator if the amount is null.
I could validate the resource in the event handler (I'm ready to handle a RepositoryConstraintViolationException), but it make me think on the point of using validator. It also seams a little inefficient that on every data rest request, spring loop through all validator checking if they support the object class.
Is validation on EventHandlers preferable than Validators (for performance reasons)? How can I force validator to run before EventHandlers?
*I'm using spring-boot 1.4.2.RELEASE
Validator
public class TransactionValidator extends SpringValidator<Transaction> {
#Override
public boolean supports(Class<?> clazz) {
return Transaction.class.equals(clazz) ;
}
#Override
public void validateObject(Transaction transaction, Errors errors) {
... validations ...
}
}
public abstract class SpringValidator<T> implements Validator {
#Override
public void validate(Object target, Errors errors) {
validateObject((T) target, errors);
}
protected abstract void validateObject(T target, Errors errors);
}
Event Handler
#Component
#RequiredArgsConstructor
#RepositoryEventHandler(Transaction.class)
public class TransacationEventHandler {
private final CurrencyUnitService currencyUnitService;
#HandleBeforeCreate
public void beforeCreate(Transaction transaction) {
adjustTransactionAmount(transaction);
}
#HandleBeforeSave
public void beforeSave(Transaction transaction) {
adjustTransactionAmount(transaction);
}
}
Edit
I checked the source code and the listeners are invoked in the following order:
Which make sense actually, using a BeforeCreateHandler is the only way to fix/change something in the Entity before running the Validator. I'm 100% open to inputs.
1. Use #Validated (did not test it)
You could do this:
#HandleBeforeCreate
public void beforeCreate(#Validated Transaction transaction) {
adjustTransactionAmount(transaction);
}
2. Otherwise - you may define the Validator through Java Configuration
You may follow this answer from #MathiasDpunkt (Spring data rest validation + exception mapper: confusing):
#Configuration
public class MyValidationConfiguration extends RepositoryRestConfigurerAdapter {
#Bean
#Primary
/**
* Create a validator to use in bean validation - primary to be able to autowire without qualifier
*/
Validator validator() {
return new LocalValidatorFactoryBean();
}
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener
validatingListener) {
Validator validator = validator();
//bean validation always before save and create
validatingListener.addValidator("beforeCreate", validator);
validatingListener.addValidator("beforeSave", validator);
}
}

Spring Autowiring not working for Abstract classes

I have a project where I have an interface, an Abstract class implementing the same interface and then a set of concrete classes which implement this interface and extend the Abstract Class.
public interface Invoice
{
void process();
}
#component
public abstract class AbstractInvoice(){
#Resource
protected Writer writer;
protected validateInvoice(){
//some implementation
}
}
#Component
public Class TypeAInvoice() extends AbstractInvoice implements Invoice{
#Override
public void process(){
//... some code
writer.write();
}
}
public Interface Writer(){
public void write();
}
#Component
public class CDWriter implements Writer{
#Override
public void write() { /* implementation.....*/}
}
Spring file has a component scan for the package.
<context:annotation-config>
<context:component-scan base-package="com.xyz" />
I am using a factory to get an instance of TypeAInvoice invoice
Now calling invoice.process() gets a NPE when getting to write.write()
I am not sure what am I missing here. I tried to see the component scan and scope and could not find anything conceptually wrong.
I am using a factory to get an instance of TypeAInvoice invoice
Depending on what your Factory does, this may be the problem. If the Factory creates a new TypeAInvoice, Spring wiring doesn't apply. You have to query the Spring context for the Bean. One way (though not very pretty) is to use ContextLoader:
return ContextLoader.getCurrentWebApplicationContext().getBean(TypeAInvoice.class)
I'd say static Factories and Spring don't go to well together. Spring stands for the Inversion of Control pattern, while Factories stand for the Service Locator pattern. I'd suggest that you get rid of your factories and autowire your Spring Beans.
Everything is good, except for the fact you use a factory to get the TypeAInvoice. If you create it like TypeAInvoice typer = new TypeAInvoice() then spring knows nothing of it, the Writer is not autowired, there for you get the NullPointerException. You should get the bean from the spring application context.
In my case, inside a Spring4 Application, i had to use a classic Abstract Factory Pattern(for which i took the idea from - http://java-design-patterns.com/patterns/abstract-factory/) to create instances each and every time there was a operation to be done.So my code was to be designed like:
public abstract class EO {
#Autowired
protected SmsNotificationService smsNotificationService;
#Autowired
protected SendEmailService sendEmailService;
...
protected abstract void executeOperation(GenericMessage gMessage);
}
public final class OperationsExecutor {
public enum OperationsType {
ENROLL, CAMPAIGN
}
private OperationsExecutor() {
}
public static Object delegateOperation(OperationsType type, Object obj)
{
switch(type) {
case ENROLL:
if (obj == null) {
return new EnrollOperation();
}
return EnrollOperation.validateRequestParams(obj);
case CAMPAIGN:
if (obj == null) {
return new CampaignOperation();
}
return CampaignOperation.validateRequestParams(obj);
default:
throw new IllegalArgumentException("OperationsType not supported.");
}
}
}
#Configurable(dependencyCheck = true)
public class CampaignOperation extends EO {
#Override
public void executeOperation(GenericMessage genericMessage) {
LOGGER.info("This is CAMPAIGN Operation: " + genericMessage);
}
}
Initially to inject the dependencies in the abstract class I tried all stereotype annotations like #Component, #Service etc but even though Spring context file had ComponentScanning for the entire package, but somehow while creating instances of Subclasses like CampaignOperation, the Super Abstract class EO was having null for its properties as spring was unable to recognize and inject its dependencies.After much trial and error I used this **#Configurable(dependencyCheck = true)** annotation and finally Spring was able to inject the dependencies and I was able to use the properties in the subclass without cluttering them with too many properties.
<context:annotation-config />
<context:component-scan base-package="com.xyz" />
I also tried these other references to find a solution:
http://www.captaindebug.com/2011/06/implementing-springs-factorybean.html#.WqF5pJPwaAN
http://forum.spring.io/forum/spring-projects/container/46815-problem-with-autowired-in-abstract-class
https://github.com/cavallefano/Abstract-Factory-Pattern-Spring-Annotation
http://www.jcombat.com/spring/factory-implementation-using-servicelocatorfactorybean-in-spring
https://www.madbit.org/blog/programming/1074/1074/#sthash.XEJXdIR5.dpbs
Using abstract factory with Spring framework
Spring and Abstract class - injecting properties in abstract classes
Inject spring dependency in abstract super class
Spring autowire dependency defined in an abstract class
Spring can you autowire inside an abstract class?
Please try using **#Configurable(dependencyCheck = true)** and update this post, I might try helping you if you face any problems.
So precisely my point here is you don't need to get a bean from spring context all the time.

Using both JSR-303 and Traditional Bean Validation?

Is it possible to use both JSR-303 bean validation and traditional validation (a single validator class for the type) in Spring? If so, what configuration is required to set this up?
I have tried the instructions on the reference.
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new DualEntryValidator());
}
#RequestMapping(value="/dualEntry.htm", method = RequestMethod.POST)
public ModelAndView handlePost(#Valid DualEntryForm form, BindingResult result) {
ModelAndView modelAndView = new ModelAndView("dualEntry", getCommonModel());
if (!result.hasErrors()){
//do logic
return modelAndView;
}else {
modelAndView.addObject("dualEntryForm", form);
return modelAndView;
}
}
I can get this to use my custom Validator or the JSR-303 validation, but not both. If I have the initBinder present as in the example it uses the custom Validator. If I remove it the JSR-303 bean validation is used. How can I use both?
I've done that following the instructions here:
http://blog.jteam.nl/2009/08/04/bean-validation-integrating-jsr-303-with-spring/
See the "Enjoy both worlds" section. Shortly, you explicitly run a JSR303 validation from a Spring validator, "joining" the results of JSR303 validations based on annotations and your custom validation logic.
I realise this is quite old, but I got this to work with minimal disturbance to my code
Change binder.setValidator(new DualEntryValidator());
to
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new DualEntryValidator());
}
With setValidator() you're replacing the JSR-303 validator with your one. With addValidator(), the JSR-303 validator is called and so is yours.
You need to make sure that your validator does not overlap with your JSR-303 #NotNull, #Min, #Max, etc. annotations otherwise you'll get duplicate error messages added.
Spring provides three handle for bean validation.
1.abstract class AbstractPropertyValidationAnnotationHandler
2.abstract class AbstractMethodValidationAnnotationHandler
3.abstract class ClassValidationAnnotationHandler
In this example i am implementing custom annotation CustomAnnotationHandle
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
Class CustomAnnotationHandle extends Annotation{
public abstract String value();
}
To implement custom annotation for property validation we need to extend AbstractPropertyValidationAnnotationHandler Class.
AbstractPropertyValidationAnnotationHandler provides createValidationRule abstract method
protected abstract AbstractValidationRule createValidationRule(Annotation annotation, Class class1, String s);
So,the extended class must provide implementation of
protected abstract AbstractValidationRule createValidationRule(Annotation annotation, Class class1, String s)
public class CustomPropertyAnnotationHandler extends AbstractPropertyValidationAnnotationHandler
{
public CustomPropertyAnnotationHandler()
{
super(new Class[] {
XXX.XXX.PackageLevle.CustomAnnotationHandle // as it takes array of custom annotation ,so we can pass more than one
// overwriting abstract method
protected AbstractValidationRule createValidationRule(Annotation annotation, Class class1, String s){
CustomAnnotationHandle value = (CustomAnnotationHandle)annotation;
return TestValidationRule(value.getValue());
// as you can see it return AbstractValidationRule.So, we need a class to give our bean specific validation rule.In our case it is
//TestValidationRule
}
}
}
public class TestValidationRule extends AbstractValidationRule
{
public TestValidationRule (String valuetest)
{
super();
this.valuetest = valuetest;
}
Private String valuetest;
}
Spring provides AnnotationBeanValidationConfigurationLoader Class.This class is used for spring own annotation for bean validation.
DefaultValidationAnnotationHandlerRegistry class is used as defaultHandlerRegistry.But if we need to provide our own annotaion then we
need to extend AnnotationBeanValidationConfigurationLoader and set our specific handleregistry via method
setHandlerRegistry(new CustomPropertyAnnotationHandler());
Class DefaultValidationAnnotationHandlerRegistry is used to register spring own annotation for bean validation.It register bean by
calling registerPropertyHandler method of SimpleValidationAnnotationHandlerRegistry class.So for our custom annotation we need to
register CustomPropertyAnnotationHandler by calling registerPropertyHandler method of SimpleValidationAnnotationHandlerRegistry class
public class OurBeanSpecificValidationLoader extends AnnotationBeanValidationConfigurationLoader
{
public OurBeanSpecificValidationLoader ()
{
super();
setHandlerRegistry(new OurSpecificAnnotationHandleRegistery ());
}
}
public class OurSpecificAnnotationHandleRegistery extends DefaultValidationAnnotationHandlerRegistry
{
public OurSpecificAnnotationHandleRegistery ()
{
registerPropertyHandler(new CustomPropertyAnnotationHandler() );
}
}
so you have your custom annotation for bean valiation.E.g
#CustomAnnotationHandle(value = "test")
private Object test;

Resources