Reputation: 8955
I am using SpringBoot with Java14. I am trying to write a SOAP client to consume an existing SOAP service.
I have created a SOAP client (consumer) below, but it gets an error when trying to run it.
I generated the related model objects from the wsdl with the following plugin in the pom:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>3.3.7</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${project.basedir}/src/main/java</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${project.basedir}/src/main/resources/wsdl/approval.wsdl</wsdl>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
Then I have the following client:
import com.travellinck.client.approval.ApprovalRequest;
import com.travellinck.client.approval.ApprovalResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.stereotype.Service;
import org.springframework.ws.client.core.WebServiceTemplate;
@Service
public class SOAPClientRioImpl implements SOAPClient {
Logger logger = LoggerFactory.getLogger(SOAPClientRioImpl.class);
@Autowired
private Jaxb2Marshaller marshaller;
private WebServiceTemplate template;
private static final String uri = "http://localhost:8088/approval?wsdl";
@Override
public ApprovalResponse submit(ApprovalRequest approvalRequest) {
try {
template = new WebServiceTemplate(marshaller);
ApprovalResponse approvalResponse = (ApprovalResponse) template.marshalSendAndReceive(uri, approvalRequest);
return approvalResponse;
} catch (Exception e) {
logger.error("There was an error calling "+uri +" with approvalRequest. ", e);
e.printStackTrace();
}
return null;
}
}
Everything compiles. When I run the client, I get the following error:
There was an error calling http://localhost:8088/approval?wsdl with approvalRequest.
org.springframework.oxm.MarshallingFailureException: JAXB marshalling exception; nested exception is javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.SAXException2: unable to marshal type "com.travellinck.client.approval.ApprovalRequest" as an element because it is missing an @XmlRootElement annotation]
at org.springframework.oxm.jaxb.Jaxb2Marshaller.convertJaxbException(Jaxb2Marshaller.java:948) ~[spring-oxm-5.3.0-M1.jar:5.3.0-M1]
at org.springframework.oxm.jaxb.Jaxb2Marshaller.marshal(Jaxb2Marshaller.java:713) ~[spring-oxm-5.3.0-M1.jar:5.3.0-M1]
at org.springframework.ws.support.MarshallingUtils.marshal(MarshallingUtils.java:81) ~[spring-ws-core-3.0.9.RELEASE.jar:na]
[com.sun.istack.SAXException2: unable to marshal type "com.travellinck.client.approval.ApprovalRequest" as an element because it is missing an @XmlRootElement annotation]
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.6.2.jar:1.6.2]
at org.springframework.oxm.jaxb.Jaxb2Marshaller.convertJaxbException(Jaxb2Marshaller.java:948)
The generated ApprovalRequest.java
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
import javax.xml.datatype.XMLGregorianCalendar;
import com.travellinck.client.approval.contact.PersonalInfo;
import com.travellinck.client.approval.evaluation.EvaluatorNomination;
import com.travellinck.client.approval.evaluation.strategy.EvaluationStrategy;
/**
* <p>Java class for ApprovalRequest complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* <complexType name="ApprovalRequest">
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="requiredConclusionTime" type="{http://www.w3.org/2001/XMLSchema}dateTime" minOccurs="0"/>
* <element name="approvalType" type="{http://www.w3.org/2001/XMLSchema}int" minOccurs="0"/>
* <element name="businessRequest" type="{http://www.w3.org/2001/XMLSchema}anyType"/>
* <element name="supportingInfo" type="{http://www.w3.org/2001/XMLSchema}anyType" maxOccurs="unbounded" minOccurs="0"/>
* <element name="messageToEvaluator" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* <element name="organisation" type="{http://www.w3.org/2001/XMLSchema}anyURI"/>
* <element name="resubmissionOf" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* <element name="requestedBy" type="{http://www.travellinck.com/client/approval/contact}PersonalInfo" minOccurs="0"/>
* <element name="nominatedEvaluator" type="{http://www.travellinck.com/client/approval/evaluation}EvaluatorNomination" minOccurs="0"/>
* <element name="evaluationStrategy" type="{http://www.travellinck.com/client/approval/evaluation/strategy}EvaluationStrategy" minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ApprovalRequest", propOrder = {
"requiredConclusionTime",
"approvalType",
"businessRequest",
"supportingInfo",
"messageToEvaluator",
"organisation",
"resubmissionOf",
"requestedBy",
"nominatedEvaluator",
"evaluationStrategy"
})
public class ApprovalRequest {
@XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar requiredConclusionTime;
protected Integer approvalType;
@XmlElement(required = true)
protected Object businessRequest;
@XmlElement(nillable = true)
protected List<Object> supportingInfo;
protected String messageToEvaluator;
@XmlElement(required = true)
@XmlSchemaType(name = "anyURI")
protected String organisation;
protected String resubmissionOf;
protected PersonalInfo requestedBy;
protected EvaluatorNomination nominatedEvaluator;
protected EvaluationStrategy evaluationStrategy;
/**
* Gets the value of the requiredConclusionTime property.
*
* @return
* possible object is
* {@link XMLGregorianCalendar }
*
*/
public XMLGregorianCalendar getRequiredConclusionTime() {
return requiredConclusionTime;
}
/**
* Sets the value of the requiredConclusionTime property.
*
* @param value
* allowed object is
* {@link XMLGregorianCalendar }
*
*/
public void setRequiredConclusionTime(XMLGregorianCalendar value) {
this.requiredConclusionTime = value;
}
/**
* Gets the value of the approvalType property.
*
* @return
* possible object is
* {@link Integer }
*
*/
public Integer getApprovalType() {
return approvalType;
}
/**
* Sets the value of the approvalType property.
*
* @param value
* allowed object is
* {@link Integer }
*
*/
public void setApprovalType(Integer value) {
this.approvalType = value;
}
/**
* Gets the value of the businessRequest property.
*
* @return
* possible object is
* {@link Object }
*
*/
public Object getBusinessRequest() {
return businessRequest;
}
/**
* Sets the value of the businessRequest property.
*
* @param value
* allowed object is
* {@link Object }
*
*/
public void setBusinessRequest(Object value) {
this.businessRequest = value;
}
/**
* Gets the value of the supportingInfo property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the supportingInfo property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getSupportingInfo().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Object }
*
*
*/
public List<Object> getSupportingInfo() {
if (supportingInfo == null) {
supportingInfo = new ArrayList<Object>();
}
return this.supportingInfo;
}
/**
* Gets the value of the messageToEvaluator property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getMessageToEvaluator() {
return messageToEvaluator;
}
/**
* Sets the value of the messageToEvaluator property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setMessageToEvaluator(String value) {
this.messageToEvaluator = value;
}
/**
* Gets the value of the organisation property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getOrganisation() {
return organisation;
}
/**
* Sets the value of the organisation property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setOrganisation(String value) {
this.organisation = value;
}
/**
* Gets the value of the resubmissionOf property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getResubmissionOf() {
return resubmissionOf;
}
/**
* Sets the value of the resubmissionOf property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setResubmissionOf(String value) {
this.resubmissionOf = value;
}
/**
* Gets the value of the requestedBy property.
*
* @return
* possible object is
* {@link PersonalInfo }
*
*/
public PersonalInfo getRequestedBy() {
return requestedBy;
}
/**
* Sets the value of the requestedBy property.
*
* @param value
* allowed object is
* {@link PersonalInfo }
*
*/
public void setRequestedBy(PersonalInfo value) {
this.requestedBy = value;
}
/**
* Gets the value of the nominatedEvaluator property.
*
* @return
* possible object is
* {@link EvaluatorNomination }
*
*/
public EvaluatorNomination getNominatedEvaluator() {
return nominatedEvaluator;
}
/**
* Sets the value of the nominatedEvaluator property.
*
* @param value
* allowed object is
* {@link EvaluatorNomination }
*
*/
public void setNominatedEvaluator(EvaluatorNomination value) {
this.nominatedEvaluator = value;
}
/**
* Gets the value of the evaluationStrategy property.
*
* @return
* possible object is
* {@link EvaluationStrategy }
*
*/
public EvaluationStrategy getEvaluationStrategy() {
return evaluationStrategy;
}
/**
* Sets the value of the evaluationStrategy property.
*
* @param value
* allowed object is
* {@link EvaluationStrategy }
*
*/
public void setEvaluationStrategy(EvaluationStrategy value) {
this.evaluationStrategy = value;
}
}
WSDL
<!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.5-b05 . -->
<!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.5-b05 . -->
<definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.travellinck.com/client/approval/service" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://www.travellinck.com/client/approval/service" name="ApprovalService">
<types>
<xsd:schema>
<xsd:import namespace="http://www.travellinck.com/client/approval/service" schemaLocation="http://localhost:8088/approval?xsd=1"/>
</xsd:schema>
<xsd:schema>
<xsd:import namespace="http://www.travellinck.com/client/approval/evaluation/strategy" schemaLocation="http://localhost:8088/approval?xsd=2"/>
</xsd:schema>
<xsd:schema>
<xsd:import namespace="http://www.travellinck.com/client/approval/evaluation" schemaLocation="http://localhost:8088/approval?xsd=3"/>
</xsd:schema>
<xsd:schema>
<xsd:import namespace="http://www.travellinck.com/client/approval/contact" schemaLocation="http://localhost:8088/approval?xsd=4"/>
</xsd:schema>
<xsd:schema>
<xsd:import namespace="http://www.travellinck.com/client/approval" schemaLocation="http://localhost:8088/approval?xsd=5"/>
</xsd:schema>
</types>
<message name="submitRequestForApproval">
<part xmlns:ns1="http://www.travellinck.com/client/approval" name="approvalRequest" element="ns1:approvalRequest"/>
</message>
<message name="submitRequestForApprovalResponse">
<part xmlns:ns2="http://www.travellinck.com/client/approval" name="approvalResponse" element="ns2:approvalResponse"/>
</message>
<message name="UnsupportedBusinessRequest">
<part name="fault" element="tns:unsupportedBusinessRequest"/>
</message>
<message name="UnsupportedSupportingDocument">
<part name="fault" element="tns:unsupportedSupportingDocument"/>
</message>
<message name="getApprovalState">
<part xmlns:ns3="http://www.travellinck.com/client/approval" name="getApprovalStateRequest" element="ns3:getApprovalStateRequest"/>
</message>
<message name="getApprovalStateResponse">
<part xmlns:ns4="http://www.travellinck.com/client/approval" name="getApprovalStateResponse" element="ns4:getApprovalStateResponse"/>
</message>
<message name="UnknownApprovalRequest">
<part name="fault" element="tns:unknownApprovalRequest"/>
</message>
<message name="withdrawRequestForApproval">
<part xmlns:ns5="http://www.travellinck.com/client/approval" name="withdrawApprovalRequest" element="ns5:withdrawApprovalRequest"/>
</message>
<message name="withdrawRequestForApprovalResponse">
<part xmlns:ns6="http://www.travellinck.com/client/approval" name="withdrawApprovalResponse" element="ns6:withdrawApprovalResponse"/>
</message>
<message name="InvalidRequest">
<part name="fault" element="tns:invalidRequest"/>
</message>
<message name="RequestNoLongerInProgress">
<part name="fault" element="tns:requestNoLongerInProgress"/>
</message>
<portType name="Approval">
<operation name="submitRequestForApproval">
<input wsam:Action="http://www.travellinck.com/client/approval/service/Approval/submitRequestForApprovalRequest" message="tns:submitRequestForApproval"/>
<output wsam:Action="http://www.travellinck.com/client/approval/service/Approval/submitRequestForApprovalResponse" message="tns:submitRequestForApprovalResponse"/>
<fault message="tns:UnsupportedBusinessRequest" name="UnsupportedBusinessRequest" wsam:Action="http://www.travellinck.com/client/approval/service/Approval/submitRequestForApproval/Fault/UnsupportedBusinessRequest"/>
<fault message="tns:UnsupportedSupportingDocument" name="UnsupportedSupportingDocument" wsam:Action="http://www.travellinck.com/client/approval/service/Approval/submitRequestForApproval/Fault/UnsupportedSupportingDocument"/>
</operation>
<operation name="getApprovalState">
<input wsam:Action="http://www.travellinck.com/client/approval/service/Approval/getApprovalStateRequest" message="tns:getApprovalState"/>
<output wsam:Action="http://www.travellinck.com/client/approval/service/Approval/getApprovalStateResponse" message="tns:getApprovalStateResponse"/>
<fault message="tns:UnknownApprovalRequest" name="UnknownApprovalRequest" wsam:Action="http://www.travellinck.com/client/approval/service/Approval/getApprovalState/Fault/UnknownApprovalRequest"/>
</operation>
<operation name="withdrawRequestForApproval">
<input wsam:Action="http://www.travellinck.com/client/approval/service/Approval/withdrawRequestForApprovalRequest" message="tns:withdrawRequestForApproval"/>
<output wsam:Action="http://www.travellinck.com/client/approval/service/Approval/withdrawRequestForApprovalResponse" message="tns:withdrawRequestForApprovalResponse"/>
<fault message="tns:InvalidRequest" name="InvalidRequest" wsam:Action="http://www.travellinck.com/client/approval/service/Approval/withdrawRequestForApproval/Fault/InvalidRequest"/>
<fault message="tns:RequestNoLongerInProgress" name="RequestNoLongerInProgress" wsam:Action="http://www.travellinck.com/client/approval/service/Approval/withdrawRequestForApproval/Fault/RequestNoLongerInProgress"/>
<fault message="tns:UnknownApprovalRequest" name="UnknownApprovalRequest" wsam:Action="http://www.travellinck.com/client/approval/service/Approval/withdrawRequestForApproval/Fault/UnknownApprovalRequest"/>
</operation>
</portType>
<binding name="ApprovalPortBinding" type="tns:Approval">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="submitRequestForApproval">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
<fault name="UnsupportedBusinessRequest">
<soap:fault name="UnsupportedBusinessRequest" use="literal"/>
</fault>
<fault name="UnsupportedSupportingDocument">
<soap:fault name="UnsupportedSupportingDocument" use="literal"/>
</fault>
</operation>
<operation name="getApprovalState">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
<fault name="UnknownApprovalRequest">
<soap:fault name="UnknownApprovalRequest" use="literal"/>
</fault>
</operation>
<operation name="withdrawRequestForApproval">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
<fault name="InvalidRequest">
<soap:fault name="InvalidRequest" use="literal"/>
</fault>
<fault name="RequestNoLongerInProgress">
<soap:fault name="RequestNoLongerInProgress" use="literal"/>
</fault>
<fault name="UnknownApprovalRequest">
<soap:fault name="UnknownApprovalRequest" use="literal"/>
</fault>
</operation>
</binding>
<service name="ApprovalService">
<port name="ApprovalPort" binding="tns:ApprovalPortBinding">
<soap:address location="http://localhost:8088/approval"/>
</port>
</service>
</definitions>
UPDATE
Thanks to the help of Joe, I have added the following to the POM:
<wsdlOption>
<wsdl>${project.basedir}/src/main/resources/wsdl/approval.wsdl</wsdl>
<bindingFiles>
<bindingFile>${basedir}/src/main/resources/wsdl/BindingFile.xjb</bindingFile>
</bindingFiles>
<extraargs><extraarg>-xjc-Xannotate</extraarg></extraargs>
</wsdlOption>
BindingFile.xjb
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxb:bindings version="2.0"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:extensionBindingPrefixes="annox xjc"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:annox="http://annox.dev.java.net"
xmlns:inheritance="http://jaxb2-commons.dev.java.net/basic/inheritance">
<jaxb:bindings schemaLocation="approval.wsdl#types1" node="/xs:schema">
<jaxb:bindings node="//xs:complexType[@name='ApprovalRequest']">
<annox:annotate target="class">
<annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="approvalRequest"/>
</annox:annotate>
</jaxb:bindings>
<jaxb:bindings node="//xs:complexType[@name='ApprovalResponse']">
<annox:annotate target="class">
<annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="approvalResponse"/>
</annox:annotate>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
However, when I do a maven build, I get the following error:
[WARNING] Exception in thread "main" org.apache.cxf.tools.common.ToolException: file:/Users/richardmarais/IdeaProjects/nexct-approval-service/src/main/resources/wsdl/BindingFile.xjb [12,73]: XPath evaluation of "//xs:complexType[@name='ApprovalRequest']" results in empty target node
[WARNING] file:/Users/richardmarais/IdeaProjects/nexct-approval-service/src/main/resources/wsdl/BindingFile.xjb [17,74]: XPath evaluation of "//xs:complexType[@name='ApprovalResponse']" results in empty target node
Upvotes: 0
Views: 3908
Reputation: 8955
I fixed this by not binding, but rather converting the object to a jaxb element.
template = new WebServiceTemplate(marshaller);
ObjectFactory objectFactory = new ObjectFactory();
JAXBElement<ApprovalRequest> jaxbApprovalRequest = objectFactory.createApprovalRequest(approvalRequest);
ApprovalResponse response = (ApprovalResponse) template
.marshalSendAndReceive("http://localhost:8088/approval", jaxbApprovalRequest);
System.out.println(response);
return response;
Upvotes: 1
Reputation: 2891
You may need to provide a binding file so that the generated class gets the @XMLRootElement
annotation so that it can be unmarshalled by the default marshaller.
From the second answer here: I would like to add @XmlRoot annotation on CXF codegen
Add the following fragments to POM.XML (replacing the binding file with the name you choose):
<wsdlOption>
<wsdl>${project.basedir}/src/main/resources/wsdl/approval.wsdl</wsdl>
<bindingFiles>
<bindingFile>${basedir}/src/main/resources/wsdl/BindingFile.xjb</bindingFile>
</bindingFiles>
<extraargs><extraarg>-xjc-Xannotate</extraarg></extraargs>
</wsdlOption>
XJB
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxb:bindings version="2.0"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:extensionBindingPrefixes="annox xjc"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:annox="http://annox.dev.java.net"
xmlns:inheritance="http://jaxb2-commons.dev.java.net/basic/inheritance">
<jaxb:bindings schemaLocation="CP_Ablakido_1.wsdl#types1" node="/xs:schema">
<jaxb:bindings node="//xs:complexType[@name='InputMapping1']">
<annox:annotate target="class">
<annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="GetList_11"/>
</annox:annotate>
</jaxb:bindings>
<jaxb:bindings node="//xs:complexType[@name='OutputMapping1']">
<annox:annotate target="class">
<annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="GetList_11Response"/>
</annox:annotate>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
Add these dependencies to your pom:
<dependency>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics-annotate</artifactId>
<version>0.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf.xjcplugins</groupId>
<artifactId>cxf-xjc-ts</artifactId>
<version>3.0.5</version>
</dependency>
Upvotes: 0