The Beanutils Package


The Beanutils Package

The Beanutils package is used to dynamically manipulate a Java Bean. Typically, when referencing other classes, most classes will use a hard-coded identifier. Using the Beanutils package, you can dynamically choose a method and then use it when the program is executing.

Let's take the example of building a serialization framework. Listing 7.1 defines a very simple bean (some parts have been omitted for simplicity).

Listing 7.1
start example
 class SimpleBean { public void setValue( int value) { } public int getValue() { } } 
end example
 

In Listing 7.1, the class SimpleBean exposes the property value. In a scripting language the bean could be assigned using a notation similar to Listing 7.2.

Listing 7.2
start example
 SimpleBean bean; bean.value = 1234; 
end example
 

In Listing 7.2, the bean is assigned as if the bean getters and setters were a data member. The problem with Listing 7.2 is that it does not work in Java. If we compiled Listing 7.2, a missing declaration error would be generated since the Java compiler would explicitly look for a data member with an identifier value. In a dynamic solution, the property could be assigned as shown in Listing 7.3.

Listing 7.3
start example
 assignPropertyValue( "value", 1234); 
end example
 

The advantage of the solution proposed by Listing 7.3 is that specific properties could be assigned using configuration files. Alternatively, we could load any generic bean and assign a property without knowing the interface or class definition details.

The sole purpose of the Beanutils package in the Jakarta Commons project is to make dynamic programming much simpler. In fact, many of the packages described in this book depend on the Beanutils package.

Technical Details for the Beanutils Package

Tables 7.1 and 7.2 contain the abbreviated details necessary to use the Beanutils package.

Table 7.1: Repository details for the Beanutils package.

Item

Details

CVS repository

jakarta-commons

Directory within repository

beanutils

Main packages used

org.apache.commons.beanutils

Table 7.2: Package and class details (legend: [ utils ] = org.apache.commons.beanutils).

Class/Interface

Details

[utils].BeanUtils

A static class used to manipulate a bean and perform dynamic operations such as retrieving and assigning property values

[utils].ConvertUtils

A static class used to perform conversions from one data type to another

[utils].Converter

An interface that is implemented when custom data conversion implementations are written

[lang].ConstructorUtils

A static class used to call a constructor dynamically, which returns a newly instantiated object

[lang].MethodUtils

A static class used to call a specific method dynamically

[lang].DynaBean

An interface used to manage a property bag of items stored in key value pairs

Setting and Retrieving Bean Properties

For the next set of demos, we will use the class BeanToWrite from Chapter 5. Listing 7.4 will refresh your memory on the implementation details of the class BeanToWrite .

Listing 7.4
start example
 public class BeanToWrite implements java.io.Serializable { private int _iValue; private String _strValue; public BeanToWrite() { } public BeanToWrite( int ival, String sval) { _iValue = ival; _strValue = sval; } public int getIntegerValue() { return _iValue; } public void setIntegerValue( int val) { _iValue = val; } public String getStringValue() { return _strValue; } public void setStringValue( String val) { _strValue = val; } public String toString() { return "_iValue: [" + _iValue + "] _strvalue: [" + _strValue + "]"; } } 
end example
 

The BeanUtils package has two classes called BeanUtils and BeanUtilsBean . The class BeanUtils is a static class that delegates all of its method implementations to a Bean-UtilsBean singleton. If desired, you can instantiate the class BeanUtilsBean and call each of the methods directly. The class BeanUtilsBean is a simplification of the Java Reflection API, which allows you to dynamically assign properties or call methods.

Listing 7.5 starts with a simple example of assigning a property with a specific value.

Listing 7.5
start example
 BeanToWrite bean = new BeanToWrite(); BeanUtils.setProperty( bean, "integerValue", new Integer( 1)); BeanUtils.setProperty( bean, "integerValue", "2"); BeanUtils.setProperty( bean, "stringValue", "something else"); 
end example
 

In Listing 7.5, the bean BeanToWrite is instantiated and assigned using the method setProperty . The method setProperty has three parameters. The first parameter is the bean that will be modified, the second is the property that is going to be updated, and the last is an object used to assign the property.

In Listing 7.5, the property integerValue was assigned twice. We did this on purpose to illustrate a very important point. The property integerValue is a primitive data type and is not an object. The Beanutils package can transform an object into a primitive data type, as shown by the two property assignments in Listing 7.5. In the first assignment example, the Integer object is instantiated, which is an easy conversion. However, the second assignment example of a string containing the value "2" is a more complex conversion. In either case, the assignment is successful, which indicates that the Beanutils package will attempt a data type transformation in the best way possible.

In contrast to assigning values to a bean, you can dynamically retrieve the values of a bean, as shown in Listing 7.6, which is a continuation of Listing 7.5.

Listing 7.6
start example
 System.out.println( "integerValue " + BeanUtils.getProperty( bean, "integerValue")); System.out.println( "stringValue " + BeanUtils.getProperty( bean, "stringValue")); 
end example
 

In Listing 7.6, the properties are retrieved using the method getProperty . The method getProperty has two parameters: bean, which has the properties, and the property to retrieve the value. Returned is a string buffer that would need to be converted to the desired data type.

Setting and retrieving bean properties using this technique is an interesting technique, but not the most efficient one. It's inefficient because the property has to be searched for, and then the data types are converted to the required data types. Performing those operations takes time, and in performance-critical applications, it might not be the best way to solve problems. Note, though, that it's sometimes not possible to use another technique, as in serialization.

Cloning and Assigning Another Bean

The Java specification defines that when an object is to be cloned or copied , an object should implement the clone method. Implementing the clone method can be tedious , but the class BeanUtils does make this simpler by exposing clone methods. A sample clone usage is shown in Listing 7.7.

Listing 7.7
start example
 BeanToWrite bean = new BeanToWrite( 1234, "hello world"); BeanToWrite cloned = (BeanToWrite)BeanUtils.cloneBean( bean); 
end example
 

In Listing 7.7, the class BeanToWrite is instantiated and then passed to the method cloneBean . The method cloneBean will instantiate a new class instance and will then copy all of the properties. The BeanUtil class becomes very useful because it can copy properties that exist on different objects, as shown in Listing 7.8.

Listing 7.8
start example
 BeanToWrite srcBean = new BeanToWrite( 1234, "hello world"); BetwixtBean destBean = new BetwixtBean(); BeanUtils.copyProperties( destBean, srcBean); 
end example
 

In Listing 7.8, there are two bean classes: BeanToWrite and BetwixtBean , both of which we discussed in Chapter 5. In addition, both classes have the exact same properties. Each class is a different type, so a normal property copy or clone is not possible. The method copyProperties can read the properties from one bean and then set the properties of the other bean. In the case of the method copyProperties , the first parameter is the bean that will have its properties set; the second parameter is the bean that has its properties retrieved.

There is a catch to the clone and copy methods: they are shallow clones or copies. We made them shallow to mimic how the Java clone method operates. However, it is inconvenient because it does make for more complicated coding when you need to perform a deep clone. At the time of this writing, this issue was being resolved. Visit the author's Web site ( www.devspace.com ) for the latest information on this issue and on where to get the updates.

Cloning and Assigning Only Specific Properties

In Listing 7.8, we copied the instance properties of one bean type to an instance of another bean type. When the properties were copied, it was an all-or-nothing situation. This meant that either all of the property values were copied or none were. For a bean cloning solution, this is acceptable, but for a property value transfer, it might not be so acceptable. For instance, the property title might be identified as a title of a book on one bean. On another bean, title might refer to a title given to a person. On an identifier level, they are identical, but logically they are not identical and copying a title of a person on a title of a book would be incorrect. You can solve this problem by selectively removing specific property values, as shown in Listing 7.9.

Listing 7.9
start example
 BeanToWrite sourceBean = new BeanToWrite( 1234, "hello world"); BetwixtBean destBean = new BetwixtBean(); Map properties = BeanUtils.describe( sourceBean); properties.remove( "stringValue"); BeanUtils.populate( destBean, properties); 
end example
 

Like in previous listings, Listing 7.9 starts by instantiating the beans that will be used as source and destination. The method describe is used to extract the property descriptors from the bean instance that will be copied. The resultant Map -based variable properties contains the property descriptors and the associated values. This means that we could serialize or modify the variable properties before assigning them to the destination bean instance. Using the class method Map.remove , a specific property is removed from the collection. We then assign the leftover properties to the destination bean instance using the method populate .

Accessing Arrays and Maps

The Beanutils package can deal with more complex data types such as arrays and maps. Listing 7.10 is a sample bean that the BeanUtils class will use to transfer properties.

Listing 7.10
start example
 public class BeanProperties { private int _intArray[] = new int[ 3]; private String _strArray[] = new String[ 3]; private Map _mapProperty = new HashMap(); public BeanProperties() { _intArray[ 0] = 1; _intArray[ 1] = 3; _intArray[ 2] = 9; _strArray[ 0] = "hello"; _strArray[ 1] = "guten tag"; _strArray[ 2] = "bonjour"; _mapProperty.put( "first", "hello"); _mapProperty.put( "second", "guten tag"); _mapProperty.put( "third", "bonjour"); } public int[] getIntArray() { return _intArray; } public void setIntArray( int[] value) { _intArray = value; } public String[] getStringArray() { return _strArray; } public void setStringArray( String[] value) { _strArray = value; } public Map getMapProperty() { return _mapProperty; } public void setMapProperty(Map value) { _mapProperty = value; } } 
end example
 

In Listing 7.10, the class BeanProperties has two properties that expose an int and String based array. There is also a property that exposes a Map type. In the constructor of the class BeanProperties , the array properties are assigned default values. The getters and setters expose the properties. The experiment is to copy the values of the property intArray to the property stringArray and vice versa. The focus of the experiment is to figure out how the Beanutils package handles this kind of property transfer. Listing 7.11 is an implementation of the experiment.

Listing 7.11
start example
 BeanProperties bean = new BeanProperties(); String arrayString[] = BeanUtils.getArrayProperty( bean, "stringArray"); String arrayInt[] = BeanUtils.getArrayProperty( bean, "intArray"); BeanUtils.setProperty( bean, "stringArray", arrayInt); BeanUtils.setProperty( bean, "intArray", arrayString); 
end example
 

In Listing 7.11, the class BeanProperties is instantiated. Using the method getArrayProperty , an array representing the contents of the property is retrieved. The return value is an array of String values. An array of String values is returned regardless of the array type. This means that the intArray property is converted into an array of string values. The resulting array of string values can then be assigned to the other property using the method setProperty .

The class BeanUtils does not expose the method setArrayProperty . It is expected that the method setProperty is used and that the class BeanUtils figures out how the data needs to be converted. This introduces a question. What happens if the property is not a standard array type like, int , Long , or String ? If a property exposes a method based on an object like the bean in Listing 7.10, BeanUtils converts the object into a string. The conversion performed is based on calling the method toString . Implementing a meaningful toString implementation in this context is a bit tricky. In classical implementations, the toString method should return a string representation of the object, which should be concise and easy to read for the person who has to read the information. The original information of the object is lost; hence, a one-way serialization has occurred. Therefore, when you use the method getArrayProperty , do not consider it as serialization but as a way of getting a textual description of the property. The more accurate way to retrieve properties without destroying the value of the property is to use the method describe, as we saw in Listing 7.9.

You can also retrieve an individual value from an array index, as shown in Listing 7.12.

Listing 7.12
start example
 BeanProperties bean = new BeanProperties(); String value = BeanUtils.getIndexedProperty( bean, "intArray[2]"); value = BeanUtils.getIndexedProperty(bean, "intArray", 2); 
end example
 

In Listing 7.12, the array index is referenced in two different ways, but each time uses the same method: getIndexedProperty . In the first example of the method getIndexedProperty , the array index is referenced using the string intArray[2] . The string is parsed for a property and index identifier. The text is structured in the same way as a programmer would structure a Java index reference. However, what is important is that there cannot be any space between the square brackets because that would generate an exception.

The second way to reference an array index is the last line of Listing 7.12. In that example, the second parameter of the method represents an identifier of the array, and the third parameter represents the index of the array element being referenced. Using the latter way to reference an array index is simpler when you're using a program to read or write the individual elements of the array.

In Listing 7.10, the property mapProperty exposes a Map type. As with the array type, the class BeanUtils can manage the Map type. Listing 7.13 shows how to access a mapped data type.

Listing 7.13
start example
 BeanProperties bean = new BeanProperties(); String value = BeanUtils.getMappedProperty( bean, "mapProperty(first)"); value = BeanUtils.getMappedProperty( bean, "mapProperty", "first"); 
end example
 

In Listing 7.13, the mapped property is accessed using a similar notation as we saw in Listing 7.12. The mapped property is accessed using the method getMappedProperty , which is shown in two different examples. In the first example of using the method getMappedProperty , the index of the map is accessed using a bracket notation. The notation is very similar to the array notation. And like the array notation, the index notation does not allow for extra spaces between the bracket and the identifier.

The second example of using the method getMappedProperty is similar to referencing an individual array index like shown in Listing 7.12. The second parameter of the method getMappedProperty represents the identifier of the mapped property, and the third parameter represents the key of the mapped value that should be retrieved. As with referencing the array index, using the latter approach to referencing a map is simpler in a programming context.

It is important to remember the title of this section ( Accessing Arrays and Maps ) when you use code demonstrated from this section. The title started with the word "accessing," which means that the techniques shown are good for extracting values. The techniques shown are not very helpful when you're transferring data from one bean to another source.

Converting Data Types

The class BeanUtilsBean uses conversion routines that convert a data type from one to another. In all of the listings so far in this chapter, the conversion happened automatically. To do manual conversions from one data type to another, we can use the class ConvertUtils . Listing 7.14 is an example where a string is converted into an integer.

Listing 7.14
start example
 String strValue = "1234"; Object value = ConvertUtils.convert( strValue, Integer.TYPE); assertTrue(value instanceof Integer); int val = ((Integer)value).intValue(); 
end example
 

In Listing 7.14, the method convert is used to convert data from one type to another. The first parameter to the method convert is the value to be converted. The second parameter to the method convert represents destination conversion type. In Listing 7.14, the second parameter is the type Integer.TYPE , which is a class identifier for the type int . The second parameter of the method convert is the class Class . In addition, in Listing 7.14 the variable value is tested to see if it is a class of type Integer , which in this case would be correct. The variable value is typecast to a primitive of type int and the method intValue is called to retrieve an int value.

It's a bit odd to use the class Class to identify the destination data type because we could have used an enumeration. However, by using the class Class , which every object references, it is easy to remember conversions. This solution also makes it simpler to extend the class ConvertUtils because the unique identifier is a class and not numeric identifier or string identifier. The standard conversions available include int , Integer , long , array , and other standard Java data types.

Creating a Custom Converter

In Listing 7.15, a custom conversion destination is defined.

Listing 7.15
start example
 Object beanValue = ConvertUtils.convert( objValue, BeanToWrite.class); assertTrue(beanValue instanceof BeanToWrite); 
end example
 

Listing 7.15 calls the method convert. The difference between Listing 7.15 and Listing 7.14 is that here the second parameter is the class identifier of the class BeanToWrite . The idea is to convert a String into the class BeanToWrite . With the code in Listing 7.15 as is, when the method convert returns the variable, beanValue does reference a valid object. In the default case, the assert method call will fail because the variable beanValue will not reference a class of type BeanToWrite. This is because the conversion routines do not have a converter destination for the class BeanToWrite . In that case, the class ConvertUtils will use a default string conversion.

To get around this problem, the developer would have to write a custom converter based on the interface Convert, as shown in Listing 7.16.

Listing 7.16
start example
 class BeanToWriteConverter implements Converter { public Object convert(Class type, Object value) { try { if( value instanceof String) { BeanToWrite bean =  (BeanToWrite)type.newInstance(); bean.setStringValue((String)value); return bean; } } catch( Exception ex) { throw new ConversionException( "Attempted conversion, but failed cause " + ex.toString()); } throw new ConversionException( "BeanToWriteConverter cannot convert type " + type.getName()); } } 
end example
 

In Listing 7.16, the class BeanToWriteConverter implements the interface Converter , which has only one method convert. Considering the implementation details, it would appear that the conversion is very simple to implement. However, quite a bit more is going on. The method convert has two parameters and expects a return value. The first parameter type is the class descriptor passed in originally to the class method ConvertUtils.convert . The second parameter value is the object value passed to the class method ConvertUtils.convert .

The fact that the class descriptors and value are passed in is important. This means when that when you perform a conversion, the converter class and descriptor class are loosely coupled . Consider the class name BeanToWriteConverter , which would indicate that all conversions would result in an object instance of the type BeanToWrite . Therefore, a small subsection of Listing 7.16 could have been rewritten as in Listing 7.17.

Listing 7.17
start example
 if( value instanceof String) {  return new BeanToWrite( 0, (String)value); } 
end example
 

Listing 7.17 is simpler and more concise than the same lines shown in Listing 7.16, but ConvertUtils wants to avoid that conciseness. The objective of implementing the interface Converter is to be able to provide conversion groups when more complex objects are involved. In the ideal case, the class BeanToWrite in Listing 7.17 would be an interface, and the class descriptor passed in would be some class implementation. The class ConvertUtils is giving the end developer the maximum amount of flexibility, which is always a good thing.

Going back to Listing 7.16, the conversion desired depends on the if statement that sees which object type the second parameter value is. In the case of Listing 7.16, the only object type that is supported is the class String . If the object type is a string, then the conversion begins by instantiating the destination class type and assigning the property stringValue . The instantiated object type is then returned to the caller.

If the conversion type is not supported, then the interface Converter implementation must throw a ConversionException exception to indicate that something went wrong. A null object should not be returned.

Listing 7.18 uses the custom converter.

Listing 7.18
start example
 String strValue = "1234"; ConvertUtils.register( new BeanToWriteConverter(), BeanToWrite.class); Object beanValue = ConvertUtils.convert( strValue, BeanToWrite.class); assertTrue(beanValue instanceof BeanToWrite); 
end example
 

Listing 7.18 looks very similar to Listing 7.15, except that an additional method is called. The additional method register is used to register the converter class and associate it with a class descriptor, which for these examples would be the class BeanToWrite . Once the registration has been completed, the remaining lines after the register method call in Listing 7.18 are identical to Listing 7.15. Then, the assertion to test the variable beanValue , which is an instance of the class BeanToWrite, would not fail, as it did in Listing 7.15.

It would seem to be a good thing to be able to convert an object from one type to another. And generally it is a good and necessary thing. However, the implementations have to be mapped out carefully . Writing converters should not be approached willy-nilly or as needed because the wrong converter could be called at the wrong time. When writing converters, a developer should consider which combinations of conversions could occur. Once the combinations have been defined, they should be implemented, and if a mapping cannot be performed, an exception should be generated. In this case, exceptions are good things because a conversion should fail loudly; it shouldn't be silently converted into a string because that could mask potential bugs .

Calling Constructors Dynamically

It would seem that calling constructors using reflection should not be such a difficult thing. And generally it is not, if you know what you are instantiating. More often than not, though, especially when you're developing more complex frameworks, it's not always obvious what you are calling. This could be due to things like legacy code. The Beanutils package's class ConstructorUtils helps the developer call constructors that instantiate objects. The interesting thing about the class ConstructorUtils is that it helps the developer figure out what he wants to call. Listing 7.19 shows a sample object instantiation.

Listing 7.19
start example
 BeanToWrite bean = (BeanToWrite)  ConstructorUtils.invokeConstructor(  BeanToWrite.class, null); 
end example
 

In Listing 7.19, the class ConstructorUtils has the method invokeConstructor , which has two parameters. The first parameter, BeanToWrite.class, represents the class descriptor used to instantiate the class. The second parameter, null, represents the constructor parameters, which in Listing 7.19 means call the constructor without any parameters. When the method returns the object instance, a type cast converts the object instance to the class BeanToWrite . At that point, you can use the object instance as you do an object instance that's instantiated using the new keyword.

Going back to the BeanToWrite constructor in Chapter 5, we used a second constructor that looked similar to Listing 7.20.

Listing 7.20
start example
 public BeanToWrite( int ival, String sval) { _iValue = ival; _strValue = sval; } 
end example
 

In Listing 7.20, the constructor has two parameters, an int and a String value. To be able to call this constructor, the other variant of the method invokeConstructor has to be called, as demonstrated in Listing 7.21.

Listing 7.21
start example
 Object args[] = new Object[ 2]; args[ 0] = new Integer( 9876); args[ 1] = "goodbye"; bean = (BeanToWrite)ConstructorUtils.invokeConstructor( BeanToWrite.class, args); 
end example
 

In Listing 7.21, you specify constructor arguments by using an array of Object objects. The constructor in Listing 7.20 requires two arguments, which in Listing 7.21 are allocated as an array of the class type Object. The first array element is an Integer class instance. This is necessary even though the constructor in Listing 7.20 is the primitive int . The Integer class instance is converted to the primitive one. The second array element is a string, which is the second parameter of the constructor in Listing 7.20. Then, in Listing 7.21, the method invokeConstructor , where the third parameter represents the constructor arguments, is called. An instance to the class BeanToWrite will be returned.

The power of the class ConstructorUtils is not that it simplifies calling and instantiating classes, but that it provides you with flexibility. When you use the method invokeConstructor , the class ConstructorUtils will attempt to find the best-matched constructor. It is also possible to exactly match the constructor arguments using the method invokeExactConstructor . Invoking this method can be at times too picky when you're choosing constructors and, when a constructor cannot be found, errors will be generated even though a constructor should have been found. For most cases, though, invokeConstructor will be used.

After all is done and said, there is one final catch-22. If the class that is being instantiated does not have an explicit constructor, then the method invokeConstructor will fail with the error that it could not find a constructor. We should state here that the invokeConstructor is not a replacement for a factory.

Calling Methods Dynamically

When the method invokeConstructor is called, the underlying implementation searches for a specific method, which is a specific constructor. The specific constructor has to match the calling sequence specified by the user . The class MethodUtils is used to find the closest -matching constructor. The class MethodUtils can also be used to invoke methods dynamically, much like the class ConstructorUtils . Listing 7.22 shows the class MethodUtils .

Listing 7.22
start example
 BeanToWrite bean = new BeanToWrite( 1234, "hello"); MethodUtils.invokeMethod( bean, "setStringValue",  "goodbye"); 
end example
 

In Listing 7.22, the class MethodUtils has the method invokeMethod , which has three parameters. The first parameter, bean , represents the object instance, where the method will be called. The second parameter, " setStringValue" , is a string that represents the name of the method that will be called. The last parameter, "goodbye" , is the parameter used to call the method. If the method had multiple arguments, then the arguments would be created in the same way as shown in Listing 7.21 for the multi-parameter constructor.

Now we need to consider two other issues: overloaded methods and static methods. Overloaded methods are similar methods with different parameters, whereas static methods are methods that do not require an object instance. As in the constructor calls, the Bean-utils package will find the method that most closely matches the method to be called. When a method is overloaded, some odd things could happen, as shown in Listing 7.23.

Listing 7.23
start example
 public void overloadedMethod( int val) { System.out.println( "Overloaded method with int"); } public void overloadedMethod( Integer val) { System.out.println( "Overloaded method with Integer"); } 
end example
 

Going back to Listing 7.21, the arguments to the constructor or method are stored in an array of objects. Primitives cannot be stored in object arrays, so to get around the problem, the class Integer is used. However, in Listing 7.23, there is a method that uses both the class Integer and the primitive int . Now there is a dilemma, because the method overloadedMethod with the primitive int will not be accessible using the method invokeMethod . There is no solution other than retrieving the specific method manually and then calling that method explicitly (we will discuss how to do this shortly). If we wanted to call the overloaded method with the primitive, we would have to change the other method with the class Integer to another data type like Long .

Calling static methods can also cause a bit of confusion, as shown in Listing 7.24.

Listing 7.24
start example
 public static void staticMethod() { System.out.println( "Static method"); } 
end example
 

In Listing 7.24, the static method is just another method except that the keyword static is used. This means that the method staticMethod does not require an instance when the method is called. Therefore, in the simplest case, Listing 7.25 can be used to call the method.

Listing 7.25
start example
 BeanMethods methods = new BeanMethods(); MethodUtils.invokeMethod( methods, "staticMethod", null); 
end example
 

Looking at Listing 7.25, it is obvious that everything will work. However, what is also obvious is that it might not be the most efficient piece of code. Ideally, a static method, like a static method where the class does not have to be initialized , should be called. The solution lies in Listing 7.26.

Listing 7.26
start example
 Method method =  MethodUtils.getMatchingAccessibleMethod( BeanMethods.class, "staticMethod", new Class[ 0]); method.invoke( null, null); 
end example
 

In Listing 7.26, the class method MethodUtils.getMatchingAccessibleMethod is used to retrieve the static method from the BeanMethods class descriptor. Returned is a Method class instance. The class Method is part of the java.lang.reflect package. Next, the method invoke is used to call the static method. There are two parameters to the method invoke . The first parameter is the object instance, which in Listing 7.26 is null . The second parameter is the array of objects used as arguments, which is also null because the static method has no arguments. The method getMatchingAccessibleMethod is used to retrieve the hidden overloaded method.

Using DynaBeans

One of the last interesting bits about the Beanutils package is the interface DynaBean . A DynaBean is a bean that acts like a property bag. A DynaBean is not like a coded bean, but is more like a class that has a number of methods to store key value pairs. In earlier chapters, we used the DynaBean interface as part of another Commons package. Some may question why there is a need for something like a DynaBean when there are Map implementations like HashMap . The answer was given in the Commons mailing list, available at the Apache Web site ( http://commons.apache.org ), and it was for simplicity and ease of use. DynaBeans support setters and getters, and that is about it. There is no logic involved. Listing 7.27 shows an example of using a DynaBean .

Listing 7.27
start example
 DynaClass dynaClass = new BasicDynaClass( "MyClass", null, new DynaProperty[] { new DynaProperty("stringValue", String.class) }); DynaBean dynaBean = dynaClass.newInstance(); dynaBean.set( "stringValue", "hello"); System.out.println( "Greeting is " + dynaBean.get( "stringValue")); 
end example
 

The Beanutils package contains the basic DynaBean class called BasicDynaBean and the factory class BasicDynaClass, as are illustrated in Listing 7.27. To create a DynaBean interface instance, the BasicDynaClass is instantiated and described using a number of properties called DynaProperties . The properties identify the supported properties that can be set and retrieved from the DynaBean instance. The method newInstance instantiates a new DynaBean . To assign and retrieve properties, the methods set and get are used, respectively.

Listing 7.27 looks entirely uninteresting because the same can be achieved with the class HashMap . In fact, if we look at the implementation of BasicDynaBean , we see that a hash map is used. So, the question still persists why we use a DynaBean . To understand the DynaBean , let's focus on Listing 7.28, which is a small piece of Listing 7.27.

Listing 7.28
start example
 dynaBean.set( "stringValue", "hello"); System.out.println( "Greeting is " + dynaBean.get( "stringValue")); 
end example
 

Listing 7.28 uses only the interface DynaBean . This means that properties from the DynaBean are set and retrieved using the methods set and get. The class BeanUtils does the same thing, except more typing is involved. DynaBean comes into its own when you use the class WrapDynaBean like in Listing 7.29.

Listing 7.29
start example
 BeanToWrite bean = new BeanToWrite( 1234, "hello"); DynaBean dynaBean = new WrapDynaBean( bean); 
end example
 

In Listing 7.29, the class BeanToWrite is instantiated. Then, the class WrapDynaBean is also instantiated, and the constructor parameter is the instance of class BeanToWrite . The returned value is an interface instance to DynaBean . Looking at Listing 7.28 again, we'll notice that something really interesting has occurred. Listing 7.28 was initially used to set and get properties from a BasicDynaBean instance defined in Listing 7.27. However, now with Listing 7.29, the exact same DynaBean code is used to retrieve and set properties in an actual coded bean. The difference is that Listing 7.28 does not care where the DynaBean comes from.

In other words, we have applied the Commons Bridge pattern. This is the power of the DynaBean , which we can use to define beans regardless of the origin of the data. The data could be XML, SQL, another bean, or simply some coded routine. In all cases, the consumer of the DynaBean sees the exact same thing. This could have been applied using a Map as well, except that a Map has many more methods to implement and the logic is more complicated. In addition, a Map is intended for collections, whereas a DynaBean is intended to represent a bean of some sort . The DynaBean interface is simple and solves the problem at hand.




Applied Software Engineering Using Apache Jakarta Commons
Applied Software Engineering Using Apache Jakarta Commons (Charles River Media Computer Engineering)
ISBN: 1584502460
EAN: 2147483647
Year: 2002
Pages: 109

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net