Aaron Johnson Now with 50% less caffeine!

Posted
20 July 2006 @ 4am

Tagged
J2EE, JavaScript, Software Development, XML

FluentInterface

A couple of weeks ago on the DWR users list, in the context of needing to wire up DWR without using an XML file, Joe Walker pointed to a blog posting by Martin Fowler. In it, Martin discusses an interface style called a ‘fluent interface’. It’s a little difficult to describe in words (so check it out in action on above mentioned blog post) but I think Piers Cawley described it best when he described the style as “…essentially interfaces that do a good job of removing hoopage.” Update: Geert Bevin uses this style in the RIFE framework and was calling the it “chainable builder methods” before Martin came along with the ‘fluent interface’ term.

Back to DWR.  I spent the last couple days working on a ‘fluent’ way of configuring DWR which obviously then wouldn’t require dwr.xml, the result of which is available here. In short, given an XML configuration file that looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting
1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
  <init>
    <converter id="testbean" class="uk.ltd.getahead.testdwr.TestBean2Converter"/>
  </init>
  <allow>
    <create creator="new" javascript="Test" scope="application">
      <param name="class" value="uk.ltd.getahead.testdwr.Test"/>
    </create>
    <create creator="new" javascript="JDate">
      <param name="class" value="java.util.Date"/>
      <exclude method="getHours"/>
      <auth method="getMinutes" role="admin"/>
      <auth method="getMinutes" role="devel"/>
    </create>
    <convert converter="bean" match="$Proxy*"/>
    <convert converter="testbean" match="uk.ltd.getahead.testdwr.TestBean"/>
    <convert converter="bean" match="uk.ltd.getahead.testdwr.ObjB"/>
    <convert converter="object" match="uk.ltd.getahead.testdwr.ObjA">
      <param name="force" value="true"/>
    </convert>
  </allow>
  <signatures>
  <![CDATA[
  import java.util.*;
  import uk.ltd.getahead.testdwr.*;
  Test.testBeanSetParam(Set<TestBean>);
  Test.testBeanListParam(List<TestBean>);
  Test.charTestBeanMapParam(Map<Character, TestBean>);
  Test.stringStringMapParam(Map<String, String>);
  Test.stringStringHashMapParam(HashMap<String, String>);
  Test.stringStringTreeMapParam(TreeMap<String, String>);
  Test.stringCollectionParam(Collection<String>);
  Test.stringListParam(List<String>);
  Test.stringLinkedListParam(LinkedList<String>);
  Test.stringArrayListParam(ArrayList<String>);
  Test.stringSetParam(Set<String>);
  Test.stringHashSetParam(HashSet<String>);
  Test.stringTreeSetParam(TreeSet<String>);
  ]]>
  </signatures>
</dwr>

you can instead configure DWR using the FluentConfiguration class like this:

FluentConfiguration fluentconfig = (FluentConfiguration)configuration;
fluentconfig
  .withConverterType("testbean", "uk.ltd.getahead.testdwr.TestBean2Converter")
  .withCreator("new", "Test")
    .addParam("scope", "application")
    .addParam("class", "uk.ltd.getahead.testdwr.Test")
  .withCreator("new", "JDate")
    .addParam("class", "java.util.Date")
    .exclude("getHours")
    .withAuth("getMinutes", "admin")
    .withAuth("getMinutes", "devel")
  .withConverter("bean", "$Proxy*")
  .withConverter("testbean", "uk.ltd.getahead.testdwr.TestBean")
  .withConverter("bean", "uk.ltd.getahead.testdwr.ObjB")
  .withConverter("object", "uk.ltd.getahead.testdwr.ObjA")
    .addParam("force", "true")
  .withSignature()
    .addLine("import java.util.*;")
    .addLine("import uk.ltd.getahead.testdwr.*;")
    .addLine("Test.testBeanSetParam(Set);")
    .addLine("Test.testBeanListParam(List);")
    .addLine("Test.charTestBeanMapParam(Map);")
    .addLine("Test.stringStringMapParam(Map);")
    .addLine("Test.stringStringHashMapParam(HashMap);")
    .addLine("Test.stringStringTreeMapParam(TreeMap);")
    .addLine("Test.stringCollectionParam(Collection);")
    .addLine("Test.stringListParam(List);")
    .addLine("Test.stringLinkedListParam(LinkedList);")
    .addLine("Test.stringArrayListParam(ArrayList);")
    .addLine("Test.stringSetParam(Set);")
    .addLine("Test.stringHashSetParam(HashSet);")
    .addLine("Test.stringTreeSetParam(TreeSet);")
  .finished();

If you’re interested in using this in your DWR project, you need only to:

  • create a class that extends DWRServlet (example: check out FluentDWRServlet.java in the zip file) and use that class as your DWR servlet
  • add a configuration param in web.xml called uk.ltd.getahead.dwr.Configuration and set the value to net.cephas.dwr.FluentConfiguration
  • add a configuration param in web.xml called skipDefaultConfig and set the value to true

    <servlet>
      <servlet-name>dwr</servlet-name>
      <servlet-class>net.cephas.dwr.FluentDWRServlet</servlet-class>
        <init-param>
          <param-name>uk.ltd.getahead.dwr.Configuration</param-name>
          <param-value>net.cephas.dwr.FluentConfiguration</param-value>
        </init-param>
        <init-param>
          <param-name>skipDefaultConfig</param-name>
          <param-value>true</param-value>
        </init-param>
    </servlet>

  • and then override the configure method in the servlet and use the fluent style of configuration I used above.

    Send me an email if you have any questions!


  • 1 Comment

    Posted by
    peter welkenbach
    6 September 2006 @ 12pm

    Hi Aaron,

    realy interessting, cause we also like to use the concept of Fluent Interfaces in out applications. But never guessed to use it in this way.
    But, I thought for configuration purposes it would be nice, not to hardcode this in the source. So the idea arose to use scripting support in Spring Framework 2 and I used your idea to impelement a proof of concept with Groovy ….
    And guess what … it works. Think it’s a nice example for what to use scripting beans in Spring

    just a liitle example:

    the script:
    —————————–
    import net.cephas.dwr.FluentConfiguration
    import com.trivadis.eurojourney.scripting.GroovyFluentConfiguration
    class GroovyFluentConfigurationImpl implements GroovyFluentConfiguration {
    public void init( FluentConfiguration configuration ) {
    FluentConfiguration fluentconfig = (FluentConfiguration)configuration;
    fluentconfig
    .withConverterType(”jodaConverter”, “com.trivadis.JodaTimeDateConverter”)
    .withCreator(”spring”, “CustomerService”)
    .addParam(”beanName”, “custService”)
    .withConverter(”bean”, “com.trivadis.eurojourney.Customer”)
    .withConverter(”jodaConverter”, “org.joda.time.DateTime”)
    .finished();
    }
    }

    the servlet:
    ———————————–
    ….
    if (configuration instanceof FluentConfiguration) {

    ApplicationContext applicationContext = (ApplicationContext)this.getServletContext().getAttribute(
    WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

    GroovyFluentConfiguration fluentconfig =
    (GroovyFluentConfiguration)applicationContext
    .getBean(”config”);

    fluentconfig.init( (FluentConfiguration)configuration );

    thankx a lot for your fantastic blog and the inspiratiion you gave me with it.

    kind regards
    peter


    Leave a Comment

    Links: 7-17-2006 Links: 7-25-2006