How do I write a JAX-WS service so the @WebParam of my @WebMethod is a Joda-Time class like DateTime? Will @XmlTypeAdapter on a parameter work? I\'m deploying to GlassFish
First write simple converter (to Calendar
in this example, but can be easily changed to Joda-Time):
public class XsdDateTimeConverter {
public static Calendar unmarshal(String dateTime) {
final GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(DatatypeConverter.parseDate(dateTime).getTime());
return calendar;
}
public static String marshal(Calendar calendar) {
return DatatypeConverter.printDate(calendar);
}
}
Next you have to introduce your converter to JAXB (xjb
file):
<globalBindings>
<javaType
name="java.util.Calendar"
xmlType="xs:dateTime"
parseMethod="XsdDateTimeConverter.unmarshal"
printMethod="XsdDateTimeConverter.marshal"
/>
<javaType
name="java.util.Calendar"
xmlType="xs:date"
parseMethod="XsdDateTimeConverter.unmarshal"
printMethod="XsdDateTimeConverter.marshal"
/>
</globalBindings>
In the generated JAXB models xjc
produced the following annotation:
@XmlJavaTypeAdapter(Adapter2.class)
@XmlSchemaType(name = "date")
protected Calendar date;
Where Adapter2.class
is a generated adapter that wraps your POJO converter. As you can see Calendar
is used instead of clumsy javax.xml.datatype.XMLGregorianCalendar
. If you adjust this example to Joda-Time, please share it with us.
You have to annotate the parameter directly such as below (I am making use of XSDDateTimeMarshaller written by @DennisTemper as one of the answers to your question but feel free to substitute with another one...) :
@WebService
@SOAPBinding(style = SOAPBinding.Style.RPC)
public interface Resender {
@WebMethod
void resend(
@WebParam(name = "start") @XmlJavaTypeAdapter(type = DateTime.class, value = XSDDateTimeMarshaller.class) DateTime start,
@WebParam(name = "end") @XmlJavaTypeAdapter(type = DateTime.class, value = XSDDateTimeMarshaller.class) DateTime end
);
}
Here's a non-annotation Joda solution. We have generated objects from xsd's and want them to use Joda instead of XmlGregorianCalendar.
Note: When I tried to pass a proper XmlGregorianCalendar object to the unmarshal methods in the classes, I got JaxB compiler errors that said it required type String, not XmlGregorianCalendar. Tested with String, and it appears to be working fine. Quick and dirty error handling here, so fix that up as you please.
Hope this helps.
Maven pom plugin snippet:
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<configuration>
<schemaDirectory>src/main/resources/schemas/</schemaDirectory>
<removeOldOutput>true</removeOldOutput>
<bindingIncludes>
<bindingInclude>jaxb-custom-bindings.xml</bindingInclude>
</bindingIncludes>
</configuration>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
jaxb-custom-bindings.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<globalBindings>
<javaType
name="org.joda.time.DateTime"
xmlType="xs:dateTime"
parseMethod="com.yourcompanyname.XSDDateTimeToJodaDateTimeMarshaller.unmarshal"
printMethod="com.yourcompanyname.XSDDateTimeToJodaDateTimeMarshaller.marshal"
/>
<javaType
name="org.joda.time.LocalDate"
xmlType="xs:date"
parseMethod="com.yourcompanyname.XSDDateToJodaLocalDateMarshaller.unmarshal"
printMethod="com.yourcompanyname.XSDDateToJodaLocalDateMarshaller.marshal"
/>
</globalBindings>
public class XSDDateTimeToJodaDateTimeMarshaller {
private static final Logger LOG = LoggerFactory.getLogger(XSDDateTimeToJodaDateTimeMarshaller.class);
public static DateTime unmarshal(String xmlGregorianCalendar) {
DateTime result= new DateTime(xmlGregorianCalendar);
return result;
}
public static String marshal(DateTime dateTime) {
String result = "MARSHALLING_ERROR";
try {
result = DatatypeFactory.newInstance().newXMLGregorianCalendar(dateTime.toGregorianCalendar()).toXMLFormat();
} catch (DatatypeConfigurationException e) {
LOG.error("Error marshalling Joda DateTime to xmlGregorianCalendar",e);
}
return result;
}
}
public class XSDDateToJodaLocalDateMarshaller {
private static final Logger LOG = LoggerFactory.getLogger(XSDDateToJodaLocalDateMarshaller.class);
public static LocalDate unmarshal(String xmlGregorianCalendar) {
return new LocalDate(xmlGregorianCalendar);
}
public static String marshal(LocalDate localDate) {
String result = "MARSHALLING_ERROR";
try {
result = DatatypeFactory.newInstance().newXMLGregorianCalendar(localDate.toDateTimeAtStartOfDay().toGregorianCalendar()).toXMLFormat();
} catch (DatatypeConfigurationException e) {
LOG.error("Error marshalling Joda LocalDate to xmlGregorianCalendar",e);
}
return result;
}
}
Well following solution template above
1.) Create an XSML Adapter
import java.util.Date;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.joda.time.DateTime;
@XmlTransient
public class XSDDateTimeMarshaller extends XmlAdapter<Date, DateTime> {
@Override
public DateTime unmarshal(Date date) throws Exception {
return new DateTime(date.getTime());
}
@Override
public Date marshal(DateTime dateTime) throws Exception {
return new Date(dateTime.getMillis());
}
}
2.) Annotate jodatime attribute with (snipet from an entity class):
...
@XmlRootElement(name="MyEntity", namespace="http://www.mycompany.com/module")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder={"...", "...", "timeStamp", "...", "..."})
public class MyEntity
...
@XmlElement(namespace="http://www.mysite.com/module")
@XmlJavaTypeAdapter(XSDDateTimeMarshaller.class)
@NotNull
@Type(type="org.joda.time.contrib.hibernate.PersistentDateTime")
@Column(name="TIME_STAMP")
private DateTime timeStamp;
...
}
3.) add type bindings to your myentity.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/XMLSchema http://www.w3.org/2001/XMLSchema.xsd"
targetNamespace="http://www.mysite.com/module"
xmlns:tns="http://www.mysite.com/module"
attributeFormDefault="unqualified"
elementFormDefault="qualified"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jaxb:extensionBindingPrefixes="xjc"
jaxb:version="2.1">
<xsd:annotation>
<xsd:appinfo>
<jaxb:globalBindings>
<jaxb:javaType name="org.joda.time.DateTime"
xmlType="xsd:dateTime"
parseMethod="com.mycompany.myproduct.marshaller.XSDDateTimeMarshaller.unmarshal"
printMethod="com.mycompany.myproduct.marshaller.XSDDateTimeMarshaller.marshal"/>
<jaxb:javaType name="org.joda.time.DateTime"
xmlType="tns:date"
parseMethod="com.mycompany.myproduct.marshaller.XSDDateTimeMarshaller.unmarshal"
printMethod="com.mycompany.myproduct.marshaller.XSDDateTimeMarshaller.marshal"/>
</jaxb:globalBindings>
</xsd:appinfo>
</xsd:annotation>
<xsd:element name="MyEntity" type="tns:MyEntity"/>
<xsd:complexType name="MyEntity">
<xsd:sequence>
...
<xsd:element name="timeStamp" type="tns:date"/>
....
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="date">
<xsd:restriction base="xsd:dateTime" />
</xsd:simpleType>
</xsd:schema>