WSDL generated Jaxb Add namespace prefix to only root element - spring

I am creating a soap web service using jaxb2 plugin. Hence I have generated jaxb classes from wsdl file. I have entered the sample of generated codes here.
//ObjectFactory.java
#XmlElementDecl(namespace = "http://xxxxx/", name = "InquiryResponse")
public JAXBElement<InquiryResponse> createInquiryResponse(InquiryResponse value) {
return new JAXBElement<InquiryResponse>(_InquiryResponse_QNAME, InquiryResponse.class, null, value);
}
//package-info.java
#javax.xml.bind.annotation.XmlSchema(namespace = "http://XXXX/",
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.UNQUALIFIED,
xmlns = {
#XmlNs(namespaceURI = "http://XXXX/", prefix = "ws")
})
package X.X.X;
import javax.xml.bind.annotation.XmlNs;
And the response is
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<InquiryResponse xmlns:ws="http://XXXX/">
<InquiryResponse>
<refId>0004440</refId>
<status>UP</status>
</InquiryResponse>
</InquiryResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
But what I wanted is
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ws:InquiryResponse xmlns:ws="http://XXXX/">
<InquiryResponse>
<refId>0004440</refId>
<status>UP</status>
</InquiryResponse>
</InquiryResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I am using a spring boot. Please help me. Thanks.

I believe that happens because you specified elementFormDefault as UNQUALIFIED in your package-info.java. Try switching this to QUALIFIED to see if it helps.

Related

spring boot cxf soap ws-modify namespace

I am working on spring boot soap-ws project with contract last approach using CXF. I have xml request from a client as below.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<teen xmlns="some url">
<!--Optional:-->
<arg0>
//DATA
</arg0>
</teen>
</soapenv:Body>
</soapenv:Envelope>
The service is not accepting the above request. I would like to change the xml request to below(service accepts below message). The arg0 should have an added empty namespace.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:se>
<soapenv:Header/>
<soapenv:Body>
<teen xmlns="some url">
<!--Optional:-->
<arg0 xmlns="">
//DATA
</arg0>
</teen>
</soapenv:Body>
</soapenv:Envelope>
Can someone help me on how I can achieve this?
I did some research and here is the answer. I have created a below class and implemented the handlemethod of extended class. The handlemethod does all the xml modification.
public class InvalidCharInterceptor extends AbstractPhaseInterceptor<Message> {
public InvalidCharInterceptor() {
super(Phase.RECEIVE); // This is important which determines at what point are we modifying the xml.
}
public void handleMessage(Message message) throws Fault {
}
}
And, then I added the new interceptor class in my webserviceconfig class.
EndpointImpl endpoint = new EndpointImpl(bus, "service-class-name");
endpoint.getInInterceptors().add(new InvalidCharInterceptor());
FYI..Apache cxf has detailed explanation on interceptors. https://cxf.apache.org/docs/interceptors.html

How to declare a element for complex data type defined in a xsd file located in jar file

I have a below xsd file in a jar dependency file in a Spring boot project. The xsd does not declare the element declaration, which is required by spring boot validation. So I am trying to add another xsd in my project resource folder to declare element type.
XSD in Jar file: /wsdl/xsd/UserService-v1-0.xsd
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema targetNamespace="http://usermanagement.com/userservice/xsd/2014/07"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:userservice="http://usermanagement.com/userservice/xsd/2014/07"
elementFormDefault="qualified">
<xsd:import namespace="http://usermanagement.com/userservice/common/xsd/2014/09" schemaLocation="userserviceCommon-v1-0.xsd"/>
<xsd:import namespace="http://usermanagement.com/userserviceabsolute/common/xsd/2014/09" schemaLocation="userserviceAbsoluteCommon-v1-0.xsd"/>
<xsd:complexType name="GetUsersRequest">
<xsd:attribute name="language" type="xsd:language" use="optional" default="en" />
</xsd:complexType>
</xsd:schema>
Please note that I tried to keep the complex type simple by removing some of the elements for this post. The location of file is /wsdl/xsd.
XSD to declare the element in the main Project resource: /wsdl/xsd/UserService-type-v1-0.xsd
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema targetNamespace="http://usermanagement.com/userservice/wsdl/userserviceService-v1-0"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:userservice="http://usermanagement.com/userservice/xsd/2014/07"
elementFormDefault="qualified">
<import namespace="http://usermanagement.com/userservice/xsd/2014/07" schemaLocation="jar:file://{path to the jar}/!/wsdl/xsd/userserviceService-v1-0.xsd"/>
<element name="GetUsersRequest" type="userservice:GetUsersRequest"/>
</xsd:schema>
As per my understanding we need to refer to the schemaLocation of xsd file in jar using notation "jar:file://{path to the jar}/!/wsdl/xsd/userserviceService-v1-0.xsd".
However I am not sure how to declare the path to the jar. Also not sure if this is the only way to refer to the definitions in the xsd in the jar files. In the above approach when ever a new version of jar is released we need to update the xsd file.
Also including the Spring WS schema validation config:
#Configuration
public class MyWsValidatorConfig extends WsConfigurerAdapter {
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
PayloadValidatingInterceptor validatingInterceptor = new PayloadValidatingInterceptor();
validatingInterceptor.setValidateRequest(true);
validatingInterceptor.setValidateResponse(true);
validatingInterceptor.setXsdSchemaCollection(new XsdSchemaCollection() {
#Override
public XsdSchema[] getXsdSchemas() {
return null;
}
#Override
public XmlValidator createValidator() {
try {
return XmlValidatorFactory.createValidator(getSchemas(), "http://www.w3.org/2001/XMLSchema");
} catch (Exception e) {
log.error("Failed to create validator e={}", e);
}
return null;
}
public Resource[] getSchemas() {
List<Resource> schemaResources = new ArrayList<>();
schemaResources.add(new ClassPathResource("/wsdl/xsd/UserService-v1-0.xsd"));
schemaResources.add(new ClassPathResource("/wsdl/xsd/UserService-type-v1-0.xsd"));
return schemaResources.toArray(new Resource[schemaResources.size()]);
}
});
interceptors.add(validatingInterceptor);
}
}
This is the first time I am working with XSD and SOAP web services. Able to cross other hurdles but got stuck with this issue.
Please help.

Spring-WS unable to set FaultCode.RECEIVER

I cannot set FaultCode.RECEIVER for my fault message;
The code in response is always SERVER
Here is my response:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header />
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring xml:lang="en">Something wrong!</faultstring>
<faultactor>http://fault.actor.com/actor/</faultactor>
<detail>
<ServiceException>
<code>ABCD</code>
<message>Invalid input</message>
</ServiceException>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The fault code is SOAP-ENV:Server instead of SOAP-ENV:Receiver
Heres my exception class
#SoapFault(faultCode = FaultCode.RECEIVER, faultStringOrReason = "Something wrong!")
public class CustomFault extends RuntimeException
When i'm putting faultCode = FaultCode.CLIENT, it changes in response to Client, but with RECEIVER doesn't work.
For adding fault details in response i'm using SoapFaultAnnotationExceptionResolver
Any ideas?

Soap webservice response object missing xmlns attributes for #PayloadRoot method

I am developing a SOAP webservice application written in Spring soap webservice
The application simply returns the same object it receives from the client as response.
In CountryEndpoint.java
#PayloadRoot(namespace = "http://xml.dexter.com/Country", localPart = "CountryLocalPart")
public Country getCountry(SoapHeader soapHeader, #RequestPayload Country request, #SoapHeader(value = "MessageHeader") SoapHeaderElement messageHeader, MessageContext messageContext) {
System.out.println("THE REQUEST: "+request);
return request;
}
The request SOAP XML
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header />
<SOAP-ENV:Body>
<ns2:Country xmlns="http://example.org" xmlns:ns2="http://xml.dexter.org/Country" xmlns:h="http://pqr.com/pqr.xsd xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<ns2:countryName>Spain</ns2:countryName>
<ns2:population>1213123</ns2:population>
</ns2:Country>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The response SOAP XML
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header />
<SOAP-ENV:Body>
<ns2:Country xmlns:ns2="http://xml.dexter.com/Country">
<ns2:countryName>Spain</ns2:countryName>
<ns2:population>1213123</ns2:population>
</ns2:Country>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The Country XML Root element seems to have the xmlns attributes missing.
This fails the JAXB unmarshalling step in the client.
The error is
ERROR: org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].
[/].[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in
context with path [] threw exception [Request processing failed; nested
exception is org.springframework.oxm.UnmarshallingFailureException: JAXB
unmarshalling exception; nested exception is javax.xml.bind.UnmarshalException:
unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/",
local:"Header"). Expected elements are <{http://www.....}Country>,<{http:
//www.....}Category>,<{http://www.....}SomeOther>] with root cause
javax.xml.bind.UnmarshalException: unexpected element
(uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Header"). Expected
elements are ...
I tried adding #PayloadRoots and also #Namespaces with array of #Namespace but nothing worked.
Is there a way I could have the response SOAP xml same as request SOAP xml?
Thanks!
The namespace URIs in the Country element on the request with the prefixes h, s, and default are superfluous as they don't correspond to any XML elements or attributes. There would be no reason to send them on the response and they would impact JAXB's ability to unmarshal the result assuming everything is properly mapped.
Based on your exception. Your JAXB client is trying to unmarshal the Header element and you need to unmarshal the Country element.

Returning JAXB-generated elements from Spring Boot Controller

I'm generating a plethora of Java files from http://www.ncpdp.org's XSD files (only available to members). After generating them, I'd like to use them in my Spring Controllers, but I'm having problems getting responses converted to XML.
I've tried returning the element itself, as well as JAXBElement<T>, but neither seems to work. The test below fails:
java.lang.AssertionError: Status
Expected :200
Actual :406
#Test
public void testHelloWorld() throws Exception {
mockMvc.perform(get("/api/message")
.accept(MediaType.APPLICATION_XML)
.contentType(MediaType.APPLICATION_XML))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_XML));
}
Here's my Controller:
import org.ncpdp.schema.transport.MessageType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class HelloWorldController {
#RequestMapping(value = "/api/message", method = RequestMethod.GET)
public MessageType messageType() {
return new MessageType();
}
}
I've tried creating a MvcConfig to override Spring Boot's MVC config, but it doesn't seem to be working.
#Configuration
#EnableWebMvc
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(marshallingHttpMessageConverter());
}
#Bean
public MarshallingHttpMessageConverter marshallingHttpMessageConverter() {
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setPackagesToScan(new String[]{"com.ncpdb.schema.transport"});
MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter();
converter.setMarshaller(jaxb2Marshaller);
converter.setUnmarshaller(jaxb2Marshaller);
converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_XML));
return converter;
}
}
What do I need to do to get Spring MVC to marshall my generated JAXB objects as XML?
I was able to solve this by creating a bindings.xjb file in the same directory as my schemas. This causes JAXB to generate #XmlRootElement on classes.
<?xml version="1.0"?>
<jxb:bindings version="1.0"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd">
<jxb:bindings schemaLocation="transport.xsd" node="/xsd:schema">
<jxb:globalBindings>
<xjc:simple/>
</jxb:globalBindings>
</jxb:bindings>
</jxb:bindings>
To add namespaces prefixes to the returned XML, I had to modify the maven-jaxb2-plugin to add a couple arguments.
<arg>-extension</arg>
<arg>-Xnamespace-prefix</arg>
And add a dependency:
<dependencies>
<dependency>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-namespace-prefix</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
Then modify my bindings.xjb to include this:
<?xml version="1.0"?>
<jxb:bindings version="1.0"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:namespace="http://jaxb2-commons.dev.java.net/namespace-prefix"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd
http://jaxb2-commons.dev.java.net/namespace-prefix http://java.net/projects/jaxb2-commons/sources/svn/content/namespace-prefix/trunk/src/main/resources/prefix-namespace-schema.xsd">
<jxb:bindings schemaLocation="transport.xsd" node="/xsd:schema">
<jxb:globalBindings>
<xjc:simple/>
</jxb:globalBindings>
<jxb:schemaBindings>
<jxb:package name="org.ncpdp.schema.transport"/>
</jxb:schemaBindings>
<jxb:bindings>
<namespace:prefix name="transport"/>
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>
I learned how to do this from https://java.net/projects/jaxb2-commons/pages/Namespace-prefix. I also found http://blog.frankel.ch/customize-your-jaxb-bindings to be a good resource on how to customize JAXB bindings.

Resources