XMLGregorianCalendar date serializing to empty string
Asked Answered
V

3

6

Using Java 1.6 wsimport I generated source from a WSDL for a web service. One of the fields in the request structure has type xs:dateTime in the XML schema included by the WSDL and type javax.xml.datatype.XMLGregorianCalendar in the generated code.

Through manual testing with soapUI I have determined that the following serialized values are accepted by the web service: 2011-12-08, 2011-12-08Z. The following are not accepted and the response in this case is an empty reply (not an explicit error): 2011-12-08T20:00:00, 2011-12-08T20:00:00-05:00. The service itself is .NET powered if that matters.

My thought is that the server should accept the full date/time and reject the date only, but the other way around is what is happening. But I am under no assumption that maintainers of the server will be open to change. So I have attempted to convince the client to send a date only.

I can't convince my client code to serialize an XMLGregorianCalendar object into a date only. Well actually I can, except when the generated code does it. When the generated client code (produced by wsimport) does it, the serialized value is the empty string, and the server correctly returns an error. I verified this using a packet sniffer.

Here is how I'm creating and populating the date field in the request:

import java.util.Calendar;
import java.util.GregorianCalendar;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import java.util.TimeZone;
// also import GeneratedRequest from generated packages

private makeRequest() {
   GeneratedRequest request;
   // ...
   request.setDateField(xmlDayNow(TimeZone.getTimeZone("America/New_York"),
       6));  // broadcast day starts at 6 am EST
   // ...
}

@XmlSchemaType(name="date")
private static XMLGregorianCalendar xmlDayNow(TimeZone tz, int localHourStart)
throws MyException {
    GregorianCalendar cal = gregorianBroadcastDayNow(tz, localHourStart);
    XMLGregorianCalendar result;
    try {
        result = DatatypeFactory.newInstance().newXMLGregorianCalendarDate(
            cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1,
            cal.get(Calendar.DAY_OF_MONTH), DatatypeConstants.FIELD_UNDEFINED)
            .normalize();
    } catch (DatatypeConfigurationException e) {
        throw new MyException("XMLGregorianCalendar issue", e);
    }
    return result;
}

protected static GregorianCalendar gregorianBroadcastDayNow(TimeZone tz,
        int localHourStart) {
    GregorianCalendar now = new GregorianCalendar(tz);
    if (now.get(GregorianCalendar.HOUR_OF_DAY) < localHourStart) {
        now.add(GregorianCalendar.DAY_OF_MONTH, -1);
    }
    return now;
}

The implementation class for XMLGregorianCalendar in my case is com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl. In the debugger, or if I add log output, calling the date object's toXMLFormat() method returns a date only, such as 2011-12-09. Using a debugger to inspect the date object itself, I see that its year, day and month fields are populated, and all the others are either null or -2147483648 which is the value of DatatypeConstants.FIELD_UNDEFINED. According to all documentation and Internet search results I have found, my date object is correctly formed.

Am I crazy? Is the server really in error? Is the refusal of the generated client code to send a date only correct? Is this a justifiable "undefined behavior" case? Is the wrong implementation class being used (could that possibly matter anyway)? Is there some known issue with wsimport that's affecting me?

Valle answered 9/12, 2011 at 16:6 Comment(1)
Could you provide GeneratedRequest class listing and xml schema?Micah
V
1

I was seeing an unintuitive implementation detail of .NET web services that I did not recognize. In my analysis I observed that providing a complete date/time serialized string resulted in an empty response from the server, but not an explicit error.

This was because on the server, they were in fact using date/time objects, but were forcing all of the times to midnight Eastern Standard Time. So the responses were only populated with results if I forced the time in the request to midnight EST, which I was not doing. (In the event that no time zone was given, the server assumed EST; and if no time was given at all, midnight EST was assumed.)

So in my case, the solution was to modify the client code to force the time zone to Olson America/New_York and force the local time to 00:00:00 when creating the request.

Valle answered 9/12, 2011 at 19:50 Comment(0)
L
4

I've found that JAXB's date conversions should not be left to their own devices. Not as familiar with wsimport, but does it allow you specify a binding file? I use Joda Date/Time but the idea is the same I'm sure. Added this to my binding.xjb:

<globalBindings>
    <serializable />
    <javaType name="org.joda.time.DateTime" xmlType="xsd:dateTime"
        printMethod="someStaticDateConverterClass.printDateIso"
        parseMethod="someStaticDateConverterClass.parseDateIso" />
    <javaType name="org.joda.time.DateTime" xmlType="xs:date"
        printMethod="someStaticDateConverterClass.printDateYYYYDashMMDashDD"
        parseMethod="someStaticDateConverterClass.parseDateYYYYDashMMDashDD" />
</globalBindings>

With a class as such:

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;

public class someStaticDateConverterClass{

private static DateTimeFormatter isoFormat = ISODateTimeFormat.dateTime();
private static DateTimeFormatter YYYYDashMMDashDDFormat = DateTimeFormat
        .forPattern("yyyy-MM-dd");
private static DateTimeFormatter YYYYMMDDFormat = DateTimeFormat
        .forPattern("yyyyMMdd");

public static String printDateIso(DateTime value) {
    return printDate(value, isoFormat);
}

public static DateTime parseDateIso(String value) {
    return parseDate(value, isoFormat);
}

public static String printDateYYYYDashMMDashDD(DateTime value) {
    return printDate(value, YYYYDashMMDashDDFormat);
}

public static DateTime parseDateYYYYDashMMDashDD(String value) {
    return parseDate(value, YYYYDashMMDashDDFormat);
}

public static String printDateYYYYMMDD(DateTime value) {
    return printDate(value, YYYYMMDDFormat);
}

public static DateTime parseDateYYYYMMDD(String value) {
    return parseDate(value, YYYYMMDDFormat);
}

private static String printDate(DateTime value, DateTimeFormatter format) {

    String dateAsStr;

    if (value != null) {
        dateAsStr = value.toString(format);
    } else {
        dateAsStr = null;
    }

    return dateAsStr;
}

private static DateTime parseDate(String value, DateTimeFormatter format) {

    DateTime strAsDate;

    if (value != null) {
        strAsDate = format.parseDateTime(value);
    } else {
        strAsDate = null;
    }
    return strAsDate;
}
}
Lindly answered 9/12, 2011 at 19:13 Comment(1)
I seem to remember seeing a documented way to provide JAXB bindings. I was able to reach a solution but thanks for contributing.Valle
V
1

I was seeing an unintuitive implementation detail of .NET web services that I did not recognize. In my analysis I observed that providing a complete date/time serialized string resulted in an empty response from the server, but not an explicit error.

This was because on the server, they were in fact using date/time objects, but were forcing all of the times to midnight Eastern Standard Time. So the responses were only populated with results if I forced the time in the request to midnight EST, which I was not doing. (In the event that no time zone was given, the server assumed EST; and if no time was given at all, midnight EST was assumed.)

So in my case, the solution was to modify the client code to force the time zone to Olson America/New_York and force the local time to 00:00:00 when creating the request.

Valle answered 9/12, 2011 at 19:50 Comment(0)
B
0

In addition to Roy's answer here is valid binding file for apache cxf with maven cxf codegen configuration

/resources/binding.jxb

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    jaxb:extensionBindingPrefixes="xjc" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    xmlns="ns" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    jaxb:version="2.1">

    <jaxb:globalBindings>
    <jaxb:serializable />
    <jaxb:javaType name="org.joda.time.DateTime" xmlType="xsd:dateTime"
        printMethod="package.converters.StaticXmlDateConverter.printDateIso"
        parseMethod="package.converters.StaticXmlDateConverter.parseDateIso" />
    <jaxb:javaType name="org.joda.time.DateTime" xmlType="xsd:date"
        printMethod="package.converters.StaticXmlDateConverter.printDateYYYYDashMMDashDD"
        parseMethod="package.converters.StaticXmlDateConverter.parseDateYYYYDashMMDashDD" />
         </jaxb:globalBindings>
</jaxb:bindings>

inside pom.xml

       <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>3.1.6</version>
                <executions>
                    <execution>
                        <id>generate-sources-0001</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <defaultOptions>
                                <frontEnd>jaxws21</frontEnd>
                            </defaultOptions>
                            <sourceRoot>${project.build.directory}/generated/</sourceRoot>
                            <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>${basedir}/src/main/resources/service.wsdl</wsdl>
                                    <wsdlLocation>
                                        http://someprovider.com/services/actualservice.asmx?WSDL
                                    </wsdlLocation>
                                    <bindingFiles>
                                        <bindingFile>${basedir}/src/main/resources/binding.xjb</bindingFile>
                                    </bindingFiles>
                                    <extraargs>
                                        <extraarg>-client</extraarg>
                                        <extraarg>-verbose</extraarg>
                                    </extraargs>
                                </wsdlOption>
                            </wsdlOptions>

                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
            <dependencies>
                <dependency>
                    <groupId>org.jvnet.jaxb2_commons</groupId>
                    <artifactId>jaxb2-basics-annotate</artifactId>
                    <version>1.0.2</version>
                </dependency>
            </dependencies>
        </plugin>
Bugbane answered 24/12, 2016 at 19:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.