using a .properties file with a Custom Bean Parser - java

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.

Related

Order YAML file entries according to its java equivalent class (SnakeYaml)

I am using SnakeYaml to both load/dump data in Java. For this I have created a custom class with fields, say that the class looks something like this:
public class Person {
private String name;
private String lastName;
private String address;
public Person() {
// Do nothing
}
// Getters and setters initialized for all the fields
}
Now, what I would like is that when I write a Person object to a file with SnakeYaml I would want to have the fields in the order they are defined in the class.
e.g.
name: Patrick
lastName: Star
Age : 42
The problem is that for more advanced examples, this ordering is not achieved. Currently I am writing/dumping to a yaml file like the following:
Constructor struct = new Constructor(YamlIteratorModel.class);
Yaml yaml = new Yaml(struct);
try {
String path = "Some/File/Path/yamlfile.yaml";
FileWriter writer = new FileWriter(path);
yaml.dump(iteratorModel, writer);
} catch (IOExcepton e) {
// Do something
}
What I have also tried is creating a Representer class which extends Representer and calls the Yaml constructor in a similar manner. This one is taken from another post, and it doesn't do the job for me as it only sorts the Properties in an order I am not entirely sure of (can't find the link right now but will update if I find it again)..
public class ConfigurationModelRepresenter extends Representer {
/**
* Create object without specified dumper object
*/
public ConfigurationModelRepresenter() {
super();
}
/**
* Create object with dumper options
*
* #param options
*/
public ConfigurationModelRepresenter(DumperOptions options) {
super(options);
}
/** {#inheritDoc} */
#Override
protected Set<Property> getProperties(Class< ? extends Object> type) {
Set<Property> propertySet;
if (typeDefinitions.containsKey(type)) {
propertySet = typeDefinitions.get(type).getProperties();
} else {
propertySet = getPropertyUtils().getProperties(type);
}
List<Property> propsList = new ArrayList<>(propertySet);
Collections.sort(propsList, new BeanPropertyComparator());
return new LinkedHashSet<>(propsList);
}
class BeanPropertyComparator implements Comparator<Property> {
#Override
public int compare(Property p1, Property p2) {
// p1.getType().get
if (p1.getType().getCanonicalName().contains("util") && !p2.getType().getCanonicalName().contains("util")) {
return 1;
} else if (p2.getName().endsWith("Name") || p2.getName().equalsIgnoreCase("name")) {
return 1;
} else {
return -1;
}
}
}
}
SUMMARY: How do I maintain the ordering when dumping an object to a YAML file (using SnakeYaml) e.g. the order the fields appear defined in the custom class?
See this question, which discusses that you cannot get the line number of a declared field via reflection.
Together with the fact that reflection gives you a classes' fields in no particular order, it is obvious that it is not possible to observe the order of declared fields in a class at runtime, and it follows that you cannot order the keys in your YAML output according to their position/order in the source, because you cannot know that order.
The remedy is to transport the knowledge of the order to the runtime. Some possible ways to do this might be:
Annotate each field with a weight that defines the position of the resulting YAML key (ugly because you need annotations on the fields).
Autogenerate code by parsing the class' definition discovering the order from there, and write it to some autogenerated source file whose code is then used to order the properties in your Representer (this solution, while avoiding bloat in the original class, is very complex and elaborate).
Hard-code the field order in the Representer. That's basically the previous solution but without autogenerating. Error-prone because the Representer must be adjusted each time the class is changed.
I recommend against using any of those solutions. The YAML spec specifically says that key order must not convey content information, and if the order is important to you, you are already violating the YAML spec and should switch to a format that better serves your needs.

Spring - How to handle an empty value in bean

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

QueryStringBindable for a custom enum

I've defined an enum type Format that implements QueryStringBindable. I think I've implemented it correctly, but in my routes file, I can't specify my type as a route parameter, because the compiler can't find it, and I have no idea how to import it into the routes file.
Here's the enum:
package web;
import java.util.Map;
import play.libs.F;
import play.mvc.QueryStringBindable;
public enum Format implements QueryStringBindable<Format> {
Html,
Pdf,
Csv;
private Format value;
#Override
public F.Option<Format> bind(String key, Map<String, String[]> data) {
String[] vs = data.get(key);
if (vs != null && vs.length > 0) {
String v = vs[0];
value = Enum.valueOf(Format.class, v);
return F.Option.Some(value);
}
return F.Option.None();
}
#Override
public String unbind(String key) {
return key + "=" + value;
}
#Override
public String javascriptUnbind() {
return value.toString();
}
}
And here's my route:
GET /deposits controllers.Deposits.index(selectedAccountKey: Long ?= 0, format: Format ?= Format.Html)
How can I tell the compiler about my enum? Thanks!
Edit
I've also tried adding the path to the type in Build.scala as has been recommended in other posts:
val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings(
routesImport += "web.Format",
resolvers += Resolver.url("My GitHub Play Repository", url("http://www.joergviola.de/releases/"))(Resolver.ivyStylePatterns)
)
I changed that and restarted my server, but it appears to make no difference whatsoever.
I had the same problem and I finally found out that it is not solvable as is.
By reading the documentation for PathBindable and QueryStringBindable I found that play framework requires the Bindable to provide a No Argument public constructor. Which by definition is no possible with enum in Java.
I'd like to offer you the same solution I gave another (more recent) question.
I just wrapped the enum into a small Wrapper class that implements QueryStringBindable or PathBindable.
play framework - bind enum in routes
Use qualified name in the routes file, i.e. web.Format

Change property name with Flexjson

I use FlexJson for serialization, the only problem is that it generates the field names lower case while I need them to start with upper case:
class Person
{
String name;
public String getName() { return name;}
}
When serialized the field is serialized as name, while I need it to be Name.
How can I specify the output field name? Is there some attribute I can put to specify the required serialization name?
You can achieve this by using a Custom Transformer. As per Flexjson page transformer is:
Responsible for deciding how to translate the passed in object to
JSON, making the appropriate calls on the JSONContext object to output
the JSON, and/or passing the object along the transformation process.
Flexjson has provided an abstract class AbstractTransformer for this purpose; Extend and override transform(Object object) to handle the transformation by yourself.
Pasted below is the code of FieldNameTransformer which I wrote for specifying the field name s manually:
public class FieldNameTransformer extends AbstractTransformer {
private String transformedFieldName;
public FieldNameTransformer(String transformedFieldName) {
this.transformedFieldName = transformedFieldName;
}
public void transform(Object object) {
boolean setContext = false;
TypeContext typeContext = getContext().peekTypeContext();
//Write comma before starting to write field name if this
//isn't first property that is being transformed
if (!typeContext.isFirst())
getContext().writeComma();
typeContext.setFirst(false);
getContext().writeName(getTransformedFieldName());
getContext().writeQuoted(object.toString());
if (setContext) {
getContext().writeCloseObject();
}
}
/***
* TRUE tells the JSONContext that this class will be handling
* the writing of our property name by itself.
*/
#Override
public Boolean isInline() {
return Boolean.TRUE;
}
public String getTransformedFieldName() {
return this.transformedFieldName;
}
}
Following is how to use this custom transformer:
JSONSerializer serializer = new JSONSerializer().transform(new FieldNameTransformer("Name"), "name");
where original field's name is 'name' but in json ouput it will be replaced with Name.
Sample out:
{"Name":"Abdul Kareem"}

Java Beans, BeanUtils, and the Boolean wrapper class

I'm using BeanUtils to manipulate Java objects created via JAXB, and I've run into an interesting issue. Sometimes, JAXB will create a Java object like this:
public class Bean {
protected Boolean happy;
public Boolean isHappy() {
return happy;
}
public void setHappy(Boolean happy) {
this.happy = happy;
}
}
The following code works just fine:
Bean bean = new Bean();
BeanUtils.setProperty(bean, "happy", true);
However, attempting to get the happy property like so:
Bean bean = new Bean();
BeanUtils.getProperty(bean, "happy");
Results in this exception:
Exception in thread "main" java.lang.NoSuchMethodException: Property 'happy' has no getter method in class 'class Bean'
Changing everything to a primitive boolean allows both the set and get call to work. I don't have this option, however, since these are generated classes. I assume this happens because the Java Bean libraries only consider an is<name> method to represent a property if the return type is a primitive boolean, and not the wrapper type Boolean. Does anyone have a suggestion as to how to access properties like these through BeanUtils? Is there some kind of workaround I can use?
Finally I've found legal confirmation:
8.3.2 Boolean properties
In addition, for boolean properties, we allow a getter method to match the pattern:
public boolean is<PropertyName>();
From JavaBeans specification. Are you sure you haven't came across JAXB-131 bug?
Workaround to handle Boolean isFooBar() case with BeanUtils
Create new BeanIntrospector
private static class BooleanIntrospector implements BeanIntrospector{
#Override
public void introspect(IntrospectionContext icontext) throws IntrospectionException {
for (Method m : icontext.getTargetClass().getMethods()) {
if (m.getName().startsWith("is") && Boolean.class.equals(m.getReturnType())) {
String propertyName = getPropertyName(m);
PropertyDescriptor pd = icontext.getPropertyDescriptor(propertyName);
if (pd == null)
icontext.addPropertyDescriptor(new PropertyDescriptor(propertyName, m, getWriteMethod(icontext.getTargetClass(), propertyName)));
else if (pd.getReadMethod() == null)
pd.setReadMethod(m);
}
}
}
private String getPropertyName(Method m){
return WordUtils.uncapitalize(m.getName().substring(2, m.getName().length()));
}
private Method getWriteMethod(Class<?> clazz, String propertyName){
try {
return clazz.getMethod("get" + WordUtils.capitalize(propertyName));
} catch (NoSuchMethodException e) {
return null;
}
}
}
Register BooleanIntrospector:
BeanUtilsBean.getInstance().getPropertyUtils().addBeanIntrospector(new BooleanIntrospector());
you can just create second getter with SET - sufix as workaround :)

Categories