Monday, July 13, 2015

A tryst with dozer custom converters

My experiments with dozer

Introduction

The object to object mapping activity happens to constitute a significant part during a front tier services hand-off of a request to the down-stream layers (be it DAO or another service). The major overhaul comes across as a substantial pain of using naive source getters and target setters in building the target objects of interest and confounds the developer and the maintainer alike. The often repeated transformations (during the mapping) such as formatting, composing, splitting, target attribute/field substitution are much left to the user's wisdom to handle in some non-descript isolated pockets of code. An ubiquitous example could be a phone number, or an address where different flavors of structures exist to cater to different locale. For instance in one flavour the road and the block name/numbers are captured in fields called address1 and address2 while an another flavor refers the same as street1/line1 and street2/line2 respectively. Further a state field in the address object can be referred to as province and zip can be substituted for pin.  

Intent

The intent of reminiscing on this article and the work underneath it came about when this above stated problem started quickly getting out of hand in a recent situation at work.It was when the number of different objects to be mapped to one or the other type started to exceed beyond handful (say more than 25) where the semantics of the source and target objects remained same however the field names started out to be different(just like address1 and street1). It was an occasion where a classic remote proprietary service needed to pass a request object to a target type with many attributes synonym-ed to a different names. 

The first thing that crossed my mind was to keep off from the lazy habit of jumping on the bandwagon of getters and setters(these days usually any half-good POJOs comes at-least with the gets, sets and default no-arg constructors without much effort). Ofcourse I am not whining at all on the POJO principles here; but just felt that life is too short for coding with POJO gets/sets and thus desired to have a bit more atop that can take care of mundane stuff in a declarative fashion than to just code. Infact this is the theme that is closer to my heart in embracing the project lombok annotations.

The real intent / reason on this article is by no means a rant on bean-bean mappers but as is explained further in the use cases section it is more about a few special situations that called in for a slightly general solution to be built forming the crux of this article. Please read further to know more on this.

Why dozer

Thus dawned a bright occasion on a winter morning of November 2014; where we decided to do some soul searching and figure out whats the best tool that is worth its salt for fitting the purpose we had. Tools / libraries such as commons convert from apache commons and spring libraries were looked upon as first choices for those libraries usually form the motherboard of many of the projects that we build. While the main aim of this transformation was about developer simplicity in not having to code and keep it mostly declarative; the approach was not desired to be entirely bereft of the coding. 

So while commons / spring approaches needs still a fair amount of hand cranked code for every conversion type; the search continued on beyond the comfort zone of apache commons, spring . The web has many articles of comparison on bean-bean mapping and  thus we will not go about comparing one library against another here. However on one such occasion when we chanced upon dozer; it sounded promising with its impressive feature set such as declarative xmlized way of defining the mapping and a great hook for plugging in custom converters that can be sewn together by using spring beans. After a quick check within organization for the yearned buy in; we quickly set out to find out the various bells and whistles it offered. 

Use-cases

Most of the mundane mappings are handled by simplistic sensible defaults applied by dozer configurations.  For instance the declarative xml pieces for a mapping did'nt needed any explicit mention of fields that are commonly available in source and target classes. Readers are encouraged to refer web articles on dozer to glean information. However, there were some peculiar use-cases that were frequented by us that couldn't be found in either dozer documentation or web and essentially are the reasons for opening this blog post.

These specific use cases were usually related to

1.     An immutable/constant object or value to be substituted to a target field
2.     In another case ; the target objects had one nagging pattern in that many of the fields such as address, phone were always requiring to be of list type(POJO generators for these third party target objects were designed to emit these fields as list and hence we had no choice). This usually meant such target fields not only had to be mapped but also need to be encapsulated to a list.

These specific use cases that needed building different custom converters purposed for as follows:
1.     A field of target class that needs to be substituted by a type specific constant (mostly the primitives and date). Here the particular target field is simply substituted with a constant.
2.     A field of target class that needs to be substituted by a pre-defined bean in the spring beans xml. Here the particular target field is a complex custom type which is a bean defined with appropriate values for its attributes.
3.     A target field that needs to be populated as java collection type (for now List type is only supported). Here the source bean needs to be converted to a target type and further added as an element into a collection of the target type.

Implementation

A custom converter in dozer provides the following configurable keys (amongst many others) which could be utilized for the purpose at hand.
1.     custom-converter-id : This is bean id straight out of a bean definition in the spring based beans.xml that is used in the project.
2.     custom-converter-param : This is basically a helpful hint or a string parameter which is set post construction of the converter

A first implementation of the primitive converters, pre-defined bean substitutors and a source to target list mapper can be found in my github repository : dozer-experiments.

The first step in before building up the solution was to firm up the project based on spring and get up quickly with a workable beans.xml for the purpose. 

Note:The rationale for adding beans.xml and even dozer bean configuration files to src/test/resources and primarily making test package as the go-to source for understanding is to keep the actual main classes light since the usage and configuration of converters can become very contextual. Basically the beans or dozer files primarily are data driven and are best served with test data in a demo environment rather than making them as part of main jar file. The readers are encouraged to understand READMEtest package, configuration samples and accordingly make up their configurations.

A note on content of beans.xml
Normally the spring based project i write would usually require some quintessentials listed below.
1.     Need my beans xml to be sleak and hence require spring p-namespace and c-namespace.These allow p:<property-name> and c:<attribute-name> in the bean definition as an xml attribute.(half sanity may survive just because of this itself :) - as otherwise beans.xml too busy!).
2.     Spring Annotation Processors(@PostConstruct etc) are usually made use of along with a few defalt values used from bean-default.properties so the following snippet.(I dont like repeating package names or base path names which increases the breadth and/or height of xml)
3.     Using Slf4j logger (Please refer to logback.xml for log level and formats) and dozer related configurations in the beans.xml as follows.
<bean
class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"
scope="singleton" />
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
scope="singleton">
<description>This is a common properties file that may declare
repetatively usable strings, constants and class/package names etc
</description>
<property name="location" value="bean-default.properties" />
 </bean>
<bean id="system-properties-setup"
 class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
 p:targetObject="#{@systemProperties}" p:targetMethod="putAll">
<property name="arguments">
<util:properties>
<!-- <prop key="java.util.logging.SimpleFormatter.format">%1$tF %1$tT
[%4$s]: %5$s %n</prop> -->
<prop key="dozer.configuration">dozer.properties</prop>
<prop key="dozer.debug">false</prop>
</util:properties>
</property>
</bean>


The rest of the beans project / problem specific:
1.     Converters of primitive type: These deal with turning a string parsable value to a specific primitive type such as int, long, short etc. It can also support for date today with an assumed simple date format. These as it sounds are going to substitute a value specified in (as part of dozerBeanMapper.xml) the attribute custom-converter-param. The converter itself is as specified in custom-converter-id.  Please refer to README for details.
2.     Converters to substitute a pre-defined target bean : A fully defined target bean can act as a stand in while mapping some fields. 
3.     A source to target list converter: It is self explanatory.
4.     The dozer mapper definition is completely done in beans xml. so its imperative to use this than create some default way for the mapper. 
Most of the bean ids have self explanatory meaning and hence will not be explaining all beans here.

While there exists a good reference on bean mapping within the dozer user guide; here an attempt is made with a small snippets xml to explain the solution for the use cases addressed. The example objects/classes for conversion are declared in dozer bean mapping file housed in the examples package for demonstration purpose.

For instance ;a quick way to get up with an use of substitution of constant values is as follows; where the custom-converter-param has the actual value to be converted to desired primitive type.

<mapping map-id="source-to-target-with-few-constants">
  <class-a>com.github.venkateshamurthy.dozer.converters.examples.SourceHolder</class-a>
  <class-b>com.github.venkateshamurthy.dozer.converters.examples.TargetHolder</class-b>
  <field map-id="source-to-target"><a>source</a><b>target</b></field>
  <!-- the custom-converter-ids are primitive converters but valueas are strings to be converted -->
  <field custom-converter-id="to-date" custom-converter-param="1947-08-15"><a>dob</a><b>dob</b></field>
  <field custom-converter-id="to-int" custom-converter-param="24"><a>age</a><b>age</b></field>
  <field custom-converter-id="to-double" custom-converter-param="500000.0"><a>salary</a><b>salary</b></field>
  <field custom-converter-id="to-long" custom-converter-param="100300500700"><a>uan</a><b>uan</b></field>
 </mapping>

Where as a pre-defined bean substitution can be had by using the following snippet:

<mapping map-id="source-to-substitute-target">
  <class-a>com.github.venkateshamurthy.dozer.converters.examples.SourceHolder</class-a>
  <class-b>com.github.venkateshamurthy.dozer.converters.examples.TargetHolder</class-b>
  <!-- the custom-converter-param values are prefixed here with bean:bean-name as the target bean for substituting must be defined beans.xml -->
  <field  custom-converter-id="source-to-target-pre-defined" custom-converter-param="bean:pre-defined-target">
   <a>source</a> <b>target</b>
  </field>
 </mapping>

Note: The custom-converter-param has a mandatory prefix bean:  and suffixed with an actual bean id from the bean xml representing the pre-defined target bean. This structural constraint of prefix bean: is mandatory and is a personal convention

The other type is about source to target list type and requires a proper mapping to exist between a singular element of source class to target class. The snippet as follows as an example:

<mapping map-id="source-to-target-list" > 
  <class-a>com.github.venkateshamurthy.dozer.converters.examples.SourceHolder</class-a>
  <class-b>com.github.venkateshamurthy.dozer.converters.examples.TargetListHolder</class-b>
  <!-- the converter is source to a target list and custom-converter-param values are prefixed here with map-id: as the singular element mapping is required in dozer bean mapping definition -->
  <field  custom-converter-param="map-id:source-to-target" custom-converter-id="source-to-target-list">
    <a>source</a> <b>targets</b>
  </field>
 </mapping>

Note: The custom-converter-param has a prefix map-id: and suffixed with an actual mapping id from the dozerBeanMapping xml (or some included dozer mapping config xml file). This basically makes sure to apply the correct mapping between singular element of source and target class and then subsequently return a list wrapping/including the singular target element. This structural constraint of prefix map-id: is mandatory and is a personal convention.

Usage:

In this section a few test code snippets are mentioned as follows. The reader can take que from this in order to make use of the new converters proposed here.

/** test a predefined target substitution */
 @Test
 public void testPreDefinedTargetSubstitution() throws NoSuchFieldException,
   SecurityException {
  SourceHolder sourceHolder = ctx.getBean("source-holder",
    SourceHolder.class);
  TargetHolder targetHolder = mapper.map(sourceHolder,
    TargetHolder.class, "source-to-substitute-target");
  Target expectedTarget = ctx.getBean("pre-defined-target", Target.class);
  assertEquals(expectedTarget, targetHolder.getTarget());
 }

 /** test a field (of type date/int/long etc) for a constant substitution */
 @Test
 public void testTargetWithFewConstants() {
  SourceHolder sourceHolder = ctx.getBean("source-holder",
    SourceHolder.class);
  TargetHolder targetHolder = mapper.map(sourceHolder,
    TargetHolder.class, "source-to-target-with-few-constants");
  log.info(targetHolder.toString());
 }

 /** test a singular element to a destination list type */
 @Test
 public void testTargetList() {
  SourceHolder sourceHolder = ctx.getBean("source-holder",
    SourceHolder.class);
  Source source = sourceHolder.getSource();
  TargetListHolder targetHolder = mapper.map(sourceHolder,
    TargetListHolder.class, "source-to-target-list");
  List<Target> targets = targetHolder.getTargets();
  assertFalse(targets.isEmpty());
  assertTrue(targets.size() == 1);
  Target target = targets.get(0);
  assertEquals(source.getAddress1(), target.getStreet1());
  assertEquals(source.getAddress2(), target.getStreet2());
  assertEquals(source.getPhone(), target.getPhone());
  log.info(targetHolder.getTargets().toString());
 }

While test code helps in understanding the usage; a good look at the actual converter classes in the main package helps deepen the understanding.

Summary:

In summary, though a set of peculiar use cases of bean to bean mapping are discussed within a context, it is largely felt that the general solution discussed here seems to be useful in a variety of similar situations arising in different contexts. This set of converters are set only to grow with different levels of customizations /enhancements applied to it . Thus I request earnestly all the readers to express their opinion/reviews on this to make this article and code a better experience for the users.



No comments:

Post a Comment