I'm using BeanUtils.setProperty to set deep properties on a bean.
Home home = new Home() ;
String path = "home.family.father.age";
Integer value = 40;
BeanUtils.setProperty(home, path, value);
// Does the same as home.getHome().getFamily().getFather().setAge(value);
// But stops on null (instead of throwing an NPE).
The behavior of BeanUtils is to do nothing if one of the intermediary properties is null. So for example in my case, home's family property is null, and nothing happens. If I do
family = new Family();
Then father will be null and I'd have to initialize it too. Obviously my real use case is more complex, with many dynamic properties (and also indexed ones).
Is there a way to tell BeanUtils to instantiate intermediate members ? I know that in general this is not possible (because the concrete type of a property may not be known). But in my case all properties have concrete types and are proper beans (with a public no-args constructor). So it would be possible.
I'd like to make sure there aren't already existing solutions (using BeanUtils or something else) for this before rolling my own.
I rolled my own. It only supports simple properties but I guess adding support for nested/mapped properties wouldn't be too hard.
Here is a gist in case anyone needs the same thing:
https://gist.github.com/ThomasGirard/7115693
And here's what the most important part looks like:
/** Mostly copy-pasted from {#link PropertyUtilsBean.setProperty}. */
public void initProperty(Object bean, String path) throws SecurityException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
// [...]
// If the component is null, initialize it
if (nestedBean == null) {
// There has to be a method get* matching this path segment
String methodName = "get" + StringUtils.capitalize(next);
Method m = bean.getClass().getMethod(methodName);
// The return type of this method is the object type we need to init.
Class<?> propType = m.getReturnType();
try {
// Since it's a bean it must have a no-arg public constructor
Object newInst = propType.newInstance();
PropertyUtils.setProperty(bean, next, newInst);
// Now we have something instead of null
nestedBean = newInst;
} catch (Exception e) {
throw new NestedNullException("Could not init property value for '" + path + "' on bean class '"
+ bean.getClass() + "'. Class: " + propType);
}
}
// [...]
}
Related
I'm putting more attention into unit tests these days and I got in a situation for which I'm not sure how to make a good test.
I have a function which creates and returns an object of class X. This X class is part of the framework, so I'm not very familiar with it's implementation and I don't have freedom as in the case of my "regular collaborator classes" (the ones which I have written). Also, when I pass some arguments I cannot check if object X is set to right parameters and I'm not able to pass mock in some cases.
My question is - how to check if this object was properly created, that is, to check which parameters were passed to its constructor? And how to avoid problem when constructor throws an exception when I pass a mock?
Maybe I'm not clear enough, here is a snippet:
public class InputSplitCreator {
Table table;
Scan scan;
RegionLocator regionLocator;
public InputSplitCreator(Table table, Scan scan, RegionLocator regionLocator) {
this.table = table;
this.scan = scan;
this.regionLocator = regionLocator;
}
public InputSplit getInputSplit(String scanStart, String scanStop, Pair<byte[][], byte[][]> startEndKeys, int i) {
String start = Bytes.toString(startEndKeys.getFirst()[i]);
String end = Bytes.toString(startEndKeys.getSecond()[i]);
String startSalt;
if (start.length() == 0)
startSalt = "0";
else
startSalt = start.substring(0, 1);
byte[] startRowKey = Bytes.toBytes(startSalt + "-" + scanStart);
byte[] endRowKey = Bytes.toBytes(startSalt + "-" + scanStop);
TableSplit tableSplit;
try {
HRegionLocation regionLocation = regionLocator.getRegionLocation(startEndKeys.getFirst()[i]);
String hostnamePort = regionLocation.getHostnamePort();
tableSplit = new TableSplit(table.getName(), scan, startRowKey, endRowKey, hostnamePort);
} catch (IOException ex) {
throw new HBaseRetrievalException("Problem while trying to find region location for region " + i, ex);
}
return tableSplit;
}
}
So, this creates an InputSplit. I would like to know whether this split is created with correct parameters. How to do that?
If the class is part of a framework, then you shouldn't test it directly, as the framework has tested it for you. If you still want to test the behaviour of this object, look at the cause-reaction this object would cause. More specifically: mock the object, have it do stuff and check if the affected objects (which you can control) carry out the expected behaviour or are in the correct state.
For more details you should probably update your answer with the framework you're using and the class of said framework you wish to test
This is possibly one of those cases where you shouldn't be testing it directly. This object is supposedly USED for something, yes? If it's not created correctly, some part of your code will break, no?
At some point or another, your application depends on this created object to behave in a certain way, so you can test it implicitly by testing that these procedures that depend on it are working correctly.
This can save you from coupling more abstract use cases from the internal workings and types of the framework.
I am using Apache Commons Configuration library with PropertiesConfiguration.
My application loads the config file right after its started, like this:
public PropertiesConfiguration loadConfigFile(File configFile) throws ConfigurationNotFoundException {
try {
if (configFile != null && configFile.exists()) {
config.load(configFile);
config.setListDelimiter(';');
config.setAutoSave(true);
config.setReloadingStrategy(new FileChangedReloadingStrategy());
setConfigLoaded(true);
}
else {
throw new ConfigurationNotFoundException("Configuration file not found.");
}
} catch (ConfigurationException e) {
logger.warn(e.getMessage());
setDefaultConfigValues(config);
config.setFile(configFile);
}
return config;
}
My question is, how can I validate the configFile, so I can be sure that no property in that file is missing and later in my code I won't get a NullPointerException when trying to access the properties, e.g.:
PropertiesConfiguration config = loadConfig(configFile);
String rootDir = config.getString("paths.download"); // I want to be sure that this property exists right at the app start
I didn't found anything in the documentation or google, just something about XML validation.
The goal is to provide feedback to the user at program start that the configuration file is corrupted.
There is no build-in mechanism for properties-file?
What is a configuration object supposed to do if you pass in a key to one of its get methods that does not map to an existing property?
the default behavior as implemented in AbstractConfiguration is to return null if the return value is an object type.
For primitive types as return values returning null (or any other special value) is not possible, so in this case a NoSuchElementException is thrown
// This will return null if no property with key "NonExistingProperty" exists
String strValue = config.getString("NonExistingProperty");
// This will throw a NoSuchElementException exception if no property with
// key "NonExistingProperty" exists
long longValue = config.getLong("NonExistingProperty");
For object types like String, BigDecimal, or BigInteger this default behavior can be changed:
If the setThrowExceptionOnMissing() method is called with an argument of true, these methods will behave like their primitive counter parts and also throw an exception if the passed in property key cannot be resolved.
Situation is little tricky for Collection & array types as they will return empty collection or array.
Background: In several Java frameworks like Spring there is the possibility to have methods that are called with injected parameter values. A good example is a controller action in Spring Web/MVC that receives a POST value and has a redirect attribute.
Example:
#RequestMapping(value = "/testform", method = RequestMethod.POST)
public ModelAndView testform(#RequestParam(value = "postvalue") String postvalue, RedirectAttributes attributes)
{
if(postvalue.equals("Test"))
{
// Do stuff with attributes
}
return new ModelAndView("addresses");
}
For example when I would like to use something similar in an own application (No Spring included available) - I end up with something like that (Hacked together):
Strign actionname = "mymethod";
Controller controller = new SampleController();
for(Method method : controller.getClass().getDeclaredMethods())
{
String name = method.getName();
if(name.equals(actionname))
{
int parametercount = method.getParameterCount();
if(parametercount == 0) // No parameter
{
ModelAndView view = (ModelAndView) method.invoke(controller);
// Do stuff
}
else if(parametercount == 1) // 1 String parameter
{
ModelAndView view = (ModelAndView) method.invoke(controller, new String("parameter1"));
// Do stuff
}
else if(parametercount == 2) // 2 String parameters
{
ModelAndView view = (ModelAndView) method.invoke(controller, new String("parameter1"), new String("parameter2"));
// Do stuff
}
else // Error
{
// Unsupported method
}
break;
}
}
Problem: This solution only supports void, 1-parameter and 2-parameter methods that take a string as argument - nothing else
Question: How does Java and Spring allow such a feature? Does Spring have a huge array of method.invoke(...) that are suitable for the most common methods or is there a more dynamic solution to this problem?
Final solution: I ended up with this (unfinished) solution based on Seelenvirtuose answer:
else if(parametercount == 2)
{
Object[] parameters = new Object[2];
parameters[0] = new String("Hello");
parameters[1] = new String("world!");
method.invoke(controller, parameters);
}
Aside from any injection dependency frameworks in general (and Spring specifically), you seem to ask how to reflectively call methods with an arbitrary number of parameters.
As you can see in the invoke method's signature, you provide all parameters in an array. So you simply should assemble an argument array and provide that:
Object[] arguments = createArguments(parametercount); // create array of correct size
(ModelAndView) method.invoke(controller, arguments);
Note, that varargs are treated like an arry. Oh, and please respect the comments about string behaviors.
In principle Spring does the same thing, just more sophisticated.
Especially they don't look for names (at least for many things) but for annotations. You can get the annotations of classes, methods, fields and so on.
The other thing they'll use is that invoke takes an vararg, which is basically an array, so instead of having one if branch for each number of parameters, they pass just an array with the correct number of elements.
I have a property file which is like this -
emailFrom=hello#abc.com
emailTo=world#abc.com
# can be separated by comma
whichServer=UserServer,GuestServer
maxTestInSec=120
numberOfUsers=1000
Now I am reading this property file like this in Java which works if everything is set properly -
private static final Properties prop = new Properties();
private static String emailFrom;
private static String emailTo;
private static List<String> whichServer;
private static String maxTestInSec;
private static String numberOfUsers;
public static void main(String[] args) {
readConfig(args);
}
private void readConfig(String[] args) throws FileNotFoundException, IOException {
if (!TestUtils.isEmpty(args) && args.length != 0) {
prop.load(new FileInputStream(args[0]));
} else {
prop.load(TestTask.class.getClassLoader().getResourceAsStream("config.properties"));
}
emailFrom = prop.getProperty("emailFrom").trim();
emailTo = prop.getProperty("emailTo").trim();
whichServer = Arrays.asList(prop.getProperty("whichServer").trim().split(","));
maxTestInSec = prop.getProperty("maxTestInSec").trim();
numberOfUsers = prop.getProperty("numberOfUsers").trim();
}
Problem Statement:-
I need to make sure that if any of the property value is missing then I want to use default value for that and if by any chance that property is commented out, then also I want to use default value but I would log a warning message stating the property is missing or empty so using default values. I am trying to cover all the corner cases for reading the file -
Now let's say, if I am not specifying values to any of my property in the above file, then I want to use default values for the property which I haven't provided and log as a warning stating that, no values have been provided for this property so using the default values. For example : Let's say if I haven't provided any value for emailFrom field, then I would like to use default value as hello#abc.com for that and similar thing for others. The default values for all the property will be :
emailFrom=hello#abc.com
emailTo=world#abc.com
whichServer=UserServer
maxTestInSec=30
numberOfUsers=500
Also, if any of the property is commented out then the above code is going to through NPE exception. How can I use default values in that scenario as well?
Should I start using Command Line parser for this? What is the best and clean way to handle these stuffs?
I don't want to have lot of if blocks to add a check and then set the default values.
As of Java 8 the easiest thing to do is use getOrDefault() which lets you specify a default value at the get-site. For example:
String email = properties.getOrDefault("emailFrom", "hello#abc.com");
This is clean and concise, but does mean you need to specify the default everywhere you access the property.
If that won't work for you (i.e. you'll be reading values from the properties object more than once) you can use the built-in support for default values -notice the constructor that takes a default Properties object. This lets you construct a Properties object containing your defaults, and then when you load the user's properties file it will fall back on the defaults if the user doesn't specify a value.
private static final Properties DEFAULTS = new Properties();
static {
DEFAULTS.setProperty("emailFrom", "hello#abc.com");
}
public Properties getProperties() {
Properties props = new Properties(DEFAULTS);
props.load(...);
return props;
}
Just notice that this isn't identical to how Map's constructor works - the defaults are left as a separate map, and only .getProperty() also queries the defaults; the methods defined in Map like .get() don't. One of the many reasons it was a terrible decision for Properties to extend Hashtable, but c'est la vie...
These options work, but they're both error-prone since a) Properties is mutable and b) only some of its public methods fall back the default instance. I prefer to never expose Properties objects directly, and instead create a wrapper class with type-safe methods that expose the values my application will care about. This is a little more typing, but it's much safer to work with. It would look something like this:
public class ApplicationSettings {
private final Properties properties = new Properties();
public ApplicationSettings() {
properties.load(...);
}
public String emailFrom() {
// simple methods are concise, and encode the default right inline
return properties.getOrDefault("emailFrom", "hello#abc.com");
}
public int getMaxTestSeconds() {
// You can do more complex validation if you want, too
String value = properties.get("maxTestInSec");
if (value == null) {
return 30;
}
int maxTestSeconds = Integer.parseInt(value);
if (maxTestSeconds <= 0) {
// could instead log a warning and return the default if you want
throw new IllegalStateException(
"maxTestInSec must be positive - was " + maxTestSeconds);
}
return maxTestSeconds;
}
}
If you need you can also expose setters that similarly validate the values before adding them to the Properties object, though by default making everything read-only is generally a good practice.
In case of a property is commented out, the return will be null, so simply do a null check.
if (prop.getProperty("name")==null)
In case of a value is not filled, check whether its equal to empty space after trim operation.
if (prop.getProperty("name").trim().equals(""))
You can try cashing the properties into static map and process on that map before its being used actually.
private Map<String, String> rawProps = new HashMap<String, String>;
public static Map<String, String> actualProps = new HashMap<String, String>;
static {
checkMapForNullAndReport();
}
private static void checkMapForNullAndReport() {
// Null logic and Reporting logic
// Empty rawProps and populate the actualProps
}
Something like this would work for you i believe.
In spring app I'd like to have ability to change content of application.proporties and propagate these change to all bean values annotated by #Value.
I know that I can simply reload all Spring context but I don't want to do this. Some of components can't be recreated. I want to reload values only for seleted bean proporties.
I am using Spring 3.2
My solution
I prototyped own solution but it requires use of reflection on some Spring internal classes. I'm looking for clearer solution
Get required internal Sping objects and make public methods/fields which I need:
TypeConverter typeConverter = beanFactory.getTypeConverter();
// get object which store autowiring metadata for beans
Object annotationProcessor = beanFactory
.getSingleton("org.springframework.context.annotation.internalAutowiredAnnotationProcessor");
// let it be public for my usage
Method findMethod = ReflectionUtils.findMethod(AutowiredAnnotationBeanPostProcessor.class ,"findAutowiringMetadata", Class.class);
ReflectionUtils.makeAccessible(findMethod);
// let it be public for my usage
Field checkedElements = ReflectionUtils.findField(InjectionMetadata.class, "checkedElements");
ReflectionUtils.makeAccessible(checkedElements);
Iterate over all beans and search for elements with my own annotation (#Reloadable). Then reload values for them
for (String name : allBeanNames) {
Object bean = beanFactory.getSingleton(name);
InjectionMetadata metadata = (InjectionMetadata) findMethod.invoke(
annotationProcessor, bean.getClass());
#SuppressWarnings("unchecked")
Set<InjectedElement> elements = (Set<InjectedElement>) checkedElements
.get(metadata);
for (InjectedElement element : elements) {
Member member = element.getMember();
// check for each elements if it has my own annotation which
// means that it should be realoaded
// check removed for simplify
// get new value and load it
String placeholder = valueAnnotation.value();
String resolvedValue = env.resolvePlaceholders(placeholder);
Object converted = typeConverter.convertIfNecessary(
resolvedValue, type, field);
// load value
if (member instanceof Method) {
((Method) member).invoke(bean, converted);
} else if (member instanceof Field) {
((Field) member).set(bean, converted);
}
}
}
Other solutions
http://www.wuenschenswert.net/wunschdenken/archives/107 - works for older version of spring