简体   繁体   中英

Spring Soap web service client using a WSDL and codehaus jaxb2; need XmlRootElement?

I am working within an existing Spring web application; I need to write code to call to a SOAP web service that provides a WSDL. I also use Netbeans, though am willing to jump out to a command line whenever that's easier.

I have configured the Maven pom for the project to include the codehaus jaxb2-maven-plugin, and written a small test program to send a request. When that call is executed, I get an error saying that it cannot process one of the generated classes because it has no XmlRootElement annotation.

On searching further for that error, I find LOTS of requests for information on it, but none that apply. Most of them use a different JaxB library, and all of them give me examples of how to configure their plugin, not the one I have.

I suppose I can change plugins (I already have once), but what I would REALLY like is to find some decent documentation on THIS one. I need something that can be done on Java 7, not 8, that does not involve Spring 4 (or 5, or 6), preferably just an explanation of the various options that can be supplied to the maven plugin and/or the command line to generate classes so they can be marshalled and unmarshalled by Spring's default classes for the purposes.

--- Edit

Here's what I have at the moment; since it's testing code, I just have the marshaller declared and set up in the code instead of the configuration:

public class XClient extends WebServiceGatewaySupport {
    public GetXResponse getXResponse(GetX XRequest) {
        // do in configuration for production...
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setContextPath("com.X.pics.service");
        setMarshaller(marshaller);
        setUnmarshaller(marshaller);

        String uri = "https://site.X.com/services/X";
        WebServiceTemplate template = getWebServiceTemplate();
        Object response = template.marshalSendAndReceive(uri,
                                                         XRequest
                                                         );
        GetXResponse getXResponse = (GetXResponse) response;
        return getXResponse;
    }

}

When I run my program, it gives the following (just first lines):

org.springframework.oxm.MarshallingFailureException: JAXB marshalling exception; nested exception is javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.internal.SAXException2: unable to marshal type "com.xo.pics.service.GetPricing" as an element because it is missing an @XmlRootElement annotation]
    at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:237)
    at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:322)
    at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:483)

I put in the line about preferring Spring libraries since a lot of answers on SO seem to take the form of "Change over to this tool/library", not because I didn't think it could be done in Spring. I'll be happy to configure and use a marshaller within Spring, as soon as I figure out (or am told) how.

I finally got this working; it does seem like it ought to be easier.

The POM file entry for the JAXB Maven plugin now looks like this:

        <plugin>
          <groupId>org.codehaus.mojo</groupId>
          <artifactId>jaxb2-maven-plugin</artifactId>
          <version>2.2</version>
          <executions>
              <execution>
                  <id>xjc</id>
                  <goals>
                      <goal>xjc</goal>
                  </goals>
              </execution>
          </executions>
          <configuration>
              <sourceType>wsdl</sourceType>
              <sources>
                  <source>C:/projects/gw/src/main/resources/wsdl/xo2.wsdl</source>
              </sources>
              <extension>true</extension>
              <xjbSources>
                  <xjbSource>bindings.xjb</xjbSource>
              </xjbSources>
          </configuration>
        </plugin>

One of the difficulties I faced was a lack of documentation on the options here; I eventually looked inside the jar file in the plugin code and found the jaxb2-maven-plugin.pom there, and looked through the unformatted documentation for the various options. This is how I discovered the "sourceType", "sources", and "xjbSources" tags. It was also looking in that part of my .m2 directory that helped me realize the different versions available, and the docs available on the web do warn you of the major differences between 1.x and 2.x.

Anyway, I had found the following bindings file, though wasn't sure until later how to specify a bindings file in this version of the plugin:

<?xml version="1.0"?>
<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
              xmlns:xjc= "http://java.sun.com/xml/ns/jaxb/xjc"
              jxb:extensionBindingPrefixes="xjc" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <jxb:bindings>
        <jxb:globalBindings>
            <xjc:simple/>
        </jxb:globalBindings>
    </jxb:bindings>
</jxb:bindings>

The specification of in the bindings is what causes the @XmlRootElement in the generated sources; you need this if your WSDL has complex types that have a defined name.

Then my client boiled down to this:

public class XOClient extends WebServiceGatewaySupport {
    public GetPricingResponse getPricingResponse(GetPricing pricingRequest) {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setClassesToBeBound(GetPricing.class, GetPricingResponse.class);
        setMarshaller(marshaller);
        setUnmarshaller(marshaller);


        String uri = "https://blah.blah.blah.com/services/pricing";
        Object o = getWebServiceTemplate().marshalSendAndReceive(uri, pricingRequest);
        GetPricingResponse response = (GetPricingResponse)o;
        return response;
    }
}

In the final Spring application, I would be more likely to configure the marshaller URI and bound classes instead of writing code to set them, but to use in my little testing program this was easier.

So this allows me to build up my pricingRequest object and get back a GetPricingResponse object, and use any of the other API methods similarly.

Hope this is a help to someone else.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM