Spring - How to handle an empty value in bean - java

I would like to ask how to handle a case if I get an empty value in bean.
the scenario is that I have a spring that load a property file and store the new property (that I just added) to myProp value:
<bean id="ConfigurationUtility" class="configuration.ConfigurationUtility">
<property name="UntilTimeInQuote" value="myProp"/>
</bean>
When the property set to true or false everything works fine and as expected. However, I want to handle a case that the property does not exist in the property file at all, meaning it gets null.
How to catch that state and handle in the code?

You can define your property as a boolean object and process the value in the setter. In this way you can manage the value at the moment when spring set the value.
public class MyBean{
private Boolean untilTimeInQuote;
public setUntilTimeInQuote(Boolean value){
if(value == null){
//do something.
}else{
// do something else.
}
}
}
Another option is to use a bean post processor action, it's fired after the bean creation and after the properties are set.
public class MyBean{
private Boolean untilTimeInQuote;
#PostConstruct
public void init(){
if(untilTimeInQuote == null){
//do something.
}else{
// do something else.
}
}
public setUntilTimeInQuote(Boolean value){
this.untilTimeInQuote = value
}
}
}
you can see more here
https://www.mkyong.com/spring/spring-postconstruct-and-predestroy-example/

If you're using #Value to inject the properties into your classes, then you can supply a default to use if the properties file doesn't specify one:
public class MyClass {
#Value("${myProperty:false}")
private boolean myProperty;
#Value("${serverUrl:localhost}")
private String serverUrl;
}
Now
myProperty will be false if nothing supplied for it.
serverUrl will be localhost if nothing supplied for it.
I like this way: no "handling" necessary - just a default value to use instead.

There's a whole chapter on bean validation.
In particular, if you declare a BeanValidationPostProcessor, then you can use annotations on your classes to define constraints:
#Data
public class ConfigurationUtility {
private boolean defaultsToFalseIfMissing = false;
#NotNull
private Boolean errorsIfMissing;
#NotBlank
private String mustBeSetToSomethingPrintable;
}
Annotation documentation:
lombok.Data (used to simplify the example code)
javax.validation.constraints.NotNull
org.hibernate.validator.constraints.NotBlank

Related

Set default value to null in Spring #Value on a java.util.Set variable

Having an interesting issue with the Spring #Value annotation using SpEL. Setting default value of null works for a String variable. However, for a Set variable it doesn't.
So, this works (varStr is null):
#Value("${var.string:#{NULL}}")
private String varStr;
while this doesn't (varSet now contains one element with "#{NULL}"):
#Value("#{'${var.set:#{NULL}}'.split(',')}")
private Set<String> varSet;
The question is how to make this work with a Set variable as well so it would be set to null by default.
Your help will be greatly appreciated.
You could try injecting the #Value into an array instead of a Set. Then in a #PostConstruct init block convert that into the desired Set instance. Spring appears to be injecting an empty array (not null) when no such property exists (note the empty default value in the #Value string). When it does exist it splits on comma by default.
Like this:
#Value("${some.prop:}")
private String[] propsArr;
private Set<String> props;
#PostConstruct
private void init() throws Exception {
props = (propsArr.length == 0) ? null : Sets.newHashSet(propsArr);
}
I will just make one other suggestion. I recommend that you don't use null at all, but rather an empty Set instead. Null tends to be error-prone and typically doesn't convey any more info than an empty collection. Your case may well be different - this is just a general recommendation.
BTW - that Sets.newHashSet(...) call is from Google's Guava library. Highly recommended.
You can create a PropertySourcesPlaceholderConfigurer. This bean will intercept the property source values and allow you to configure them.
#Configuration
#ComponentScan
class ApplicationConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
PropertySourcesPlaceholderConfigurer c = new PropertySourcesPlaceholderConfigurer();
c.setNullValue("");
return c;
}
Reference: http://blog.codeleak.pl/2015/09/placeholders-support-in-value.html
Which will default an empty string to null.
Unless you can find an elegant solution to get around this, you can inject the property into your contructor as a String and then Split() it yourself or default to null.
class Foo {
private Set<String> varSet;
public Foo(#Value("${var.string:#{NULL}}") String varString) {
varSet = (varString == null) ? null : new HashSet<>(Arrays.asList(varString.split(",")));
}
}

My custom accessor is never used and only the field default value is used by hibernate validator instead

I have the following code inside a javabean:
#AssertTrue
private boolean addressReferenceValid;
public boolean isAddressReferenceValid() {
if (addressType.equals(AddressType.ON_THE_FLY_ADDRESS) && StringUtils.isEmpty(addressReference)) {
return false;
}
return true;
}
The issue I have is that the isAddressReferenceValid accessor is never used and it seems that only the default value for addressReferenceValid is used (i.e. false).
I have double checked that Spring Roo did not generate its own accessor for that variable.
Can anyone please help?
P.S. I can't put the #AssertTrue annotation directly on the method because then the following key in ValidationMessages.properties file would not be resolved: AssertTrue.familyAdvertisementSearchCriteriaInfo.addressReferenceValid
edit 1:
Full bean:
#RooEquals
#RooJavaBean
public class FamilyAdvertisementSearchCriteriaInfo {
#Valid
private FamilyAdvertisementSearchCriteria searchCriteria;
private Address currentAddress;
private String addressReference;
#NotNull
private AddressType addressType;
#AssertTrue(groups=Default.class)
private boolean addressReferenceValid;
public boolean isAddressReferenceValid() {
if (addressType.equals(AddressType.ON_THE_FLY_ADDRESS) && StringUtils.isEmpty(addressReference)) {
return false;
}
return true;
}
}
Validation occurs in the following controller:
#RequestMapping(value = "/familyAdvertisementSearch", method = RequestMethod.POST, produces = "text/html")
public String familyAdvertisementSearchResults(#ModelAttribute #Validated(Default.class) FamilyAdvertisementSearchCriteriaInfo familyAdvertisementSearchCriteriaInfo, BindingResult bindingResult, Model model) {
if(bindingResult.hasErrors()){
populateModel(model);
familyAdvertisementSearchCriteriaInfo.setCurrentAddress(memberService.retrieveCurrentMemberAddress());
return "search/familyAdvertisementSearchForm";
}
...
I think I understand now what you are trying to do. You want to place the constraint on the field, but during validation you expect the method isAddressReferenceValid to be called/used. That's not going to work. If you place a constraint on a field access is used to get the property to validate (using reflection). If you place it on a method/getter method access is used. So he placement of the annotation matters. As you already seem to have discovered placing the annotation on the method works. Of course this leads to inconsistent placement of annotations. You could:
Just place the annotation for this single constraint
switch to method annotations completely
update the Boolean every time the address type changes (and get rid of isAddressReferenceType method)
create a custom constraint for verifying the addressReference
Just some ideas. It all depends on your use case and personal preferences.

using a .properties file with a Custom Bean Parser

I have a custom implementation of an AbstractSingleBeanDefinitionParser to allow me to define 3D Vectors in my spring config with less... ceremony than would otherwise be required.
<rbf:vector3d id="test_vector" delimeter=";" value="45;46;47"/>
That works great, and I have been using it for months without any problems. Yesterday I tried to define the value in a .properties file like this:
In test.properties I have:
vector3d.value=1,2,3
And in the xml file I have:
<context:property-placeholder location="test.properties"/>
<rbf:vector3d id="test_vector_with_properties" delimeter="," value="${vector3d.value}"/>
When I try to run my unit test, it crashes, and I get this exception:
Caused by: java.lang.NumberFormatException: For input string: "${vector3d.value}"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1222)
at java.lang.Double.parseDouble(Double.java:510)
at scala.collection.immutable.StringLike$class.toDouble(StringLike.scala:234)
at scala.collection.immutable.StringOps.toDouble(StringOps.scala:31)
at rb.foundation.spring.xml.Vector3DBeanDefinitionParser$$anonfun$1.apply(Vector3DBeanDefinitionParser.scala:25)
When I use the .properties file for normal beans, it works great, which leads me to believe that there is a subtlety that I overlooked in my implemention of my parser. It's written in scala, but you should be able to follow it:
class Vector3DBeanDefinitionParser extends AbstractSingleBeanDefinitionParser
{
override def getBeanClass(element : Element) = classOf[Vector3D]
override def doParse(element: Element, builder: BeanDefinitionBuilder)
{
val delim = element.getAttribute("delimeter")
val value = element.getAttribute("value")
val values = value.split(delim).map(_.toDouble)
builder.addConstructorArgValue(values(0))
builder.addConstructorArgValue(values(1))
builder.addConstructorArgValue(values(2))
}
}
I'm happy to add the key substitution if necessary, I just need to know where/how to do it.
Ideas?
So the reason this doesn't work is that your BeanDefinitionParser runs much before property placeholders are resolved. Simple overview as I understand it:
BeanDefinitionParsers parse the XML into BeanDefinition objects in memory
BeanDefinitions are then loaded into a BeanFactory
BeanFactoryPostProcessors (including the property placeholder configurers) are executed on the bean definitions
beans are created from the bean definitions
(Of course other things happen along the way, but those are the relevant steps here.)
So in order to get the resolved property value into your Vector3D object, I think you're going to have to delay specifying the arguments to the Vector3D constructor until after BeanFactoryPostProcessors have run. One way that occurs to me is to have your BeanDefinitionParser construct a bean definition for a Spring FactoryBean instead of the Vector3D itself. Then the splitting of the vector value that you currently have in your Vector3DBeanDefinitionParser would need to be in the FactoryBean implementation instead.
Sorry, I'm not too familiar with Scala so this will be in Java.
The FactoryBean class would look something like this:
import org.springframework.beans.factory.FactoryBean;
public class Vector3DFactoryBean implements FactoryBean<Vector3D> {
private String delimiter;
private String value;
private transient Vector3D instance;
public String getDelimiter() { return delimiter; }
public void setDelimiter(String delimiter) { this.delimiter = delimiter; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
#Override
public Vector3D getObject() {
if (instance == null) {
String[] values = value.split(delimiter);
instance = new Vector3D(
Double.parseDouble(values[0]),
Double.parseDouble(values[1]),
Double.parseDouble(values[2])
);
}
return instance;
}
#Override
public Class<?> getObjectType() {
return Vector3D.class;
}
#Override
public boolean isSingleton() {
return true;
}
}
Then your Vector3DBeanDefinitionParser would just pass the delimiter and value untouched to the Vector3DFactoryBean bean definition:
class Vector3DBeanDefinitionParser extends AbstractSingleBeanDefinitionParser
{
override def getBeanClass(element : Element) = classOf[Vector3DFactoryBean]
override def doParse(element: Element, builder: BeanDefinitionBuilder)
{
val delim = element.getAttribute("delimeter")
val value = element.getAttribute("value")
builder.addPropertyValue("delimiter", delim)
builder.addPropertyValue("value", value)
}
}
Then later when the placeholder property configurer runs, it should resolve the property values in the Vector3DFactoryBean bean definition. When beans are finally created from bean definitions, the Vector3DFactoryBean will parse the vector values and create the Vector3D object.

Spring 3.1.1.RELEASE Databinding: Error when validating submitted form

Since upgrading my webapplication from Spring 3.0.5 to 3.1.1 I have to face some serious errors when validating my form-beans. My previously configured validator(s) doesn't work any more like they should. The problem is that the method getFieldValue(String fieldname) from Class org.springframework.validation.Errors does not return the original binded bean value like it should (and was before).
This is what my form-bean looks like:
public class PersonBean extends BaseFormBean {
private String firstname; // getters and setter omitted...
private String lastname; // getters and setter omitted...
private Integer age; // getters and setter omitted...
public PersonBean() {}
#Override
public void validateForm(Errors errors) {
WebValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstname", "validator.requiredvalidator.lbl", "field required");
WebValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastname", "validator.requiredvalidator.lbl", "field required");
WebValidationUtils.rejectInvalidIntValue(errors, "age", "validator.std.age", "invalid age", false);
}
}
The WebValidationUtils-class that gets invoked has some standard methods for checking bean properties. The error occurcs only on non-String values, like the property age which is of type Integer. It also happens on Collection(s).
The following snippet shows how Integer values are validated in my utils-class:
import org.springframework.validation.Errors;
...
public abstract class WebValidationUtils {
...
public static void rejectInvalidIntValue(Errors errors, String field, String errorCode, String defaultMessage){
Assert.notNull(errors, "Errors object must not be null");
Object value = errors.getFieldValue(field); // returns the string value (type: java.lang.String)
Class<?> fieldType = errors.getFieldType(field); // returns the class Integer!
if (value == null || !value.getClass().equals(Integer.class) || ((Integer)value).intValue() <= 0){
errors.rejectValue(field, errorCode, null, defaultMessage);
}
}
}
The bean itself has the correct value bound...
Do I have to configure some additonal spring beans in my context-servlet.xml do achieve the same bevahior like it was in 3.0.5?
Edit: The official Spring Doku for the method "getFieldValue(...)" says
Return the current value of the given field, either the current bean property value or a rejected update from the last binding.
So I don't have a clue why this method returns a String-value instead of the propagated bean value of type Integer...
Seem like you have a binding error so getFieldValue() return FieldError.getFieldValue() that return the value that causes the binding error. This is the expected behavior.
You can get the value that hold the property using getRawFieldValue() instead. This method always return the value using the PropertyAccessor.

Jackson: What happens if a property is missing?

What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
Summarizing excellent answers by Programmer Bruce and StaxMan:
Missing properties referenced by the constructor are assigned a default value as defined by Java.
You can use setter methods to differentiate between properties that are implicitly or explicitly set. Setter methods are only invoked for properties with explicit values. Setter methods can keep track of whether a property was explicitly set using a boolean flag (e.g. isValueSet).
What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
For questions such as this, I like to just write a sample program and see what happens.
Following is such a sample program.
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
// {"name":"Fred","id":42}
String jsonInput1 = "{\"name\":\"Fred\",\"id\":42}";
Bar bar1 = mapper.readValue(jsonInput1, Bar.class);
System.out.println(bar1);
// output:
// Bar: name=Fred, id=42
// {"name":"James"}
String jsonInput2 = "{\"name\":\"James\"}";
Bar bar2 = mapper.readValue(jsonInput2, Bar.class);
System.out.println(bar2);
// output:
// Bar: name=James, id=0
// {"id":7}
String jsonInput3 = "{\"id\":7}";
Bar bar3 = mapper.readValue(jsonInput3, Bar.class);
System.out.println(bar3);
// output:
// Bar: name=null, id=7
}
}
class Bar
{
private String name = "BLANK";
private int id = -1;
Bar(#JsonProperty("name") String n, #JsonProperty("id") int i)
{
name = n;
id = i;
}
#Override
public String toString()
{
return String.format("Bar: name=%s, id=%d", name, id);
}
}
The result is that the constructor is passed the default value for the data type.
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
One simple approach would be to check for a default value post deserialization processing, since if the element were present in the JSON but had a null value, then the null value would be used to replace any default value given the corresponding Java field. For example:
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFooToo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY);
// {"name":null,"id":99}
String jsonInput1 = "{\"name\":null,\"id\":99}";
BarToo barToo1 = mapper.readValue(jsonInput1, BarToo.class);
System.out.println(barToo1);
// output:
// BarToo: name=null, id=99
// {"id":99}
String jsonInput2 = "{\"id\":99}";
BarToo barToo2 = mapper.readValue(jsonInput2, BarToo.class);
System.out.println(barToo2);
// output:
// BarToo: name=BLANK, id=99
// Interrogate barToo1 and barToo2 for
// the current value of the name field.
// If it's null, then it was null in the JSON.
// If it's BLANK, then it was missing in the JSON.
}
}
class BarToo
{
String name = "BLANK";
int id = -1;
#Override
public String toString()
{
return String.format("BarToo: name=%s, id=%d", name, id);
}
}
Another approach would be to implement a custom deserializer that checks for the required JSON elements. And yet another approach would be to log an enhancement request with the Jackson project at http://jira.codehaus.org/browse/JACKSON
In addition to constructor behavior explained in #Programmer_Bruce's answer, one way to differentiate between null value and missing value is to define a setter: setter is only called with explicit null value.
Custom setter can then set a private boolean flag ("isValueSet" or whatever) if you want to keep track of values set.
Setters have precedence over fields, in case both field and setter exist, so you can "override" behavior this way as well.
I'm thinking of using something in the style of an Option class, where a Nothing object would tell me if there is such a value or not. Has anyone done something like this with Jackson (in Java, not Scala, et al)?
(My answer might be useful to some people finding this thread via google, even if it doesn't answer OPs question)
If you are dealing with primitive types which are omittable, and you do not want to use a setter like described in the other answers (for example if you want your field to be final), you can use box objects:
public class Foo {
private final int number;
public Foo(#JsonProperty Integer number) {
if (number == null) {
this.number = 42; // some default value
} else {
this.number = number;
}
}
}
this doesn't work if the JSON actually contains null, but it can be sufficient if you know it will only contain primitives or be absent
another option is to validate the object after deserialization either manually or via frameworks such java bean validation or, if you are using spring, the spring validation support.

Categories