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

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.

Related

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 use Java 8 Optional with Moxy and Jersey

Is it possible to use Jersey with Moxy to/from Json and Java 8 Optionals?
How to configure it?
You can declare following class:
public class OptionalAdapter<T> extends XmlAdapter<T, Optional<T>> {
#Override
public Optional<T> unmarshal(T value) throws Exception {
return Optional.ofNullable(value);
}
#Override
public T marshal(Optional<T> value) throws Exception {
return value.orElse(null);
}
}
And use like this:
#XmlRootElement
public class SampleRequest {
#XmlElement(type = Integer.class)
#XmlJavaTypeAdapter(value = OptionalAdapter.class)
private Optional<Integer> id;
#XmlElement(type = String.class)
#XmlJavaTypeAdapter(value = OptionalAdapter.class)
private Optional<String> text;
/* ... */
}
Or declare in package-info.java and remove #XmlJavaTypeAdapter from POJOs:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlJavaTypeAdapters({
#XmlJavaTypeAdapter(type = Optional.class, value = OptionalAdapter.class)
})
But here are some drawbacks:
Adapter above can only work with simple types like Integer, String, etc. that can be parsed by MOXY by default.
You have to specify #XmlElement(type = Integer.class) explicitly to tell the parser type are working with, otherwise null values would be passed to adapter's unmarshal method.
You miss the opportunity of using adapters for custom types, e.g. custom adapter for java.util.Date class based on some date format string. To overcome this you'll need to create adapter something like class OptionalDateAdapter<String> extends XmlAdapter<String, Optional<Date>>.
Also using Optional on field is not recommended, see this discussion for details.
Taking into account all the above, I would suggest just using Optional as return type for your POJOs:
#XmlRootElement
public class SampleRequest {
#XmlElement
private Integer id;
public Optional<Integer> getId() {
return Optional.ofNullable(id);
}
public void setId(Integer id) {
this.id = id;
}
}

Spring Boot String Deserializer for every form field

In our web app create and update forms have a size validation. For instance:
#Size(min = 4, max = 20)
private String mobile;
As seen the field is not required. But at the front-end user wants to clear field. Then form validation fails because of length restriction. Incoming data is an empty string instead of null. So minimum length validation restricts the input.
Therefore I start to search a solution to convert empty strings to null values. I found a #InitBinder and StringTrimmerEditor solution but our system uses #ResponseBody approach. So It doesn't fit.
Adding #JsonDeserialize(using = CustomTrimDeserializer.class) annotation or writing a custom setter for every string field is not DRY solution.
I just want to add app wide custom deserializer for String fields.
I finally examine the JsonComponentModule class and noticed spring is looking for the JsonComponent annotation for deserializer registration.
This is a one file spring boot project for solution
#RestController
#SpringBootApplication
public class CheckNullApplication {
public static void main(String[] args) {
SpringApplication.run(CheckNullApplication.class, args);
}
#PostMapping("/check-null")
public boolean checkNull(#RequestBody final HelloForm form) {
return form.getName() == null;
}
public static class HelloForm {
private String name;
public String getName() { return name; }
public void setName(final String name) { this.name = name;}
}
#JsonComponent
public static class StringTrimmerDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(final JsonParser p, final DeserializationContext ctxt)
throws IOException, JsonProcessingException {
String result = StringDeserializer.instance.deserialize(p, ctxt);
if (result != null) {
result = result.trim();
if (StringUtils.isEmpty(result)) {
return null;
}
}
return result;
}
}
}
Instead of adding #JsonDeserialize annotation you may want to just register your custom deserializer via Module (for example, SimpleModule), and it will apply to all String valued properties. Something like:
SimpleModule module = new SimpleModule(...);
module.addDeserializer(String.class, new CustomTrimDeserializer());
mapper.registerModule(module);
Create a class as following and annotate with #JsonComponent. Spring boot will pick that up as a component.
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
#JsonComponent
public class WhitSpaceTrimmerDeserializer extends StringDeserializer {
#Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
final String value = super.deserialize(p, ctxt);
return value!=null?value.trim():null;
}

Spring #Requestbody not mapping to inner class

I am doing Spring Rest Api project with Spring 4.x
This Works:
Controller.java
#PostMapping("newTransaction")
TransactionRequestModel insertNewTransaction(#RequestBody TransactionRequestModel model){
//do something
}
TransactionRequestModel.java
public class TransactionRequestModel {
private int id;
private List<KeyValue> keyValueList;
public TransactionRequestModel(){}
//default constructor
//getter-setter
}
KeyValue.java
public class KeyValue {
String key;
String value;
//default constructor
//setter-getter
}
Request Body Json
{
"id": 1
"keyValueList": [
{
"key": "dummy",
"value": "dummy"
}
]
}
Spring message converter using jackson is working fine.
This Won't:
When i change TransactionRequestModel.java to following (and delete KeyValue.java)
public class TransactionRequestModel {
public class KeyValue {
String key;
String value;
//default constructor
//setter-getter
}
private int id;
private List<KeyValue> keyValueList;
public TransactionRequestModel(){}
//default constructor
//getter-setter
}
means, making KeyValue an inner class, got following error.
org.springframework.http.converter.HttpMessageNotReadableException:
Could not read document: No suitable constructor found for type
[simple type, class
com.example.model.TransactionRequestModel$KeyValue]: can not
instantiate from JSON object (missing default constructor or creator,
or perhaps need to add/enable type information?)
Why?
All the related post in SO mentions the first scenario. I would like to know why this wont work. Please help.
You have to make your inner class static.
public class TransactionRequestModel {
public static class KeyValue {
String key;
String value;
//default constructor
//setter-getter
}
private int id;
private List<KeyValue> keyValueList;
public TransactionRequestModel(){}
//default constructor
//getter-setter
}

#ConfigurationProperties referencing properties that themselves reference other properties

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;
...
}

Resources