Saturday, February 21, 2015

Builder Pattern - Java

Builder pattern - Java

 (Exploring in Java with few open source libraries)

Introduction

A few days back i was presented a situation; at work; related to constructing several POJO objects for some service orchestration. The orchestration of calls required us to build/construct the argument objects(which were POJO) for each call. In this context, neither the objects were simple nor the number of calls were less; meaning; the objects were non-trivial with fairly large set of attributes and such service api calls exceeded beyond ten. Now the interesting part was that these POJO objects were to be consumed from a dependency jar where the POJO objects itself was getting code-generated by some tool (a proprietary code generator tool) which i didn't have a control upon. Well, the utility/tool that generated this code obviously didn't provide any option for creation method support such as factory or builders; which is the subject of the discussion in the rest of paragraphs here.

What is a builder:

 Builder is well discussed in wikipedia and is defined in GoF design patterns. it is basically a support class that has fluent setter methods for all the settable attributes of a class. It may be available as nested static class within a  POJO or perhaps as a separate class.

When is a builder needed:

It is a common knowledge; that; once the object's state attributes increase (Refer to Effective Java on Builder); the user who wants to construct the object seeks refuge to some facility to construct such as a builder or prototype, factory pattern that requires logic just beyond plain-jane constructors. Of course; in this situation of mine; the POJO generated had just a zero argument constructor and hence user of this object is left with no choice to call several set methods. An interesting corollary to this use case is when developer or the user of this POJO is also the owner of POJO and has the choice of adding builder / factory classes along with the POJO.

This article discusses two use-cases on making use of a builder pattern in complex object construction and in specific a few open source libraries usage in this context.
  1. Auto-generated POJO as third party dependency which doesn't provide  builder/factory
  2. The current user of the POJO is the owner of POJO creation and appreciates a need of builder.

Use case - Auto-generated POJO without any construction hooks

In this situation briefly discussed in the introduction; the  POJO is typically available as a jar file and hence if a builder needs to be built; then a naive way of manually adding a <POJOName>Builder for every POJO proves futile two ways:
  1. Manually inspecting the setter methods (and thus attributes) to build each builder seems to be ridden with non-productive, highly repetitive and effort intensive 
  2. Next, this activity may not be restricted to one time; as the POJO might change in its structure and now; aligning the builder with these changes is even more repetitive and highly ineffective.
These above consequences might at best discourage the developers in making use of builders. 

A better way then; could be to study the POJO class structure for attributes, set methods and auto-create the builder source. If this activity can be automated by some program and aligned to  pre-compile phase of the project build cycle; the builder class thus auto generated can be added as sources just before compilation of the project sources. The builder functionality is typically about providing fluent setter methods for each attribute of the object and can realized in two ways viz.,
  1. Generate a PojoBuilder java class for each of the Pojo.
  2. Generate an interface for the PojoBuilder and use some proxy technique to wire the interface to the Pojoclass for all the set methods call delegation.
 It seemed that the option with interface was better alternative which allows any specific builder implementation to be envisaged  in future (a proxy implementation is just one such).

Going ahead on the second option with interface approach; the tasks really boiled down to the following for any given POJO class named for e.g.; say PojoName
  1. Figure out all the set methods (and thus settable attributes) of the POJO class PojoName by means of some libraries that understands class byte codes. 
  2. Create on the fly; an Interface with the name <PojoName>Builder and create method signatures that resemble all the set methods of the POJO class in question. Of course the set methods in this builder interface will return the <PojoName>Builder object in the true spirit of fluent pattern.
  3. Create the Java source for this builder interface generated in step 2 and place the source newly generated into some source folder which is to be picked up for compilation later.
For step 1;, There are many byte code understanding libraries for various purposes; however the one i chose for the experiment is javassist which not only understands the class bytes but also helps in creating on-the-fly classes, interfaces etc. The first step is to figure out the setter methods and with javassist it becomes much simpler with illustrated method below.

private Set<CtMethod> getWritableMethods(final Class<?> 
            thisPojoClass)throws NotFoundException {
    final CtClass ctClass = ctPool.get(thisPojoClass.getName());
    final Set<CtMethod> ctMethodSet = new LinkedHashSet<>(); 
    final Set<Class<?>> propTypes =
        getPropertyClassTypes(thisPojoClass,
                ctClass, ctMethodSet);
    for (Method method : thisPojoClass.getDeclaredMethods()) {
        if (method.isSynthetic()) {
            LOGGER.warning(method.getName() + 
                " is synthetically added,so ignoring");
            continue;
        }
        final CtMethod ctMethod = ctClass.getDeclaredMethod(
                        method.getName());

        if (Modifier.isPublic(method.getModifiers())
        && setMethodNamePattern.matcher(method.getName())
        .matches() && !ctMethodSet.contains(ctMethod)) {
        // Make sure the types u get from method 
        //is really is of a field type
        boolean isAdded = /*
                * propTypes.containsAll(
                * Arrays.asList(method.
                * getParameterTypes())) &&
                */ctMethodSet.add(ctMethod);
            if (!isAdded) {
                LOGGER.warning(method.getName() +
                 " is not added");
            }
        }
    }
    return ctMethodSet;
}

(Note: This method is part of a class in FluentBuilders class)

In here, the ctClass is the javassist class object representing a java class. All the declared methods of the passed in Pojo class is examined and the ones which match the following conditions are considered for adding to the builder interface.
  1. public mutator method
  2. whose name matches an agrreed upon regex which is passed to the utilty
  3. are not synthetic methods
An additional check can be that the arguments passed into such a setter method to  be of field types as expected in the pojo class.

For Steps 2 and 3;, a base interface Builder from fluent-interface library is assumed and here is the snippet for getting the interface for the Pojo. The source code itself i generated using codemodel.

final Set<CtMethod> writableMethods = getWritableMethods(thisPojoClass);
final Set<Method> writableNormalMethods = getWritableNormalMethods(
thisPojoClass);
 
if (writableMethods.isEmpty()) {
    failedList.add(thisPojoClass);
} else {
    //Create the interface first 
    final String interfaceName = String.format("%s.%sBuilder"
                                     thisPojoClass.getPackage().getName(), 
                                         thisPojoClass.getSimpleName());
 
    //fluentBuilderClass represents the base builder from open source lib 
    final CtClass pojoBuilderInterface = ctPool.makeInterface(interfaceName, 
                                               fluentBuilderClass);
 
    // next add methods to this on the fly interface
    addMethodsToBuilder(pojoBuilderInterface, writableMethods); 
 
    //next create source code model.
    JCodeModel codeModel = new JCodeModel();
    try {
        JClass builderInterface = new JCodeModel()
        ._class(Builder.class.getName(), ClassType.INTERFACE)
        .narrow(thisPojoClass);
        JDefinedClass definedClass = codeModel._class(interfaceName, 
        ClassType.INTERFACE)._extends(builderInterface);
        Class<?> builderClass = ctPool.toClass(pojoBuilderInterface);
        for (Method method : writableNormalMethods) {
            JMethod jMethod = definedClass.method(Modifier.PUBLIC, 
                builderClass, method.getName());
            for (int i = 0; i<method.getParameterTypes().length; i++) {
                jMethod.param(method.getParameterTypes()[i], 
                "arg" + i++);
            }
        }
        sourceFolderRoot.mkdirs();
        codeModel.build(sourceFolderRoot);
    } catch (Exception e) {
        throw new IOException(e);
    }
}
 
 
(Note: This method is part of a class i wrote in FluentBuilders class).
 

How to use this in a maven based set up:

I have added a small set of classes for creating builders  using few open sources and is available 
here in github. The following quick steps can get a first glimpse of how builders can be constructed 
within maven set up.
  1. First have a maven quick archetyped java project created where standard folders such as 
    • src/main/java, 
    • src/test/java and 
    • src/main/resources folders are available under root folder builders-test. 
  2. Next, create a simple Pojo class such as Pojo1.java  in  builders-test/src/main/java/
  3. This Pojo could be already made available to you in the form of dependency jar
    • dependency jar containing the pojo class is in the real situation and  is usual 
    • however for demo purpose assuming a java source code (Pojo1.java)
    • this java source will be compiled and for which subsequently a builder is generated
  4. Next, create a text file class-listing.txt in builders-test/src/main/resources/
  5. Next, create a pom.xml that has relevant sections to note below apart from dependencies 
    • The builder needs to be generated when the Pojo1 is compiled 
      • and hence in the maven-exec-plugin, i am triggering this in process-classes phase
    • The builder generated needs to be considered for adding it as a source and then compiled 
      • and hence build-helper-maven-plugin runs add-source in process-classes phase
    • Next, the compile plugin itself being configured to trigger compilation in 2 phases
      • once during compile and second pass dueing process-class due to added source. 
    • the maven test phase is assumed since builder interface is compiled in process-classes
      @Test
      public void test() throws ClassNotFoundException {
         Pojo1 pojo1=FluentBuilders.<Pojo1,Pojo1Builder>builder(
      Pojo1.class).setI(10).setJ("Murthy").build();
         System.out.println(pojo1.toString());
      }
    • Lastly, snapshot repositories for the builders and other libs
      <repositories>
         <repository>
            <id>oss-snapshot</id>
            <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
         </repository>
         <repository>
            <id>apache-snapshots</id>
            <url>https://repository.apache.org/content/repositories/snapshots</url>
         </repository>
      </repositories
       
       Please refer to the complete example in the readme snippet.

Usecase - Auto-generating builders as part of your POJO.

This use case is about when as a developer oneself owns the POJO creation and i have just explored
two libraries. Please let me know of others in this category
  1. Google's  AutoValue
  2. Project lombok
While one can  naively manually add builder class as a static nested class within the POJO, a
simple annotation based approach may reduce the mundane activity which is the reason to
discuss on this use case.

In case of auto value one needs to think in terms of abstract class for the POJO class and
expose the attributes as abstract get methods as shown below in their original example
[Courtesy: from AutoValue site]

public final class PojoClass {

    @AutoValue
    abstract static class Animal {
        static Animal create(String name, int numberOfLegs) {
            return new AutoValue_PojoClass_Animal(name, numberOfLegs);
        }
        abstract String name();
        abstract int numberOfLegs();
    }
}

AutoValue has other additional benefits; however in the context of experimenting for most basic 
needs of a simple builder; i am leaving those details out here.

On the other hand project lombok has even more leaner annotation based intent as follows:

@Builder
@Data
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
class Example {
    int foo;
    String bar;
}
 
Now, you have here a full fledged class with all the fields as private and final and with getter(s) and
a static nested ExampleBuilder class with relevant setters already wired in the .class files. 

Can you ask for anything simpler?

Note : Please note that the sole intent in this use case was to explore a few popular libraries use of automating, 
simplyfing the construction and use of builders. It is by no means a comparison of features or limitations here
 as i have not exhaustively demonstrated their abilities here.

 

Summary:

In summary, this was an attempt to understand how a builder can be created for dependent POJO
classes coming from third party. While nothing short of byte code reading and adding interface/class
on the fly is the approach in these cases; the use of annotation based libraries in the own POJO
case is heartening to note the simplicity and convenience it brings upon.


I am signing off finally by making a wish:

If only did the proprietary POJO code generators considered creating builders/factories/creation 
methods along with generating POJO itself; i wouldn't have got a chance to write this post!.

No comments:

Post a Comment