Spring boot custom constraint validator not injected with service - spring

This is my custom validator which checks some fields availability. The UserRepository is null, therefore the validator is not injected with it.
public class AvailableValidator implements ConstraintValidator<Available,String> {
#Autowired
private UserRepository userRepository;
private Available.Field type;
public void initialize(Available usernameAvailable) {
this.type = usernameAvailable.type();
}
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
if (userRepository == null) System.out.println("\n\n------USER REPOSITORY IS NULL-------\n\n");
switch (type){
case EMAIL:
return userRepository.findByEmail(s)==null;
case NUMBER:
return userRepository.findByNumber(s)==null;
case NAME:
return userRepository.findByName(s)==null;
default:
return false;
}
}
}
I've read on similar threads that I have to set up validator factory.
I've done this:
#SpringBootApplication
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
#Bean
public Validator validator(){
return new LocalValidatorFactoryBean();
}
}
But it still doesn't work. userRepository is a null. Probably I got the config wrong, first time trying java configuration.

As an alternative to disabling Hibernates validation, have you tried this:
#Primary
#Bean
public Validator validator(){
return new LocalValidatorFactoryBean();
}
This would give your validator preference as suggested here.
Mike Kowalski

you have to see why user repository is null.
please check the logs or check the paramaters the database configurations.
try adding a constructor in the class AvailableValidator with user repository as an arugument to the constructor .
or
provide a setter method for user repository and use autowire over that setter method.

Related

how can application yaml value inject at runtime in spring boot?

I want to change the value of application.yaml at loading time.
ex) application.yaml
user.name: ${name}
Here, I want to put this value by calling an external API such as a vault, rather than a program argument when the jar is executed with the name value.
First of all, I think I need to write code that implements EnvironmentPostProcessor and calls external API, but I don't know how to inject that value. can I get help?
public class EnvironmentConfig implements EnvironmentPostProcessor {
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
// API CAll
// how can inject yaml value??
}
}
I don't know which way to orient myself.
OPTION 1: doing it via EnvironmentPostProcessor:
assuming you have registered you EnvironmentPostProcessor in /resources/META-INF/spring.factories file:
org.springframework.boot.env.EnvironmentPostProcessor=package.to.environment.config.EnvironmentConfig
all you need is to add your custom PropertySource:
public class EnvironmentConfig implements EnvironmentPostProcessor {
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
environment.getPropertySources()
.addFirst(new CustomPropertySource("customPropertySource"));
}
}
public class CustomPropertySource extends PropertySource<String> {
public CustomPropertySource(String name) {
super(name);
}
#Override
public Object getProperty(String name) {
if (name.equals("name")) {
return "MY CUSTOM RUNTIME VALUE";
}
return null;
}
}
OPTION 2: doing it via PropertySourcesPlaceholderConfigurer:
A class that is responsible for resolving these palceholders is a BeanPostProcessor called PropertySourcesPlaceholderConfigurer (see here).
So you could override it and provide you custom PropertySource that would resolve your needed property like so:
#Component
public class CustomConfigurer extends PropertySourcesPlaceholderConfigurer {
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, ConfigurablePropertyResolver propertyResolver) throws BeansException {
((ConfigurableEnvironment) beanFactoryToProcess.getBean("environment"))
.getPropertySources()
.addFirst(new CustomPropertySource("customPropertySource"));
super.processProperties(beanFactoryToProcess, propertyResolver);
}
}
use ConfigurationProperties for your properties and change it via an api like this:
#Component
#ConfigurationProperties(prefix = "user")
public class AppProperties {
private String name;
//getter and setter
}
#RestController
public class AppPropertiesController {
#Autowire
AppProperties prop;
#PostMapping("/changeProp/{name}")
public void change(#PathVariable String name){
prop.setName(name);
}
}

Access Job Parameter in Custom ItemProcessor

I am implementing a custom ItemProcessor<I, O> in spring batch for processing data from a Rest api .
I want access some values from jobParameter inside my ItemProcessor class .
Any suggestion on how to do that ?
In Tasklet we can access JobParameter but not sure how to do in ItemProcessor .
MyItemProcessor.java
#Component
public class MyItemProcessor implements ItemProcessor<User, UserDetails> {
#Override
public UserDetails process(User user) throws Exception {
// access values from job parameter here
return null;
}
}
You can make your item processor step-scoped and inject job parameters in it. The following is one way of doing that:
#Component
#StepScope
public class MyItemProcessor implements ItemProcessor<User, UserDetails> {
#Value("#{jobParameters}")
private JobParameters jobParameters;
#Override
public UserDetails process(User user) throws Exception {
// access values from job parameter here
return null;
}
}
You could also inject a specific parameter if you want with something like the following:
#Component
#StepScope
public class MyItemProcessor implements ItemProcessor<User, UserDetails> {
#Value("#{jobParameters['myParameter']}")
private String myParameter;
#Override
public UserDetails process(User user) throws Exception {
// use myParameter as needed here
return null;
}
}
Since field injection is not recommended, you can inject job parameters in your item processor when you define it as a bean, something like:
// Note how nothing related to Spring is used here, and the processor can be unit tested as a regular Java class
public class MyItemProcessor implements ItemProcessor<User, UserDetails> {
private String myParameter;
public MyItemProcessor(String myParameter) {
this.myParameter = myParameter;
}
#Override
public UserDetails process(User user) throws Exception {
// use this.myParameter as needed here
return null;
}
}
Once that in place, you can declare your item processor bean as follows:
#Bean
#StepScope
public MyItemProcessor itemProcessor(#Value("#{jobParameters['myParameter']}") String myParameter) {
return new MyItemProcessor(myParameter);
}
Fore more details about scoped beans, please check the documentation here: Late Binding of Job and Step attributes.

Hibernate validation - autowired returns null

After looking around, I couldn't find any good solution to this.
My autowired didn't work as expected where it returns null. I've autowired this particular class in other classes and it works so it only doesn't work in constraintvalidator classes.
UserService class
#Service
public class UserService {
#Autowired
private UserRepository userRep;
public void addUser(User user) {
userRep.save(user);
}
public void deleteUser(long userId) {
userRep.deleteById(userId);
}
public List<User> retrieveAllUsers(){
Iterable<User>temp =userRep.findAll();
List<User>allUsers = null;
temp.forEach(allUsers::add);
return allUsers;
}
public boolean searchByEmail(String email) {
return userRep.findByEmail(email);
}
public void updateUser(User user) {
userRep.save(user);
}
}
Annotation interface class
#Target(ElementType.FIELD)
//When will the annotation be processed compilation, runtime etc
#Retention(RetentionPolicy.RUNTIME)
//Where is the logic
#Constraint(validatedBy = EmailValidator.class)
#Documented
public #interface ValidEmail {
//Error message
String message() default "Invalid email";
//Required for annotation
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Annotation logic class. The autowired here returns null
public class EmailValidator implements ConstraintValidator<ValidEmail, String> {
#Autowired
private UserService service;
//Actual place to place the logic to check if the data is valid or not
#Override
public boolean isValid(String email, ConstraintValidatorContext context) {
if (email == null) {
return false;
}
List<User> users = service.retrieveAllUsers();
if (users.size() > 0) {
return Pattern.matches("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")#(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])", email)
&& service.searchByEmail(email);
}
else {
return Pattern.matches("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")#(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])", email);
}
}
#Override
public void initialize(ValidEmail validEmail) {
validEmail.message();
}
}
Main
#SpringBootApplication
#ComponentScan(basePackages = {
"com.Alex.Mains", "com.Alex.UserPackage", "com.Alex.Flights", "com.Alex.Security"
})
#EntityScan( basePackages = {"com.Alex.UserPackage", "com.Alex.Flights"})
#EnableJpaRepositories({"com.Alex.UserPackage", "com.Alex.Flights"})
public class JpaApplication {
public static void main(String[] args) {
SpringApplication.run(JpaApplication.class, args);
}
// #Bean
// public Validator validator(final AutowireCapableBeanFactory beanFactory) {
//
// ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
// .configure()
// .constraintValidatorFactory(new SpringConstraintValidatorFactory(beanFactory))
// .buildValidatorFactory();
//
// return validatorFactory.getValidator();
// }
}
Edit: Tried #Componenet
Fixed with adding the following to application.properties. No idea why but it works
spring.jpa.properties.javax.persistence.validation.mode=none
EDIT: My Suggestion
Instead of a custom validator, use the existing #EMail and a unique constraint:
#Entity
public class User {
// ...your properties
#Email
#Column(unique = true)
private String email.
// Rest of class...
}
OLD:
So, first off:
List<User> users = service.retrieveAllUsers();
if (users.size() > 0) {
You are fetching all the Users from the database, just to check whether any users exists? This is very, very inefficient. If you are already using Spring Data, you can just do
#Query("SELECT COUNT(*) > 0 FROM Users")
boolean anyExists();
Furthermore, your Service does not get injected, because EmailValidator is a POJO (plain old java object) and not a Spring managed component. If you annotate it with #Component or #Service Spring will take care of injection.
But I would not recommend that. I'm not sure what your exact use case is, but validators are often used on Entities and as such, they get called when the entity is created or updated. You don't want to issue additional queries in those cases.
Like I said, I don't know what exactly you are trying to achieve, but you could use the existing #Email validator (you can even provide a custom regular expression with the regexp attribute).

Using Mockito to mock out Spring Boot repository delete call throws java.util.NoSuchElementException on get()

I am new to Spring Boot and Mockito and having a problem mocking out a repository call in my service test.
I have a "delete" service method call as follows that I am trying to test with Mockito by mocking out the repository calls:
public interface IEntityTypeService {
public EntityType getById(long id);
public EntityType getByName(String name);
public List<EntityType> getAll();
public void update(EntityType entityType);
public void delete(long id);
public boolean add(EntityType entityType);
}
#Service
public class EntityTypeServiceImpl implements IEntityTypeService {
#Autowired
private EntityTypeRepository entityTypeRepository;
#Override
public void delete(long id) {
entityTypeRepository.delete(getById(id));
}
#Override
public EntityType getById(long id) {
return entityTypeRepository.findById(id).get();
}
....implementation of other methods from the interface
}
My repository looks as follows:
#RepositoryRestResource
public interface EntityTypeRepository extends LookupObjectRepository<EntityType> {
}
I have not implemented any of the methods in the repository as I am letting Spring Boot wire it up for me.
My test is as follows:
#RunWith(SpringRunner.class)
public class EntityTypeServiceTest {
#TestConfiguration
static class EntityTypeServiceImplTestContextConfiguration {
#Bean
public IEntityTypeService entityTypeService() {
return new EntityTypeServiceImpl();
}
}
#Autowired
private IEntityTypeService entityTypeService;
#MockBean
private EntityTypeRepository entityTypeRepository;
#Test
public void whenDelete_thenObjectShouldBeDeleted() {
final EntityType entity = new EntityType(1L, "new OET");
Mockito.when(entityTypeRepository.findById(1L).get()).thenReturn(entity).thenReturn(null);
// when
entityTypeService.delete(entity.getID());
// then
Mockito.verify(entityTypeRepository, times(1)).delete(entity);
assertThat(entityTypeRepository.findById(1L).get()).isNull();
}
}
When I run the test, I get an error saying "java.util.NoSuchElementException: No value present"
java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at xyz.unittests.service.EntityTypeServiceTest.whenDelete_thenObjectShouldBeDeleted(OriginatingEntityTypeServiceTest.java:41)
It references the line in the test saying Mockito.when(originatingEntityTypeRepository.findById(1L).get()).thenReturn(entity).thenReturn(null);
The reason I think I have to mock that call out is because the delete method in the Service calls the getById() method in the same service, which in turn calls entityTypeRepository.findById(id).get()
It is that, that I am assuming I have to mock out on the delete. But clearly I am wrong. Any assistance would be appreciated.
Many thanks
#Test
public void whenDelete_thenObjectShouldBeDeleted() {
final EntityType entity = new EntityType(1L, "new OET");
Optional<EntityType> optionalEntityType = Optional.of(entity);
Mockito.when(entityTypeRepository.findById(1L)).thenReturn(optionalEntityType);
// when
entityTypeService.delete(entity.getID());
// then
Mockito.verify(entityTypeRepository, times(1)).delete(entity);
//I dont think you need to assert to confirm actual delete as you are testing mock registry. to assert somethink like below you need to return null by mocking the same call again and return the null but thats of no use
//assertThat(entityTypeRepository.findById(1L).get()).isNull();
}
Updated your test. Basically we first need to mock the result of findById. refer my comment above asserting the actual delete.

#Autowired not working with custom AccessDecisionVoter

I am implementing a custom AccessDecisionVoter and I have a JPA repository which I need to autowire in the custom AccessDecisionVoter implementation. #Autowire is simply not working for neither a Service or Jpa Repository inside this class.
Project Structure
Application.java
#SpringBootApplication
#ComponentScan(basePackages="com")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
DynamicAuthorizationVoter.java
#Component
public class DynamicAuthorizationVoter implements AccessDecisionVoter<FilterInvocation> {
#Autowired
private PrivilegeRepository privilegeRepo;
#Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
#Override
public boolean supports(Class clazz) {
return true;
}
#Override
public int vote(Authentication authentication, FilterInvocation object, Collection<ConfigAttribute> collection) {
String url = determineModule(object);
if (authentication == null || authentication instanceof AnonymousAuthenticationToken) {
return ACCESS_ABSTAIN;
}
return isAccessGranted(authentication, object.getRequestUrl())? ACCESS_GRANTED : ACCESS_DENIED;
}
String determineModule(FilterInvocation filterObject){
String url = filterObject.getRequestUrl();
return url;
}
boolean isAccessGranted(Authentication authObject, String url){
Set<Privilege> privileges = privilegeRepo.findByUrl(url);
String userRole;
for(GrantedAuthority authority : authObject.getAuthorities()){
userRole = authority.getAuthority();
for(Privilege priv : privileges){
if(priv.equals(userRole)){
return true;
}
}
}
return false;
}
}
PrivilegeRepository.java
public interface PrivilegeRepository extends JpaRepository<Privilege, Long> {
Set<Privilege> findByName(String name);
Set<Privilege> findByUrl(String url);
}
For #Autowire to work inside the DynamicAuthorizationVoter class, I changed the #Component to #Service, #Configuration and everything else I found here on SO but none of them works. This JPA Repository is #Autowired everywhere else.
I will appreciate all the help.
-Adil
Usually, if you don't see any error during deployment, autowired worked fine because it is required by default. See the #Autowired documentation
Anyway, try to use an #Autowired constructor instead of an #Autowired property.
private PrivilegeRepository privilegeRepo;
#Autowired
public DynamicAuthorizationVoter(PrivilegeRepository privilegeRepo){
this.privilegeRepo = privilegeRepo;
}
With that, you could add a breakpoint to this constructor and debug it to know if the autowire process works well.
Also, remember that to use the DynamicAuthorizationVoter instance you mustn't declare it as new. You must include the following code in the related class where you want to use it.
#Autowired
AccessDecisionVoter dynamicAuthorizationVoter;
Hope it helps,

Resources