UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.NotBlank' validating type java.lang.String - spring

I have #Configuration class in lib.
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
#Configuration
#ConfigurationProperties(prefix = "token")
#Getter
#Setter
#Validated
public class JwtTokenSettings {
#NotBlank
private String jwtSecret;
#NotNull
private Long lifetimeMinutes;
#NotNull
private Long refreshTokenLifetimeMinutes;
}
and imported sbs-validation in my build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
... other dependencies
}
my test application.yml
token:
jwt-secret: test
lifetime-minutes: 1
refresh-token-lifetime-minutes: 1
When i running test to load full context it fails with
Caused by: javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.NotBlank' validating type 'java.lang.String'. Check configuration for 'jwtSecret'
Why its happen? Another service with that config pass context load test and validators do its work
in another app with this config:
token:
jwt-secret:
lifetime-minutes: 1
refresh-token-lifetime-minutes: 1
result is:
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'token' to security.jwt.JwtTokenSettings$$EnhancerBySpringCGLIB$$c4509846 failed:
Property: token.jwtSecret
Value:
Origin: class path resource [application.yml]:40:14
Reason: must not be blank
Action:
Update your application's configuration
and when i full jwt-secret property test pass

Related

Cannot bind Map to Object in Spring Configuration Properties from YAML file

I have the following configuration in my Spring boot's application.yml file:
project:
country-properties:
france:
capital: paris
population: 60
And I have the the properties class : CountryProperties :
#Getter
#AllArgsConstructor
#ConstructorBinding
#ConfigurationProperties(prefix="project.country-properties")
public class CountryProperties {
private Map<String, CountryData> countryProperties;
#Getter
#Setter
public static class CountryData {
private String capital;
private Integer population;
}
However my CountryProperties is always null, and it's because of a failed mapping with the CountryData object.
Any ideas what is wrong with what I wrote?
You have the annotation #ConstructorBinding. This annotation tells Spring to look for a constructor in your class that has parameters corresponding to your configuration properties, and then will bind the properties.
What you are missing is:
public CountryProperties(Map<String, CountryData> countryProperties) {
this.countryProperties = countryProperties;
}
Update:
After inspecting your code again, it looks like you aren't mapping the configuration correctly to the instance field. Please update your #ConfigurationProperties(prefix="project.country-properties") to #ConfigurationProperties(prefix="project").
Also replace the #ConstructorBinding with #Configuration.

Spring can't find implementation

Here is my folder structure:
In my IAppUserMapper I have a method to convert every AppUser entity instance to Data Transfer Object Model. Here is the code in IAppUserMapper interface:
import com.server.ecommerceapp.dto.AppUserDTO;
import com.server.ecommerceapp.model.AppUser;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
#Mapper
public interface IAppUserMapper {
IAppUserMapper appUserMapper = Mappers.getMapper(IAppUserMapper.class);
#Mapping(target = "username")
#Mapping(target = "email")
#Mapping(target = "password")
#Mapping(target = "roles", expression = "java(appUser.getRoles().stream().map(this::getRoleName).collect(Collectors.toList()))")
AppUserDTO toAppUserDTO(AppUser appUser);
default String getRoleName(Role role) {
return role.getRoleName();
}
}
And here is the MapperConfiguration class code where I configure both Product and user mappers:
import com.server.ecommerceapp.mapper.IAppUserMapper;
import com.server.ecommerceapp.mapper.IProductMapper;
import org.mapstruct.factory.Mappers;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MapperConfiguration {
#Bean
public IAppUserMapper appUserMapper() {
return Mappers.getMapper(IAppUserMapper.class);
}
#Bean
public IProductMapper productMapper() {
return Mappers.getMapper(IProductMapper.class);
}
}
The error I get:
Error creating bean with name 'appUserMapper' defined in class path
resource
[com/server/ecommerceapp/configuration/MapperConfiguration.class]:
Bean instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [com.server.ecommerceapp.mapper.IAppUserMapper]: Factory
method 'appUserMapper' threw exception; nested exception is
java.lang.RuntimeException: java.lang.ClassNotFoundException: Cannot
find implementation for com.server.ecommerceapp.mapper.IAppUserMapper
I was told I should make META-INF package in resources, with service package and the com.server.ecommerceapp.mapper.AppUserMapper txt with the content same as the name of the file, so that Spring can scan and find the package following the path:
src/main/resources/META-INF/service/com.server.ecommerceapp.mapper.AppUserMapper
but it didnt work. Any ideas how to solve this, and by the way, is it bad practise to start interface names with capital I cause Im coming from ASP?
Edit:
I added #Mapper(componentModel = "spring") to my interfaces and implemented them as DI with Autowired. I dont know if its related to that problem that I had but now I get error that it cant find collectors. Im trying to map a collection of Roles from AppUser to AppUserDTO. Here are both AppUser and AppUserDTO classes:
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Data
public class AppUser {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#Column(name = "username", nullable = false, unique = true)
private String username;
#Column(name = "email", nullable = false, unique = true)
private String email;
#Column(name = "password", nullable = false)
private String password;
#ManyToMany(fetch = EAGER)
private Collection<Role> roles;
}
And DTO:
#NoArgsConstructor
#AllArgsConstructor
#Data
public class AppUserDTO {
private String username;
private String email;
private String password;
private Collection<String> roles;
}
So you're using Spring, but you are trying to not use Spring.
You should make your mappers use Spring component model:
#Mapper(componentModel = "spring")
public interface MyMapper {
Target map(Source source);
}
Check docs for dependency injection: https://mapstruct.org/documentation/stable/reference/html/#using-dependency-injection
Or do it with shared configuration: https://mapstruct.org/documentation/stable/reference/html/#shared-configurations
After that you can just #Autowired MyMapper myMapper; as any other Spring bean. No need to create instance in interface (the "Mappers.getMapper" thing) and no need to create mappers in java configuration, bean creation will be handled by framework.
#Mapping(target = "roles", expression = "java(appUser.getRoles().stream().map(this::getRoleName).collect(Collectors.toList()))")
now I get error that it cant find collectors
You are using an expression with Collectors class. As stated in the documentation https://mapstruct.org/documentation/stable/reference/html/#expressions:
Please note that the fully qualified package name is specified because MapStruct does not take care of the import of the TimeAndFormat class (unless it’s used otherwise explicitly in the SourceTargetMapper). This can be resolved by defining imports on the #Mapper annotation.
So you either need to fully qualify java.util.stream.Collectors in your expression or set "imports" parameter in #Mapper annotation: #Mapper(imports = Collectors.class).
I would also say, you could just write a normal Java method for roles mapping and not be dealing with expressions. But that's up to your taste.
The file name of the service should be the interface and its content the implementation. You have named it by the implementation.

Spring Boot - Entity With Kotlin List<Enum> Not Booting

I have been helping my team upgrade our Maven/SpringBoot/Kotlin project from Spring-Boot 2.7.5 to Spring-Boot 3.0.0. However, an issue on startup is preventing us from progressing. This has not been an issue before Spring-Boot 3.0.0.
Upon booting the application, I receive the following stack trace:
org.springframework.context.ApplicationContextException: Unable to start web server
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaSharedEM_entityManagerFactory': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: class sun.reflect.generics.reflectiveObjects.WildcardTypeImpl cannot be cast to class java.lang.reflect.ParameterizedType (sun.reflect.generics.reflectiveObjects.WildcardTypeImpl and java.lang.reflect.ParameterizedType are in module java.base of loader 'bootstrap')
After spending a day checking configurations and isolating the problem, we were left with one entity in our application, where we were still having the issue. We then started removing fields from the entity, until the application was able to run. The field we removed was a kotlin.collections.List of type Interaction, an enum class that we had defined for the application.
In order to ensure privacy, here is an isolated slice of the application MVC that will replicate this issue:
package com.example.adminapp.adminauth.persistence
import com.fasterxml.jackson.databind.ObjectMapper
import jakarta.persistence.*
import org.springframework.data.repository.CrudRepository
import org.springframework.stereotype.Repository
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
#Entity
#Table(name = "a_test_entity")
class AdminTestEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Int? = null
var name: String? = null
#Column
#Convert(converter = StrListConverter::class)
var interactions: List<Interaction> = emptyList()
}
enum class Interaction { HI, BYE }
class StrListConverter : AttributeConverter<List<Interaction>, String?> {
override fun convertToDatabaseColumn(p0: List<Interaction>): String? = ObjectMapper().writeValueAsString(p0)
override fun convertToEntityAttribute(p0: String?): List<Interaction> =
p0?.let { listOf(*ObjectMapper().readValue(it, Array<Interaction>::class.java)) } ?: emptyList()
}
#Repository
interface AdminTestEntityRepository : CrudRepository<AdminTestEntity, Int?>
#RestController
#RequestMapping("/api/v1/admin/test")
class AdminTestController(private val adminTestEntityRepository: AdminTestEntityRepository) {
#GetMapping("all")
fun getAllTest() = adminTestEntityRepository.findAll()
}
The kicker for this whole issue is that it only seems to be List<Enum> that causes this issue. For example, the following three re-definitions do not cause an instance of this issue:
var interactions: ArrayList<Interaction> = emptyList()
var interactions: List<String> = emptyList()
var interactions: List<DataClass> = emptyList()
What could be the cause of this? Why is it only List<Enum>?
It seems that https://hibernate.atlassian.net/browse/HHH-15624 fixed this issue.

Spring does not complain if a property is not set when using ConfigurationProperties

I have a bean that is configured via ConfigurationProperties:
#Component
#ConfigurationProperties(prefix = "mybean")
public class MyBean {
#NotEmpty
private String name;
// Getters, setters, ...
}
I configure the field values via application.yml but in "two levels". In the default application.yml I just set the value to the value of another property:
myBean.name: ${theValueOf.myBean.name}
In the profile specific YML file I have:
theValueOf.myBean.name: 'The desired value'
My expectation would be that if I forget to specify the property theValueOf.myBean.name then the application should fail at startup with the message that the placeholder 'theValueOf.myBean.name' could not be resolved. Instead, the field name is assigned the value (literally) ${theValueOf.myBean.name}.
If I annotate the name field with #Value("${myBean.name}") (and do not use ConfigurationProperties), and forget to define the property theValueOf.myBean.name, then the application fails at startup -- as expected.
My question is: How can I make Spring fail at startup with the message 'Could not resolve placeholder ...' when using ConfigurationProperties?
Simply mark your properties with JSR303 annotations, inside your #ConfigurationProperties.
#Component
#ConfigurationProperties(prefix = "mybean")
public class MyBean {
#NotEmpty
private String name;
}

Embedded object inheritance in EJB3

I am developping an user/password system with EJB3.
An user have an embedded password.
And I have two kinds of passwords: user defined or not.
Therefore I have a superclass Password, and its subclass GeneratedPassword. Architecture is indeed debatable.
Here are the "signatures" :
#Entity
#NamedQueries({ //... })
#Table(name="UserAccount")
public class UserAccount implements Serializable {
#Id
#Email
private String email;
#Embedded
private Password password;
public UserAccount(String email) {
this.email = email;
this.password = new GeneratedPassword();
}
// ...
}
#Embeddable
public class Password implements Serializable {
private String encryptedPassword;
// ...
}
#Embeddable
public class GeneratedPassword extends Password {
private String tmpPassword;
// ...
}
Problem is I am having a weird exception (weird because I don't understand it...):
Caused by: javax.persistence.EntityExistsException:
Exception Description: No subclass matches this class [class entities.user.GeneratedPassword] for this Aggregate mapping with inheritance.
Mapping: org.eclipse.persistence.mappings.AggregateObjectMapping[password]
Descriptor: RelationalDescriptor(entities.user.UserAccount --> [DatabaseTable(UserAccount)])
2nd part:
Caused by: Exception [EclipseLink-126] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: No subclass matches this class [class entities.user.GeneratedPassword] for this Aggregate mapping with inheritance.
Mapping: org.eclipse.persistence.mappings.AggregateObjectMapping[password]
Descriptor: RelationalDescriptor(entities.user.UserAccount --> [DatabaseTable(UserAccount)])
So what I understand from these exceptions is that the GeneratedPassword is not recognized as an entity. But if I use the Password class, evrything works fine! So I'm back to the incomprehension state...
Anybody knows how to use embeddable entities within a hierarchy? Is that even the problem???
Specification does not tell anything about inheritance of embeddables, so looks like it is not supported. Probably because of simplicity as target.
Of course some implementations can have it. Unfortunately Hibernate is not one of those: https://hibernate.onjira.com/browse/HHH-1910
Eclipselink supports, but not via annotation or XML descriptor: http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Entities/Embeddable#Inheritance
By the way, question is tagged with hibernate, but you use EclipseLink.

Resources