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!