commons-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Carlos Cajina" <cecaj...@hotmail.com>
Subject [BEAN UTILS] mini HOW-TO for dummies
Date Thu, 18 Nov 2004 16:27:36 GMT
Hi. After day and a half struggling to grasp the use of commons-beanutils regarding (automatic)
data type conversions I finally came up with what I'd like to think of as a good starting
point for those who -like me- start from zero. Before diving into code examples I'd like to
share what made me look into commons-beanutils for a solution:

* Since I'm using Struts in my current J2EE project one of my concerns was to find a way to
transfer data between the presentation layer and the data layer. From the Struts part I have
ActionForms and from the data persistence part I have Value Objects and CMP Entity Beans.

* According to Ted Husted's "Struts in Action" (2003, Manning) there are several strategies
to transfer values between tiers. I was using one that relies on "factory methods" ("A helper
method encapsulates, instantiates, and populates a business tier bean."), but including such
methods in my ActionForms to instantiate, populate, and return a specific value object brought
along (at least to me) messy code to handle data type conversions and validations, moreover,
as Ted points it "This strategy binds the ActionForm to the business-tier type". Of course
there are advantages to this approach, but the use of reflection -as I hope you'll see- is
a cleaner and safer path, and introduces very little overhead.

* For the sake of simplicity in my ActionForms I only have string properties -even though
users "see" and save String, Date, Integer, Boolean, Timestamp, and Float values. Considering
that the BeanUtils methods can convert data between Strings and native types this seemed like
a good decision. If you have "exotic" data types,s you'll have to implement "bridge methods"
to keep data type conversion transparent (I'll show a rather simple example later)

Having described some of my scenario, here's what I did:

1. Following commons-beanutils JavaDocs' two-step recipe, I wrote a class that implemented
the Converter interface. Within this class the convert() method should accept theclass that
you want to convert to, and a String representing the incoming value to be converted. I had
to go through this first step because from the user perspective dates are handled in dd-mm-yyyy
format but they go into the database as an SQL DATE value, using yyyy-mm-dd format. To implement
the Converter interface I kind of cheated because I grabbed commons-beanutils source code
and peek into the default SqlDateConverter class code (written by Mr. Craig R. McClanahan)
What I did next was re-implement the convert() method to add code within the try to "reverse"
the date format, like this:

int year,month,day;
String userDate = (String) value;
            
year = Integer.parseInt(userDate.substring(6));
month = Integer.parseInt(userDate.substring(3,5));
day = Integer.parseInt(userDate.substring(0,2));
            
GregorianCalendar dbDate = new GregorianCalendar(year, month-1, day);
                        
Date correctDate = new Date(dbDate.getTimeInMillis());
            
return (correctDate);

NOTE: there are default converters that use Locale objects, if you need something more refined.

2. The next step in the recipe is to "register an instance of your converter class by calling
the ConvertUtils.register() method". It is suggested that this be done at application startup
time, and since I already had a ServletContext Listener I just added the following lines in
the contextInitialized method:

appContext.log("Registering MyOwnSqlDateConverter..."); 
ConvertUtils.register(new MyOwnSqlDateConverter(),java.sql.Date.class);
appContext.log("MyOwnSqlDateConverter registered...");

As you can imagine, for every String to java.sql.Date conversion that takes place in the application
the converter to use will be the one I'm providing in step 1 and registering in step 2.

3. Finally, to populate ActionForms and create Value Objects you should add some simple methods:

    public void populateFormFromValueObjectData(Object vo) throws Exception
    {
        try {
            BeanUtils.copyProperties(this, vo);
        } catch (Throwable t) {
            throw new PopulateSolicitudDataException(t);
        }
    }

and

    public SomeValueObject getSomeValueObjectFromFormData() throws Exception
    {
        SomeValueObject svo = new SomeValueObject();

        try {
            BeanUtils.copyProperties(svo, this);
        } catch (Throwable t) {
            throw new PopulateSolicitudVOException(t);
        }

        return svo;
    }

In both cases, "this" references the ActionForm. In the first method, BeanUtils.copyProperties(this,
vo) copy property values from the origin bean (vo) to the destination bean (this=ActionForm)
for all cases where the property names are the same, doing all the necessary data type conversions
under the hood; and the same applies for BeanUtils.copyProperties(svo, this).

Doing things this ways allows you to transfer data between tiers without binding your form
to the business tier, in fact, you don't even have to know the details of the data transfer
objects (value objects) to get data from them and send data through them. Mr. Husted refers
to this kind of felxibility as "round-tripping ActionForms and business beans".

One of my actual ActionForms has to gather a lot of data (or is it datum? sorry, not very
good in english grammar) using wizard like screens, but this information doesn't go to the
same places in the database. I create three different types of Value Objects and obtain information
from those three types with just a few lines of code and without caring about any data type
conversions. This is one step closer to heaven for me! :^)

I hope this serves you as a good and practical introduction to part of the commons-beanutils
power ;^)  If any questions arise or you come up with better examples and/or corrections,
please share them!

Regards,

    Carlos

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message