Copying data from Struts ActionForm to ValueObject: Easing the pain
Enterprise applications are all about data manipulation. Data flows through the system from one tier to another.
Consider a typical J2EE application built using Struts. The user fills out the HTML Form. The data makes its way into a Action Form. The they are copied into Value Objects using Value Object Assemblers (or DTO Assemblers). Then the data is copied into Entity EJBs (if used) and are persisted. The whole process is reversed when user retrieves data.
Traditionally value object assemblers are hand coded. More recently Commons BeanUtils has been used to simplify this tedious and errorprone task of copying data from one object to another. Remember BeanUtils.copyProperties()... However, BeanUtils have some important limitations.
BeanUtils can automatically copy (and convert) properties between objects, when the names of the properties are same between source and target objects. In large projects, this happens a lot as the web tier objects do not match persistence objects in both name and structure simply because they were developed by different groups.
BeanUtils package provides PropertyUtils class that can copy any property between source and target. However you have to explicitly invoke PropertyUtils.getProperty() and PropertyUtils.setProperty(). This works very well in many scenarios (like generic frameworks for example.), but doesn't work when you have to copy data between objects in the absence of mapping metadata. This would be almost like coding target.setXXX(source.getXXX()).
BeanUtils (actually ConvertUtils class) can only convert from String to primitives (such as int, boolean) and basic java.lang objects (such as Integer, String) and vice versa. and nothing else. If you want to convert from java.sql.Timestamp to long (say) or foo.bar.Blah to java.util.Date, you simply cannot. The only extensibility in BeanUtils is to write custom converter from String to foo.bar.Blah and vice versa.
If you are facing a scanarios like one of the following a new framework called OTOM (stands for Object To Object Maping - pronounced as Autumn :-D ) can be a big help.
- Source and target objects have different names and structures
- You have to convert data from and to arbitrary data types
- You want to map data conditionally
Like all of us, I have faced this problem. Being the lazy person that I am, I didn't do anything about it until recently. The truth is that such a need at one of my client scratched my itch.
And so, last week, I rolled out OTOM 0.5. As is evident, it is not complete yet but has most of the features for helping in the above mentioned scenarios. So, how does OTOM work? It is pretty simple actually.
OTOM has a Swing based GUI that allows you to graphically map properties from a source class to target class. Variety of mappings are allowed - Direct Mapping, Type Conversion Mapping, Collection Mapping (to be added) and even Conditional Mapping. The GUI generates a XML mapping file that stores the conversion metadata. The GUI looks as follows:
The mapping metadata is stored in xml format. OTOM uses the concept of repositories to store the metadata. A repository consists of multiple Class Mappings. A Class Mapping stores the mapping information from a source class to a target class. Each ClassMapping has a collection of PropertyMapping correpsonding to mapping of JavaBean properties in the source class to those in the target class. The mapping can be conditional too. All thise can be done through the GUI itself. Writing the GUI as an Eclipse Plugin is still in my radar, but given that my immediate priority was to provide IDE independent GUI, I chose Swing.
The source code for the mapping can then be generated using an Ant task provided. The actual generation is done via Velocity templates. One of the nice features is that the generated code has absolutely no dependency on the OTOM framework. The generated code also does not use reflection as the mapping metadata helps me avoid it.
So, the next time you have to copy data from one object to another, think if you can use OTOM. Typical usages for OTOM include data mapping from Struts ActionForm to ValueObject (or POJO), JAXB generated objects to POJOs and so on. If you have suggestions for improvements, please post it in the project forums.
Spring break might be just a week away, but it still feels like OTOM to me ;-D. But then hey, I am from Austin, TX and there is no such weather as winter down here.