#ConfigurationProperties referencing properties that themselves reference other properties - spring-boot

project.name=my-project
base.url=http://localhost:8080
cas.url=http://my-server:8010/cas
cas.callback.url=${base.url}/${project.name}
Basically I want to use the above in a spring-boot ConfigurationProperties but the casCallbackUrl is always null.
#Component
#ConfigurationProperties(prefix = "cas")
#Getter
#Setter
public class CasSettings {
#NotBlank
private String url; //this is resolved correctly
#NotBlank
private String callbackUrl; //callbackUrl is null
}
update
Well I got it working by camelCasing the property names, but according to the documentation you should be able to use dot notation for property names.
from:
cas.callback.url=${base.url}/${project.name}
to:
cas.callbackUrl=${base.url}/${project.name}
Why is spring-boot not picking up the dot notation?

The dot represents a separate object within the configuration properties object. cas.callback-url would work.

Spring relaxed property is not relaxed enugh to to transform dot notated properties to camel case fields. But you can implement it yourself easily:
#Service
#PropertySource("classpath:git.properties")
public class MngmntService implements EnvironmentAware {
private BuildStatus buildStatus;
private static final Logger LOG = LoggerFactory.getLogger(MngmntService.class);
#Override
public void setEnvironment(Environment env) {
RelaxedPropertyResolver pr = new RelaxedPropertyResolver(env, "git.");
buildStatus = new BuildStatus();
for (Field field : BuildStatus.class.getDeclaredFields()) {
String dotNotation = StringUtils.join(
StringUtils.splitByCharacterTypeCamelCase(field.getName()),
'.'
);
field.setAccessible(true);
try {
field.set(buildStatus, pr.getProperty(dotNotation, field.getType()));
} catch (IllegalArgumentException | IllegalAccessException ex) {
LOG.error("Error setting build property.", ex);
}
}
}
public BuildStatus getBuildStatus() {
return buildStatus;
}
Property object:
public class BuildStatus implements Serializable {
private String tags;
private String branch;
private String dirty;
private String commitId;
private String commitIdAbbrev;
private String commitTime;
private String closestTagName;
private String buildTime;
private String buildHost;
private String buildVersion;
...
}

Related

Builder Pattern and Dependecy Injection - how to fix problem

I wanted to create a class based on the builder pattern. Using the static method build. Which would return a properly built object based on initial validation checking whether a given object exists in the database.
#Component
#Data
#Builder
public class GetBookedSeatsRequest {
#Autowired
private MovieRepository movieRepository;
#Autowired
public CinemaRepository cinemaRepository;
#Autowired
public PropertiesMovieRepository propertiesMovieRepository;
private String cinemaName;
private String movieName;
private String movieRoom;
#JsonFormat(pattern="yyyy-MM-dd; HH:mm:ss",shape = JsonFormat.Shape.STRING)
private LocalDateTime localDateTime;
private List<Integer> wantedSeats;
public GetBookedSeatsRequest build(ReservationModel reservationModel) throws CinemaNotFoundException, MovieNotFoundException, PropertyMovieNotFoundException {
boolean cinemaExist = cinemaRepository.existsByCinemaName(reservationModel.getCinemaName());
if (!cinemaExist) {
throw new CinemaNotFoundException("Cinema doesn't exist");
}
boolean movieExist = movieRepository.existsByMovieName(reservationModel.getMovieName());
if (!movieExist) {
throw new MovieNotFoundException("Movie doesn't exist");
}
boolean roomExist = movieRepository.existsByMovieRoom(reservationModel.getMovieRoom());
if (!roomExist) {
throw new MovieNotFoundException("Movie Romm doesn't exist");
}
boolean existData = propertiesMovieRepository.existsByStartTimeOfTheMovie(reservationModel.getDateAndTime());
if (!existData) {
throw new PropertyMovieNotFoundException("This data doesn't exist");
}
// boolean existSeats = movieRepository.existsBySeating(reservationModel.getSeatsToBooked());
// if (!existSeats) {
// throw new MovieNotFoundException("This seats doesn't exist");
// }
GetBookedSeatsRequest correct = GetBookedSeatsRequest.builder()
.cinemaName(reservationModel.getCinemaName())
.movieName(reservationModel.getMovieName())
.movieRoom(reservationModel.getMovieRoom())
.localDateTime(reservationModel.getDateAndTime())
.wantedSeats(reservationModel.getSeatsToBooked())
.build();
return correct;
}
}
#Data
#AllArgsConstructor
public class ReservationModel {
private String cinemaName;
private String movieName;
private String movieRoom;
#JsonFormat(pattern="yyyy-MM-dd; HH:mm:ss",shape = JsonFormat.Shape.STRING)
private LocalDateTime dateAndTime;
private List<Integer> seatsToBooked;
}
But I still got some erros. What am I doing wrong, I am learing Spring Boot. Thanks for help
Description:
Parameter 3 of constructor in com.cinema.booking.aop.GetBookedSeatsRequest required a bean of type 'java.lang.String' that could not be found.
Action:
Consider defining a bean of type 'java.lang.String' in your configuration.

Best Approach to load application.yml in spring boot application

I am having Spring Boot application and having application.yml with different properties and loading as below.
#Configuration
#ConfigurationProperties(prefix="applicationprops")
public class ApplicationPropHolder {
private Map<String,String> mapProperty;
private List<String> myListProperty;
//Getters & Setters
}
My Service or Controller Class in which I get this properties like below.
#Service
public ApplicationServiceImpl {
#Autowired
private ApplicationPropHolder applicationPropHolder;
public String getExtServiceInfo(){
Map<String,String> mapProperty = applicationPropHolder.getMapProperty();
String userName = mapProperty.get("user.name");
List<String> listProp = applicationPropHolder.getMyListProperty();
}
}
My application.yml
spring:
profile: dev
applicationprops:
mapProperty:
user.name: devUser
myListProperty:
- DevTestData
---
spring:
profile: stagging
applicationprops:
mapProperty:
user.name: stageUser
myListProperty:
- StageTestData
My questions are
In my Service class i am defining a variable and assigning Propertymap for every method invocation.Is it right appoach?
Is there any other better way I can get these maps without assigning local variable.
There are three easy ways you can assign the values to instance variables in your bean class.
Use the #Value annotation as follows
#Value("${applicationprops.mapProperty.user\.name}")
private String userName;
Use the #PostConstruct annotation as follows
#PostConstruct
public void fetchPropertiesAndAssignToInstanceVariables() {
Map<String, String> mapProperties = applicationPropHolder.getMapProperty();
this.userName = mapProperties.get( "user.name" );
}
Use #Autowired on a setter as follows
#Autowired
public void setApplicationPropHolder(ApplicationPropHolder propHolder) {
this.userName = propHolder.getMapProperty().get( "user.name" );
}
There may be others, but I'd say these are the most common ways.
Hope, you are code is fine.
Just use the below
#Configuration
#ConfigurationProperties(prefix="applicationprops")
public class ApplicationPropHolder {
private Map<String,String> mapProperty;
private List<String> myListProperty;
public String getUserName(){
return mapProperty.get("user.name");
}
public String getUserName(final String key){
return mapProperty.get(key);
}
}
#Service
public ApplicationServiceImpl {
#Autowired
private ApplicationPropHolder applicationPropHolder;
public String getExtServiceInfo(){
final String userName = applicationPropHolder.getUserName();
final List<String> listProp = applicationPropHolder.getMyListProperty();
}
}

#value not able to read from application.properties in springboot

I am trying to read value from properties file using #value as follows.
#Value("${abc}")
private String abc;
public List<Record> fetchRecords(String label, String predicate) {
System.out.println(abc);
}
but value of abc is coming as null. Whereas when I try to print the same using #PostConstruct, I am getting the expected value.
#PostConstruct
public void postconstruct() {
System.out.println(abc);
}
Any lead why I am not able to get the value in fetchRecords() method?
For reference, here goes the code
#Component
public class AuditRecord {
private String subject;
private String predicate;
private String oldObject;
private String newObject;
private String readOnlyAuthInfo;
#Value("${registry.system.base}")
private String registrySystemContext;
public void record(DatabaseProvider provider) throws AuditFailedException {
System.out.println("---registrySystemContext value showing null here---"+registrySystemContext);
...
}
#PostConstruct
public void postconstruct() {
System.out.println("---registrySystemContext value showing here as expected---"+registrySystemContext);
}
}
The way I am calling is as follows:
#Component
public class RegistryDaoImpl implements RegistryDao {
...
private void addOrUpdateVertexAndEdge(Vertex v, Vertex dbVertex, GraphTraversalSource dbGraph, String methodOrigin){
...
AuditRecord record = new AuditRecord();
record
.subject(dbVertex.label())
.predicate(e.label())
.oldObject(null)
.newObject(existingV.label())
.record(databaseProvider);
}
}
P.S. registry.system.base is in application.yml.
You need to autowire AuditRecord and not use new directly. Only that way you will have your class in Spring's context.
We don't know your exact usage of the class but you might be interested in Spring's FactoryBean.

Make #ConfigurationProperties properties partially mandatory

Given the following simple (not nested) configuration properties class:
#ConfigurationProperties("env")
public class MyServiceProperties {
private String anyProperty;
private Boolean anyOther;
...
}
How can I make sure that anyProperty is mandatory, i.e. env.any-property must be set to startup the application? Is there any difference for nested configuration property classes?
You can perform all kind of validations.
#Validated
#ConfigurationProperties("env")
public class MyServiceProperties {
#NotNull
#Min(5)
private String anyProperty;
// this is for nested objects
#Valid
#NotNull
private FooNested fooNested;
public static class FooNested{
#NotNull
private String someVal;
}
}
You could also perform manual validation in setter
#Validated
#ConfigurationProperties("env")
public class MyServiceProperties {
private String anyProperty;
public void setAnyProperty(String anyProp){
// just an example
if(anyProp.lenght < 6){
throw new RuntimeException();
}
this.anyProperty = anyProp;
}
}

How to map configuration objects to java object

I have a spring boot application which is using a spring cloud config.
How can i map a configuration element with some java object.
My config is something like this:
clients:
- id : 1
name: client 1
groups : [a,b]
- id : 2
name: client 2
groups : [a]
And my java object is:
public class ClientInfo {
private String clientId;
private List<String> profiles;
public ClientInfo(String clientId, List<String> pips) {
this.clientId = clientId;
this.profiles = pips;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public List<String> getProfiles() {
return profiles;
}
public void setProfiles(List<String> profiles) {
this.profiles = profiles;
}
}
I want to map my configuration with List
Use below code to configure configuration properties in to java Object,
#Component
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "clients")
public class ClientInfo {
private String id;
private String name;
private List<String> groups;
public String getId(){ return id;}
public String getName(){ return name;}
public List<String> getGroups(){ return groups;}
}
Check following for example http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
Inject this class in another class :
#Autowired
private ClientInfo clientInfo;
The above auto wiring will not work if the class is instantiated using "new operator".
Actually I found the reason why it was not working.
All that was needed is to have another class which contains a list of ClientInfo and have #EnableConfigurationProperties and #ConfigurationProperties annotations on it. This is because "clients" in my configuration is a list. After this change we can use #Autowired annotation to inject the configuration.

Resources