Replace usage of WebServerFactoryCustomizer with TomcatWebServerFactoryCustomizer - java

tl:dr; I want to be able to have square brackets in URL parameters http://localhost:8080/controller/square?parameter=PL&parameter[wow]=wow and following three Java files are doing that correctly. My question is how I can rewrite my code to be using https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.html?
Following three Java files are working correctly, they allow me to have [] character in the URL. But I would like to rewrite the code to use TomcatWebServerFactoryCustomizer but I do not know how and from where I should get values for its constructor: TomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) .
Controller.java, file 1 of 3
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping(value = "controller", produces = MediaType.APPLICATION_JSON_VALUE)
public class Controller {
#GetMapping("/square")
public ResponseEntity<String> requiredFields(
#RequestParam(name = "parameter") final String parameter) {
return ResponseEntity.status(HttpStatus.OK).body("OK");
}
}
CustomWebServerAllowingSquareWebBrackets.java, file 2 of 3
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
class CustomWebServerAllowingSquareBracketsInParameters implements
WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
/**
* {#inheritDoc}
*/
#Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addConnectorCustomizers(connector -> {
connector.setProperty("relaxedQueryChars", "[]");
});
}
}
DemoForParametersWithSquareBracketsApplication.java, file 3 of 3
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class DemoForParametersWithSquareBracketsApplication {
/**
* Bean for {#link WebServerFactoryCustomizer}.
* #return static class with customised web server.
*/
#Bean
public CustomWebServerAllowingSquareBracketsInParameters containerCustomizer() {
return new CustomWebServerAllowingSquareBracketsInParameters();
}
public static void main(String[] args) {
SpringApplication.run(DemoForParametersWithSquareBracketsApplication.class, args);
}
}
These three Java files above, after running allow you to execute successfully following GET request http://localhost:8080/controller/square?parameter=PL&parameter[wow]=wow.
Below you will find my modified classes, currently not working, of custom web server class, using TomcatWebServerFactoryCustomizer.
Modified CustomWebServerAllowingSquareBracketsInParameters.java, file 2 of 3
package com.example.demo;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer;
import org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory;
import org.springframework.core.env.Environment;
class CustomWebServerAllowingSquareBracketsInParameters extends TomcatWebServerFactoryCustomizer {
public CustomWebServerAllowingSquareBracketsInParameters(Environment environment, ServerProperties serverProperties) {
super(environment, serverProperties);
}
/**
* {#inheritDoc}
*/
#Override
public void customize(ConfigurableTomcatWebServerFactory factory) {
factory.addConnectorCustomizers(connector -> {
connector.setProperty("relaxedQueryChars", "[]");
});
super.customize(factory);
}
}
Modified DemoForParametersWithSquareBracketsApplication.java, file 3 of 3
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class DemoForParametersWithSquareBracketsApplication {
/**
* Bean for {#link WebServerFactoryCustomizer}.
* #return static class with customised web server.
*/
#Bean
public CustomWebServerAllowingSquareBracketsInParameters containerCustomizer() {
// TODO it will NOT compile
return new CustomWebServerAllowingSquareBracketsInParameters(null, null);
}
public static void main(String[] args) {
SpringApplication.run(DemoForParametersWithSquareBracketsApplication.class, args);
}
}
My question is, from where and how I could get values Environment environment, ServerProperties serverProperties for the constructor?

Access Environment by #Autowired tag.
#Autowired
protected Environment env;

Related

Which is simpliest way to "threadify" a springboot application?

I start in java / springboot and I want to distribute an export function (initially a single class managed by SpingApplication), in several small classes performing an export chunk executed simultaneously.
/*
Here is my initial application class setted conventionally according to SpringBoot practice ...
Which works but it's too long
*/
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
#SpringBootApplication
public class Application {
public static Logger logger = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Exporter.class, args);
Exporter exporter = ctx.getBean(Exporter.class);
exporter.main(args);
}
}
/* The runnable classe in Exporter.java*/
package moteur;
import config.ClauseWhereConfig;
import config.FtpConfig;
import config.PigeExportHighcoConfig;
import config.Sql2oConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
#Configuration
#EnableAutoConfiguration
#Import({ ExportSession.class})
public class Exporter {
#Autowired
private ExportSession currentExport;
public static Logger logger = LoggerFactory.getLogger(Exporter.class);
public void main(String[] args) {
run(args);
}
public void run(String[] args) {
/*do some stuff ... */
}
/*
Then I tried this :
Each exporter instance is supposed to make a piece of "select from" original (as you know with the keyword limit <from>, <to>
*/
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.EnableAsync;
#SpringBootApplication
#EnableAsync
public class Application {
public static Logger logger = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Exporter.class, args);
Exporter exporter1 = ctx.getBean(Exporter.class);
Exporter exporter2 = ctx.getBean(Exporter.class);
exporter1.setName("exporter1");
exporter2.setName("exporter2");
exporter1.start();
exporter2.start();
}
}
/* The Exporter class now extends Thread object */
#Configuration
#EnableAutoConfiguration
#EnableAsync
#Import({ ExportSession.class, Sql2oConfig.class, ClauseWhereConfig.class, FtpConfig.class, PigeExportHighcoConfig.class})
public class Exporter extends Thread {
#Autowired
private ExportSession currentExport;
public static Logger logger = LoggerFactory.getLogger(Exporter.class);
public void main(String[] args) {
run(args);
}
public void run(String[] args) {
logger.info(getName() + "---- >: Is running" ) ;
}
This foolish attempt that of course doesn't work :
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:708)
at moteur.Application.main(Application.java:27)
In fact i'm loking for a solution for a small autonomous batch treatment
have you got this ?

Spring framework does not recognize component after being marked with qualifier annotation

I'm a Spring framework beginner and I'm struggling with the following:
It looks like Spring is not recognizing the Component once it is marked with Qualifier annotation. It seems to be a problem with the packages which component scan cannot find. I'm running the program using right click run on the main class.
I've tried many thing so far but nothing is working. But in the end I get the following error:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-06-27 13:37:10.177 ERROR 8476 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
APPLICATION FAILED TO START
Description:
Parameter 0 of method setEducationLevelServiceAcc in com.tony.practicas.controllers.PersonaController required a bean of type 'com.tony.practicas.services.EducationLevelService' that could not be found.
Action:
Consider defining a bean of type 'com.tony.practicas.services.EducationLevelService' in your configuration.
Process finished with exit code 1
Here it is the structure of the project:
Here are the code of my classes:
PersonaController
package com.tony.practicas.controllers;
import com.tony.practicas.services.EducationLevelService;
import com.tony.practicas.services.PersonaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
#Controller
public class PersonaController {
private PersonaService personaService;
private EducationLevelService educationLevelService;
private EducationLevelService educationLevelServiceAcc;
#Autowired
public void setPersonaService(PersonaService personaService) {
this.personaService = personaService;
}
#Autowired
public void setEducationLevelService(EducationLevelService educationLevelService) {
this.educationLevelService = educationLevelService;
}
#Autowired
#Qualifier("educationLevelAcc")
public void setEducationLevelServiceAcc(EducationLevelService educationLevelServiceAcc) {
this.educationLevelServiceAcc = educationLevelServiceAcc;
}
public void setPersonaName (String name){
personaService.setName(name);
System.out.println(personaService.getName() + educationLevelService.getEducationLevel());
System.out.println(educationLevelServiceAcc.getEducationLevel());
}
}
Main
package main;
import com.tony.practicas.controllers.PersonaController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#SpringBootApplication
#Configuration
#EnableAutoConfiguration
#ComponentScan("com.tony.practicas")
public class PracticasApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(PracticasApplication.class, args);
PersonaController tony = (PersonaController) context.getBean("personaController");
tony.setPersonaName("Tony");
}
}
Services
package com.tony.practicas.services;
public interface EducationLevelService {
String getEducationLevel();
}
package com.tony.practicas.services;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
#Component("educationLevelAcc")
#Profile("accountant")
#Primary
public class EducationLevelServiceAccountantImpl implements
EducationLevelService {
#Override
public String getEducationLevel() {
return " Accountant";
}
}
package com.tony.practicas.services;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
#Component("educationLevelIng")
#Profile("engineer")
#Primary
public class EducationLevelServiceEngineerImpl implements
EducationLevelService {
#Override
public String getEducationLevel() {
return " Engineer";
}
}
package com.tony.practicas.services;
public interface PersonaService {
public abstract void setName(String name);
public abstract String getName();
}
package com.tony.practicas.services;
import org.springframework.stereotype.Component;
#Component
public class PersonaServiceImpl implements PersonaService{
private String name;
#Override
public void setName(String name) {
this.name = name + " Miguel Morantes Polanco";
}
#Override
public String getName() {
return name;
}
}
Aplication Properties
spring.profiles.active=accountant
You use annotation #Profile("accountant") which means that this bean will be created only if Spring profile accountant is active.
Once you have profile specific configuration, you would need to set the active profile in an environment.
There are multiple ways of doing this
•Using -Dspring.profiles.active=prod in VM Arguments
•Use spring.profiles.active=prod in application.properties
http://www.springboottutorial.com/spring-boot-profiles

not able to load netflix zuul route values from configuration class

I was trying my hands on netflix zuul api gateway technology. I was able to route my urls using application.properties file. But I was not able to do the same with third party configuration using ZuulProperties. This is a requirement. How can I do this. I tried below code:
config class code
package com.example.springbootzuulgatwayproxy;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
#Configuration
public class AppConfig {
#Primary
#Bean(name = "zuulConfigProperties")
#RefreshScope
#ConfigurationProperties("zuul")
public ZuulProperties zuulProperties() {
ZuulProperties.ZuulRoute route = new ZuulProperties.ZuulRoute("http://localhost:8090");// ZuulRoute is static inner class of ZuulProperties
Map<String,ZuulRoute> map = new HashMap<String,ZuulRoute>();
map.put("zuul.routes.employee.url", route);
ZuulProperties props = new ZuulProperties();
props.setRoutes(map);
return props;
}
}
here I am expecting that ZuulProperties will load the zuul.route property from inside my config class.. am I going wrong? As I had said, I could have done this easily using applicatin.properties. But this the requirement where I am stuck. To be frank, I want those properties to be loaded from database. But then I came across this piece of code, it was looking promising. But I am not able to do with this.
main class
package com.example.springbootzuulgatwayproxy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import com.example.springbootzuulgatwayproxy.filters.ErrorFilter;
import com.example.springbootzuulgatwayproxy.filters.PostFilter;
import com.example.springbootzuulgatwayproxy.filters.PreFilter;
import com.example.springbootzuulgatwayproxy.filters.RouteFilter;
#ComponentScan
#SpringBootApplication
#EnableZuulProxy
public class SpringBootZuulgatwayproxyApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootZuulgatwayproxyApplication.class, args);
}
#Bean
public PreFilter preFilter() {
return new PreFilter();
}
#Bean
public PostFilter postFilter() {
return new PostFilter();
}
#Bean
public ErrorFilter errorFilter() {
return new ErrorFilter();
}
#Bean
public RouteFilter routeFilter() {
return new RouteFilter();
}
}
when I hit http://localhost:8080/employee/getEmployeeDetails/{employee_name} url, postman gives me 404 not found error.

Spring #Autowired fails when ApplicationContext is inside another context

I'm trying to put together an SDK that uses Spring internally through a context it manages of its own. I want the jar that gets built to be usable regardless of whether or not Spring is in use on the application that wants to use the SDK.
I have something that works when it is running on its own. However if I attempt to use the SDK inside another Spring context (in my case a Spring Boot based application) I get a org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type exception.
Try as I might I cannot understand how to get this working, or indeed what I am doing wrong. The classes below show what I'm doing, the org.example.testapp.MySDKTest fails with the exception while the org.example.test.MySDKTest successfully passes. Sorry there is so much code but I can't reproduce the issue with a simplified case.
SDK source
package org.example.mysdk;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.example.mysdk.MyService;
import org.example.mysdk.MyServiceConfiguration;
public final class MySDK {
private static ApplicationContext applicationContext;
public static <T extends MyService> T getService(Class<? extends MyService> clazz, MyServiceConfiguration configuration) {
T tmp = (T) getApplicationContext().getBean(clazz);
tmp.setConfiguration(configuration);
return tmp;
}
private static ApplicationContext getApplicationContext() {
if (applicationContext == null) {
applicationContext = new AnnotationConfigApplicationContext(SpringContext.class);
}
return applicationContext;
}
}
.
package org.example.mysdk;
import org.springframework.beans.factory.annotation.Autowired;
public abstract class MyService {
private MyServiceConfiguration configuration;
#Autowired
private MyAutowiredService myAutowiredService;
MyService() {
}
MyService(MyServiceConfiguration configuration) {
super();
this.configuration = configuration;
}
public MyServiceConfiguration getConfiguration() {
return configuration;
}
void setConfiguration(MyServiceConfiguration configuration) {
this.configuration = configuration;
}
String getSomething(String in) {
return "something + " + myAutowiredService.getThing(configuration.getValue()) + " and " + in;
}
}
.
package org.example.mysdk;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
#Service
#Scope("prototype")
public class MyServiceImpl1 extends MyService {
public MyServiceImpl1() {
}
public MyServiceImpl1(MyServiceConfiguration configuration) {
super(configuration);
}
public String method1() {
return this.getSomething("method1");
}
}
.
package org.example.mysdk;
public class MyServiceConfiguration {
private String value;
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
.
package org.example.mysdk;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
#Service
public class MyAutowiredService {
private String thing = "a value";
public String getThing(String in) {
return thing + " " + in;
}
#PostConstruct
void init() {
System.out.println("MyAutowiredService bean created");
}
}
.
package org.example.mysdk;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages = {
"org.example.mysdk"
})
public class SpringContext {
}
Tests
This first test fails with a org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type exception,
package org.example.testapp;
import static org.junit.Assert.*;
import org.example.mysdk.MyServiceConfiguration;
import org.example.mysdk.MyServiceImpl1;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = App.class, loader = AnnotationConfigContextLoader.class)
public class MySDKTest {
#Autowired
MyServiceImpl1 service;
#Test
public void test() {
MyServiceConfiguration conf = service.getConfiguration();
assertEquals(conf.getValue(), "this is the instance configuration");
}
}
.
package org.example.testapp;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.example.mysdk.MySDK;
import org.example.mysdk.MyServiceConfiguration;
import org.example.mysdk.MyServiceImpl1;
#Configuration
#ComponentScan(basePackages = {
"org.example.testapp"
})
public class App {
#Bean
public MyServiceImpl1 myServiceImpl1() {
MyServiceConfiguration configuration = new MyServiceConfiguration();
configuration.setValue("this is the instance configuration");
return MySDK.getService(MyServiceImpl1.class, configuration);
}
}
and this test succeeds,
package org.example.test;
import static org.junit.Assert.*;
import org.example.mysdk.MySDK;
import org.example.mysdk.MyServiceConfiguration;
import org.example.mysdk.MyServiceImpl1;
import org.junit.Test;
public class MySDKTest {
#Test
public void test() {
MyServiceConfiguration configuration = new MyServiceConfiguration();
configuration.setValue("this is the instance configuration");
MyServiceImpl1 service = MySDK.getService(MyServiceImpl1.class, configuration);
assertEquals(service.getConfiguration().getValue(), "this is the instance configuration");
}
}
If I've gone about this the completely wrong way I'm happy to hear suggestions of how this should be done differently!
You have to modify two files.
First App.java, it should scan for "org.example.mysdk" package to inject myAutowiredService in abstract class MyService, If not it has to be created in App.java. And the name of the MyServiceImpl1 bean must be different from myServiceImpl1 as it will conflict.
package org.example.testapp;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.example.mysdk.MySDK;
import org.example.mysdk.MyServiceConfiguration;
import org.example.mysdk.MyServiceImpl1;
#Configuration
#ComponentScan(basePackages = {
"org.example.testapp", "org.example.mysdk"
})
public class App {
#Bean
public MyServiceImpl1 myServiceImpl() {
MyServiceConfiguration configuration = new MyServiceConfiguration();
configuration.setValue("this is the instance configuration");
return MySDK.getService(MyServiceImpl1.class, configuration);
}
}
Then secondly in MySDKTest.java should inject myServiceImpl which was created in App.java
import static org.junit.Assert.*;
import org.example.mysdk.MyServiceConfiguration;
import org.example.mysdk.MyServiceImpl1;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = App.class, loader = AnnotationConfigContextLoader.class)
public class MySDKTest {
#Autowired
MyServiceImpl1 myServiceImpl;
#Test
public void createOxiAccountService() {
MyServiceConfiguration conf = myServiceImpl.getConfiguration();
assertEquals(conf.getValue(), "this is the instance configuration");
}
}

Spring-Data-Rest Validator

I have been trying to add spring validators to a spring-data-rest project.
I followed along and setup the "getting started" application via this link: http://spring.io/guides/gs/accessing-data-rest/
...and now I am trying to add a custom PeopleValidator by following the documents here:
http://docs.spring.io/spring-data/rest/docs/2.1.0.RELEASE/reference/html/validation-chapter.html
My custom PeopleValidator looks like
package hello;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
public class PeopleValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return true;
}
#Override
public void validate(Object target, Errors errors) {
errors.reject("DIE");
}
}
...and my Application.java class now looks like this
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
#Configuration
#EnableJpaRepositories
#Import(RepositoryRestMvcConfiguration.class)
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public PeopleValidator beforeCreatePeopleValidator() {
return new PeopleValidator();
}
}
I would expect that POSTing to the http://localhost:8080/people URL would result in an error of some kind since the PeopleValidator is rejecting everything. However, no error is thrown, and the validator is never called.
I have also tried manually setting up the validator as shown in section 5.1 of the spring-data-rest documentation.
What am I missing?
So it appears that the before/after "save" events only fire on PUT and PATCH. When POSTing, the before/after "create" events fire.
I tried it the manual way again using the configureValidatingRepositoryEventListener override and it worked. I'm not sure what I'm doing differently at work than here at home. I'll have to look tomorrow.
I sure would love to hear if others have suggestions on why it wouldn't work.
For the record, here is what the new Application.java class looks like.
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
#Configuration
#EnableJpaRepositories
#Import(RepositoryRestMvcConfiguration.class)
#EnableAutoConfiguration
public class Application extends RepositoryRestMvcConfiguration {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", new PeopleValidator());
}
}
Looks like the feature is currently not implemented (2.3.0), unluckily there are no constants for the event names otherwise the solution below would not be that fragile.
The Configuration adds all properly named Validator beans to ValidatingRepositoryEventListener using the right event.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
import org.springframework.validation.Validator;
#Configuration
public class ValidatorRegistrar implements InitializingBean {
private static final List<String> EVENTS;
static {
List<String> events = new ArrayList<String>();
events.add("beforeCreate");
events.add("afterCreate");
events.add("beforeSave");
events.add("afterSave");
events.add("beforeLinkSave");
events.add("afterLinkSave");
events.add("beforeDelete");
events.add("afterDelete");
EVENTS = Collections.unmodifiableList(events);
}
#Autowired
ListableBeanFactory beanFactory;
#Autowired
ValidatingRepositoryEventListener validatingRepositoryEventListener;
#Override
public void afterPropertiesSet() throws Exception {
Map<String, Validator> validators = beanFactory.getBeansOfType(Validator.class);
for (Map.Entry<String, Validator> entry : validators.entrySet()) {
EVENTS.stream().filter(p -> entry.getKey().startsWith(p)).findFirst()
.ifPresent(p -> validatingRepositoryEventListener.addValidator(p, entry.getValue()));
}
}
}
A bit of a stab in the dark - I've not used spring-data-rest. However, after having a read of the tutorial you're following, I think the problem is that you need a PersonValidator not a PeopleValidator. Rename everything accordingly:
PersonValidator
package hello;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
public class PersonValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return true;
}
#Override
public void validate(Object target, Errors errors) {
errors.reject("DIE");
}
}
Application
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
#Configuration
#EnableJpaRepositories
#Import(RepositoryRestMvcConfiguration.class)
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public PersonValidator beforeCreatePersonValidator() {
return new PersonValidator();
}
}
Another way of doing it is to use annotated handlers as specified here
http://docs.spring.io/spring-data/rest/docs/2.1.0.RELEASE/reference/html/events-chapter.html#d5e443
Here is an example of how to use annotated handlers:
import gr.bytecode.restapp.model.Agent;
import org.springframework.data.rest.core.annotation.HandleBeforeCreate;
import org.springframework.data.rest.core.annotation.HandleBeforeSave;
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
import org.springframework.stereotype.Component;
#Component
#RepositoryEventHandler(Agent.class)
public class AgentEventHandler {
public static final String NEW_NAME = "**modified**";
#HandleBeforeCreate
public void handleBeforeCreates(Agent agent) {
agent.setName(NEW_NAME);
}
#HandleBeforeSave
public void handleBeforeSave(Agent agent) {
agent.setName(NEW_NAME + "..update");
}
}
Example is from github edited for brevity.

Categories

Resources