Java – xmladapter to JAXB binding joda interval?
I have encountered a web service problem in my JAXB binding. I have been trapped for several hours:
In order to prepare a larger web service that must return an instance of joda time class (instant, duration, interval, etc.), I began to use a web service with only one method that returns interval:
package jodaws; import javax.jws.WebService; import javax.xml.ws.Endpoint; import org.joda.time.Interval; @WebService(name = "JodaWS") public class JodaWebService { public Interval readInterval() { return new Interval(30,40); } public static void main(String[] args) { Endpoint.publish("http://localhost:10100/JodaWS",new JodaWebService()); } }
When publishing this web service, I received an exception indicating that "org.joda.time.interval does not have a no Arg default constructor":
29.05.2011 17:24:07 com.sun.xml.internal.ws.model.RuntimeModeler getRequestWrapperClass INFO: Dynamically creating request wrapper Class jodaws.jaxws.ReadInterval 29.05.2011 17:24:07 com.sun.xml.internal.ws.model.RuntimeModeler getResponseWrapperClass INFO: Dynamically creating response wrapper bean Class jodaws.jaxws.ReadIntervalResponse Exception in thread "main" javax.xml.ws.WebServiceException: Unable to create JAXBContext at com.sun.xml.internal.ws.model.AbstractSEIModelImpl.createJAXBContext(AbstractSEIModelImpl.java:153) at com.sun.xml.internal.ws.model.AbstractSEIModelImpl.postProcess(AbstractSEIModelImpl.java:83) at com.sun.xml.internal.ws.model.RuntimeModeler.buildruntimeModel(RuntimeModeler.java:244) at com.sun.xml.internal.ws.server.EndpointFactory.createSEIModel(EndpointFactory.java:312) at com.sun.xml.internal.ws.server.EndpointFactory.createEndpoint(EndpointFactory.java:178) at com.sun.xml.internal.ws.api.server.WSEndpoint.create(WSEndpoint.java:456) at com.sun.xml.internal.ws.api.server.WSEndpoint.create(WSEndpoint.java:475) at com.sun.xml.internal.ws.transport.http.server.EndpointImpl.createEndpoint(EndpointImpl.java:213) at com.sun.xml.internal.ws.transport.http.server.EndpointImpl.publish(EndpointImpl.java:143) at com.sun.xml.internal.ws.spi.ProviderImpl.createAndPublishEndpoint(ProviderImpl.java:102) at javax.xml.ws.Endpoint.publish(Endpoint.java:170) at jodaws.JodaWebService.main(JodaWebService.java:17) Caused by: java.security.PrivilegedActionException: com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions org.joda.time.Interval does not have a no-arg default constructor. this problem is related to the following location: at org.joda.time.Interval at public org.joda.time.Interval jodaws.jaxws.ReadIntervalResponse._return at jodaws.jaxws.ReadIntervalResponse org.joda.time.base.BaseInterval does not have a no-arg default constructor. this problem is related to the following location: at org.joda.time.base.BaseInterval at org.joda.time.Interval at public org.joda.time.Interval jodaws.jaxws.ReadIntervalResponse._return at jodaws.jaxws.ReadIntervalResponse at java.security.AccessController.doPrivileged(Native Method) at com.sun.xml.internal.ws.model.AbstractSEIModelImpl.createJAXBContext(AbstractSEIModelImpl.java:140) ... 11 more Caused by: com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions org.joda.time.Interval does not have a no-arg default constructor. this problem is related to the following location: at org.joda.time.Interval at public org.joda.time.Interval jodaws.jaxws.ReadIntervalResponse._return at jodaws.jaxws.ReadIntervalResponse org.joda.time.base.BaseInterval does not have a no-arg default constructor. this problem is related to the following location: at org.joda.time.base.BaseInterval at org.joda.time.Interval at public org.joda.time.Interval jodaws.jaxws.ReadIntervalResponse._return at jodaws.jaxws.ReadIntervalResponse at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:91) at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:436) at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:277) at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1100) at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:143) at com.sun.xml.internal.bind.api.JAXBRIContext.newInstance(JAXBRIContext.java:95) at com.sun.xml.internal.ws.developer.JAXBContextFactory$1.createJAXBContext(JAXBContextFactory.java:97) at com.sun.xml.internal.ws.model.AbstractSEIModelImpl$1.run(AbstractSEIModelImpl.java:148) at com.sun.xml.internal.ws.model.AbstractSEIModelImpl$1.run(AbstractSEIModelImpl.java:140) ... 13 more
So I have read several tutorials and so on. Finally, I wrote my own xmladapter for the interval class - the first implementation returns a valid but constant value:
package jodaws; import javax.xml.bind.annotation.adapters.XmlAdapter; import org.joda.time.Interval; public class IntervalAdapter extends XmlAdapter<String,Interval> { @Override public Interval unmarshal(String v) throws Exception { return new Interval(10,20); } @Override public String marshal(Interval v) throws Exception { return "10-20"; } }
In addition, I have annotated my web method to use the adapter:
package jodaws; import javax.jws.WebService; // import added import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import javax.xml.ws.Endpoint; import org.joda.time.Interval; @WebService(name = "JodaWS") public class JodaWebService { // annotation added @XmlJavaTypeAdapter(IntervalAdapter.class) public Interval readInterval() { return new Interval(30,new JodaWebService()); } }
It should work now However, when I start the web service again, I still receive an exception, even though it is another exception:
29.05.2011 17:27:33 com.sun.xml.internal.ws.model.RuntimeModeler getRequestWrapperClass INFO: Dynamically creating request wrapper Class jodaws.jaxws.ReadInterval 29.05.2011 17:27:34 com.sun.xml.internal.ws.model.RuntimeModeler getResponseWrapperClass INFO: Dynamically creating response wrapper bean Class jodaws.jaxws.ReadIntervalResponse Exception in thread "main" javax.xml.ws.WebServiceException: java.lang.IllegalArgumentException: value class jodaws.IntervalAdapter at com.sun.xml.internal.ws.model.WrapperBeanGenerator.createResponseWrapperBean(WrapperBeanGenerator.java:269) at com.sun.xml.internal.ws.model.RuntimeModeler.getResponseWrapperClass(RuntimeModeler.java:293) at com.sun.xml.internal.ws.model.RuntimeModeler.processDocWrappedMethod(RuntimeModeler.java:688) at com.sun.xml.internal.ws.model.RuntimeModeler.processMethod(RuntimeModeler.java:612) at com.sun.xml.internal.ws.model.RuntimeModeler.processClass(RuntimeModeler.java:401) at com.sun.xml.internal.ws.model.RuntimeModeler.buildruntimeModel(RuntimeModeler.java:240) at com.sun.xml.internal.ws.server.EndpointFactory.createSEIModel(EndpointFactory.java:312) at com.sun.xml.internal.ws.server.EndpointFactory.createEndpoint(EndpointFactory.java:178) at com.sun.xml.internal.ws.api.server.WSEndpoint.create(WSEndpoint.java:456) at com.sun.xml.internal.ws.api.server.WSEndpoint.create(WSEndpoint.java:475) at com.sun.xml.internal.ws.transport.http.server.EndpointImpl.createEndpoint(EndpointImpl.java:213) at com.sun.xml.internal.ws.transport.http.server.EndpointImpl.publish(EndpointImpl.java:143) at com.sun.xml.internal.ws.spi.ProviderImpl.createAndPublishEndpoint(ProviderImpl.java:102) at javax.xml.ws.Endpoint.publish(Endpoint.java:170) at jodaws.JodaWebService.main(JodaWebService.java:18) Caused by: java.lang.IllegalArgumentException: value class jodaws.IntervalAdapter at com.sun.xml.internal.ws.org.objectweb.asm.ClassWriter.newConstItem(ClassWriter.java:893) at com.sun.xml.internal.ws.org.objectweb.asm.AnnotationWriter.visit(AnnotationWriter.java:185) at com.sun.xml.internal.ws.model.WrapperBeanGenerator.createBeanImage(WrapperBeanGenerator.java:111) at com.sun.xml.internal.ws.model.WrapperBeanGenerator.createResponseWrapperBean(WrapperBeanGenerator.java:265) ... 14 more
That's where I'm trapped What on earth did I do wrong?
Solution
You are right. You need to use xmladapter in this use case Here is an example of how to do this:
IntervalStringAdapter
The following adapter will convert the interval instance into strings in the form of start end
package blog.joda; import javax.xml.bind.annotation.adapters.XmlAdapter; import org.joda.time.Interval; public class IntervalStringAdapter extends XmlAdapter<String,Interval>{ @Override public Interval unmarshal(String v) throws Exception { int dashIndex = v.indexOf('-'); long start = Long.valueOf(v.substring(0,dashIndex)); long end = Long.valueOf(v.substring(dashIndex + 1)); return new Interval(start,end); } @Override public String marshal(Interval v) throws Exception { return v.getStartMillis() + "-" + v.getEndMillis(); } }
root
The following is an example of how to configure properties to use an adapter:
package blog.joda; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.joda.time.Interval; @XmlRootElement public class Root { private Interval interval; @XmlJavaTypeAdapter(IntervalStringAdapter.class) public Interval getInterval() { return interval; } public void setInterval(Interval interval) { this.interval = interval; } }
demonstration
package blog.joda; import java.io.File; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(Root.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); Root root = (Root) unmarshaller.unmarshal(new File("src/blog/joda/input.xml")); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true); marshaller.marshal(root,System.out); } }
input. In XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <root> <interval>10-20</interval> </root>
For more information
> http://bdoughan.blogspot.com/2011/05/jaxb-and-joda-time-dates-and-times.html