Search code examples
javajavascriptspringmarshallingdwr

How to marshall a javax.xml.datatype.Duration Java <--> JS using Spring/DWR


I've been given a pretty complex stack of generated classes and need to send it between the server and the client using DWR. One of the classes uses a jax.xml.datatype.Duration. Here is the Java code where I create a bean with test data using the duration:

DatatypeFactory df = DatatypeFactory.newInstance();
Duration duration = df.newDuration(RANDOM.nextLong());
testObject.setDuration(duration);

DWR doesn't like that duration in the object:

ERROR [org.directwebremoting.dwrp.DefaultConverterManager] - No converter found for 'com.sun.org.apache.xerces.internal.jaxp.datatype.DurationImpl'

and occasionally I see the same message (I think it was when I was going from JavaScript back to Java) for the class com.sun.org.apache.xerces.internal.jaxp.datatype.DurationImpl

Defining a regular converter doesn't help:

<dwr:convert type="bean" match="com.sun.org.apache.xerces.internal.jaxp.datatype.DurationImpl"/>
<dwr:convert type="bean" match="javax.xml.datatype.Duration"/>

I'm sure the above doesn't work because they're not really beans. But I'm at a loss as to how to get it to work. I've read in several places that you can define a custom converter, but the details seem very vague and in most cases out dated. The DWR website says there's a link that explains it all, but it just links you to the javadoc which doesn't help me with the actual implementation.

Can anybody help me figure out how to handle a duration?


Solution

  • I couldn't find one place that provided the answer, so I had to piece bits of information together from different places -- the spring-dwr schema, examples of classes that extend the provided, etc.

    For starters, this was the proper way to configure spring.xml:

      <dwr:configuration>
        <dwr:init>
            <dwr:converter id="durationConverter" class="com.foo.DurationConverter"/>
        </dwr:init>
    
        <dwr:convert class="com.sun.org.apache.xerces.internal.jaxp.datatype.DurationImpl" type="durationConverter"/>
        <dwr:convert class="javax.xml.datatype.Duration" type="durationConverter"/>
        ...
      </dwr:configuration>
    

    I'm not really sure if I needed both the DurationImpl and the Duration convert declarations, but I've seen messages for both of them, so I put them both in.

    Here was how I built my converter:

    public class DurationConverter extends BeanConverter {
    
        @Override
        public Duration convertInbound(Class<?> type, InboundVariable iv, InboundContext ic) throws MarshallException {
            String value = iv.getValue();
    
            // If the text is null then the whole bean is null
            if (value == null) {
                return null;
            }
    
            Duration duration = null;
            try {
                DatatypeFactory df = DatatypeFactory.newInstance();
                duration = df.newDuration(Long.valueOf(value));
    
            } catch (DatatypeConfigurationException ex) {
                Logger.getLogger(DurationConverter.class.getName()).log(Level.SEVERE, null, ex);
            }
    
            return duration;
        }
    
        @Override
        public OutboundVariable convertOutbound(Object o, OutboundContext oc) throws MarshallException {
            Duration duration = (Duration) o;
            String varname = oc.getNextVariableName();
            Map<String, OutboundVariable> ovs = new TreeMap<>();
            OutboundVariable durationOV = getConverterManager().convertOutbound(duration.getSeconds(), oc); 
    
            ovs.put("duration", durationOV);
    
            ObjectJsonOutboundVariable oj = new ObjectJsonOutboundVariable();
            oj.setChildren(ovs);
    
            return oj;
        }
    }
    

    Hope this helps somebody else struggling with the same thing.