Friday, May 14, 2010

Mapping Immutable Value-Objects with Dozer

2 good things happened to me this week (well actually 3, but I will probably blog about the 3rd one later):

The first thing is that I finally managed to convince Dozer to map immutable objects, and the second one is that I found something interesting to blog about ;)

I mentioned in a previous post about Dozer’s lack of support for constructor arguments. In general Dozer is aimed at supporting JavaBean to JavaBean mapping, and other usage scenarios seem to be hard to implement. It turns out that the problem can be solved using design-patterns, and a little bit of trickery.

The first step towards the solution, is introducing the Builder Pattern. Actually a form of the Builder Pattern that introduced by Joshua Bloch at Java One. The pattern solves the problem of too many constructors, too many constructor arguments, and the verbosity of object creation while using setters. The pattern is described in detail here: http://ow.ly/1L2JV.

Let’s suppose we are about to map an Address JavaBean to an Immutable Address object. Here are the Address classes:

public class Coordinate {
 private double longitude;
 private double latitude;
 // getters, setters, c'tors, equals(), hashCode(), toString(), etc...
}
public class Address {
 private String country;
 private String state;
 private String city;
 private String street;
 private String zipcode;
 private Coordinate coordinate;
 // getters, setters, c'tors, equals(), hashCode(), toString(), etc...
}

And here’s the immutable address:

public class ImmutableCoordinate {
 private final double longitude;
 private final double latitude;
 
 private ImmutableCoordinate(Builder builder) {
  this.latitude = builder.latitude;
  this.longitude = builder.longitude;
 }

 public double getLongitude() {
  return longitude;
 }
 
 public double getLatitude() {
  return latitude;
 }
  
 public static class Builder {
  private double longitude;
  private double latitude;
  
  public Builder longitude(double longitude) {
   this.longitude = longitude;
   return this;
  }
  
  public Builder latitude(double latitude) {
   this.latitude = latitude;
   return this;
  }
  
  public ImmutableCoordinate build() {
   return new ImmutableCoordinate(this);
  }
 }
}
public class ImmutableAddress {
 private final String country;
 private final String state;
 private final String city;
 private final String street;
 private final String zipcode;
 private final ImmutableCoordinate coordinate;
 
 private ImmutableAddress(Builder builder) {
  this.country = builder.country;
  this.state = builder.state;
  this.city = builder.city;
  this.street = builder.street;
  this.zipcode = builder.zipcode;
  this.coordinate = builder.coordinate;  
 }

 public String getCountry() {
  return country;
 }
 
 public String getState() {
  return state;
 }
  
 public String getCity() {
  return city;
 }
 
 public String getStreet() {
  return street;
 }
  
 public String getZipcode() {
  return zipcode;
 }
 
 public ImmutableCoordinate getCoordinate() {
  return coordinate;
 }
  
 public static class Builder {
  private String country;
  private String state;
  private String city;
  private String street;
  private String zipcode;
  private ImmutableCoordinate coordinate;
    
  public Builder country(String country) {
   this.country = country;
   return this;
  }

  public Builder state(String state) {
   this.state = state;
   return this;
  }

  public Builder city(String city) {
   this.city = city;
   return this;
  }

  public Builder street(String street) {
   this.street = street;
   return this;
  }

  public Builder zipcode(String zipcode) {
   this.zipcode = zipcode;
   return this;
  }

  public Builder coordinate(ImmutableCoordinate coordinate) {
   this.coordinate = coordinate;
   return this;
  }
  
  public ImmutableCoordinate getCoordinate() {
   return coordinate;
  }

  public ImmutableAddress build() {
   return new ImmutableAddress(this);
  }
 }
}

Now, by we can map our mutable class to the Builder of the immutable class, and throw in a custom DozerConverter where nested properties are involved. Below is the mapping for the Address classes:

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozer.sourceforge.net
          http://dozer.sourceforge.net/schema/beanmapping.xsd">

 <configuration>
  <stop-on-errors>true</stop-on-errors>
  <date-format>MM/dd/yyyy HH:mm</date-format>
  <wildcard>true</wildcard>
 </configuration>
 
 <mapping>
  <class-a>location.Coordinate</class-a>
  <class-b>location.ImmutableCoordinate$Builder</class-b>
  
  <field>
   <a set-method="setLongitude">longitude</a>
   <b set-method="longitude">longitude</b>
  </field>
  <field>
   <a set-method="setLatitude">latitude</a>
   <b set-method="latitude">latitude</b>
  </field>
 </mapping>

 <mapping>
  <class-a>location.Address</class-a>
  <class-b>location.ImmutableAddress$Builder</class-b>
  
  <field>
   <a set-method="setCountry">country</a>
   <b set-method="country">country</b>
  </field>
  <field>
   <a set-method="setState">state</a>
   <b set-method="state">state</b>
  </field>
  <field>
   <a set-method="setCity">city</a>
   <b set-method="city">city</b>
  </field>
  <field>
   <a set-method="street">street</a>
   <b set-method="street">street</b>
  </field>
  <field>
   <a set-method="setZipcode">zipcode</a>
   <b set-method="zipcode">zipcode</b>   
  </field>
  <field custom-converter-id="coordConverter">
   <a set-method="setCoordinate">coordinate</a>
   <b set-method="coordinate">coordinate</b>
  </field>
 </mapping>
</mappings>

And the DozerConverter is a fairly straight forward implementation (I actually use Dozer to do its own job…):

public class CoordinateConverter extends DozerConverter {

 private final Mapper mapper;
  
 public CoordinateConverter(Mapper mapper) {
  super(Coordinate.class, ImmutableCoordinate.class);
  this.mapper = mapper;
 } 
 
 @Override
 public Coordinate convertFrom(ImmutableCoordinate source, Coordinate destination) {  
  return mapper.map(source, Coordinate.class);
 }
 
 @Override
 public ImmutableCoordinate convertTo(Coordinate source, ImmutableCoordinate destination) {
  return source == null ? null : mapper.map(source, ImmutableCoordinate.Builder.class).build();
 }
}

Now mapping between the classes is a matter of a single line:

Address address = new Address();
// set set set...
  
ImmutableAddress immutableAddress = mapper.map(address, ImmutableAddress.Builder.class).build();
And it even works in the opposite direction :D

Although this solution is not as clean as it should have been – there’s still some over verbosity, and an obscure need for a getter in some cases, it is still preferable over the piles of code you get when messing with object mapping. This technique may also be easier to sneak into the Dozer code-base than constructor arguments support.

7 comments:

  1. I was facing exactly the same problem of mapping some object to its immutable value-object counterpart. I came up with a solution that is much simpler.
    Just use is-accessible="true" on the side of the value-object.
    This works for me. I look forward to comments on this solution.

    ReplyDelete
  2. Hi Anonymous,

    10X for your input :)

    I can see how you can copy an immutable object to an immutable object using the solution you described, but I don't think I understand how you managed to populate the immutable object's final members when copying a mutable object into an immutable one.

    Can you please elaborate?

    ReplyDelete
  3. I do not declare the fields final in the immutable object. However it is immutable by the absence of setter methods.

    I even had to remove the final modifiers on the fields to be able to persist the immutable value objects using JPA/Hibernate.

    ReplyDelete
  4. I assume you also need to declare an empty (default) constructor for this to work.

    What I was trying to achieve is a truly immutable class, that is validating it's state upon construction.

    The idea was to create an easy, straight forward, and SAFE to create immutable class, which can be mapped using Dozer mapping.

    IMHO, the approach you have chosen is problematic, although you managed to avoid most of the verbosity.

    ReplyDelete
  5. You are right: I have an empty constructor, which I also need for Hibernate to be able to create the persistent instances. But the nice thing is that this constructor can be PRIVATE, so there is no way to create a new instance in an unsafe state.

    For me, the risk of having no final modifiers is small compared to the amount of code needed to be "safer". But I guess this is a choice that everyone needs to make for himself.

    ReplyDelete
  6. I agree.

    Thanks for sharing.

    ReplyDelete
  7. To whom it may concern:
    At least with Dozer 5.5.1 there is a neat trick to have a lot less XML.
    Put a standard getter for every setter into the builder and name the setters in the builder like in a bean (i.e. "Builder setCity(String city)". Then Dozer will subsequently recognize the setter methods returning the builder without a field definition in XML.
    This is arguably code noise, but it works fine.

    ReplyDelete