JAX-WS and Joda-Time?
Asked Answered
P

4

13

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 2.1.

Let me clarify the question because both answers so far have focused on binding custom types to existing JAXB classes, which is related but not the question I'm asking. How do I make the following @WebService accept joda DateTime objects as parameters?

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import org.joda.time.DateTime;

@WebService
@SOAPBinding(style = SOAPBinding.Style.RPC)
public interface Resender {
    @WebMethod
    void resend(
            @WebParam(name = "start") DateTime start,
            @WebParam(name = "end") DateTime end
    );

}
Pasturage answered 24/2, 2011 at 15:51 Comment(0)
C
3

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
    );
}
Cincinnati answered 28/10, 2014 at 18:48 Comment(0)
O
9

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.

Overabundance answered 24/2, 2011 at 22:35 Comment(2)
Since I'm writing the Java classes by hand with the JAXB annotations, I'm assuming there isn't any problem going in the reverse direction and skipping the xjb file all together?Pasturage
Also another point, you are reiterating the easily found examples of mapping JAXB class fields, but my question was about specifically about the parameters of a method exposed as a SOAP service via JAX-WS annotations. Do you have any experience or examples of JAXB type adapting in that context?Pasturage
H
3

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>
Hispaniola answered 1/3, 2011 at 17:24 Comment(1)
Please see my updated question, I don't think I phrased it properly when I originally wrote it.Pasturage
M
3

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;
}
}
Meagre answered 8/3, 2012 at 5:45 Comment(2)
Thank you for trying but I'm looking for an answer to the question as stated, a (at)WebService that can accept Joda DateTime objects as (at)WebParams.Pasturage
The <bindingIncludes> tag is in the wrong place, at least for today's maven plugin. Checkout maven-cxf-codegen-plugin for working examples. It goes inside <wsdlOption> but multi-line xml doesn't work with comments.Cornstalk
C
3

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
    );
}
Cincinnati answered 28/10, 2014 at 18:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.