spring boot cxf soap ws-modify namespace - spring-boot

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

Related

WSDL generated Jaxb Add namespace prefix to only root element

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.

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?

Apache Camel dataFormat

Im using Camel to handle requests from a webservice and route those request to somewhere else (i will send them to RabbitMQ, but this is not relevant for my question).
Im using the standard Dataformat in camel-cxf endpoint which is POJO, but when i receive the packet in the camel route, im getting something that im not expecting.
Let me show you what i have right now, and i hope someone can point me in the right direction :)
Im using SOAPUI to send the request to the webservice:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:es:tf:mm:types">
<soapenv:Header/>
<soapenv:Body>
<urn:PromoEvent>
<urn:id>601234567</urn:id>
<urn:text>qwerty</urn:text>
<urn:date>01012001</urn:date>
</urn:PromoEvent>
</soapenv:Body>
</soapenv:Envelope>
Now, I have the following cxf endpoint bean definition in my camel-context.xml file, which by default, uses the POJO dataformat:
<cxf:cxfEndpoint id="Promo"
address="/Promo"
serviceClass="es.tf.mm.mmPortType"/>
And finally, i defined a camel route where im printing some logs and following you can see the results:
<route id="fromMySOAPws" autoStartup="true">
<from uri="cxf:bean:Promo"/>
<log message="Body: ${body}"/>
<log message="Body in: ${in.body}"/>
<log message="Body 0: ${in.body[0]}"/>
<log message="Body 1: ${in.body[1}"/>
<log message="Body 2: ${in.body[2]}"/>
<log message="Body 3: ${in.body[3]}"/>
<transform>
<simple>${in.body[0]}</simple>
</transform>
<unmarshal ref="jaxbWsDf"/>
<process ref="PromoProcessRequest"/>
<to uri="rabbitmq://rabbitmqhostname:5672/exchange_name?connectionFactory=#customConnectionFactory&autoDelete=false"/>
<bean ref="PromoResponse" method="generateResponse(0,"event published in rabbitmq")"/>
</route>
Here you can see the PromoProcessRequest Java class:
public class PromoProcessRequest implements Processor {
#Override
public void process(Exchange exchange) throws Exception {
EventPromoType request = (EventPromoType)exchange.getIn().getBody();
Map<String, Object> eventFields = new LinkedHashMap<String, Object>();
eventFields.put("id", request.getId());
eventFields.put("text", request.getText());
eventFields.put("date", request.getDate());
exchange.getOut().setBody(eventFields);
}
}
Results:
Body: qwerty
Body in: qwerty
Body 0: javax.xml.ws.Holder#6ecf43c8
Body 1: qwerty
Body 2: 1012001
Body 3:
Thank you all in advance :)
#EDIT:
As Souciance correctly asked, what is unexpected for me, is to have just one of the fields (text:'qwerty') as the whole {body} of the request.
Since im using the standard POJO dataFormat, i need to call a process before Camel sends the message to rabbit and create a JSON with all the fields.
Im updating the code to show you the whole Camel route and also to include the process method which is where im having the issues, since im trying to cast the body of the request.
Hope this clarifies everything a little bit, but ofc, feel free to ask for more details if needed.
#EDIT2:
As fiw asked, here is the jaxbWsDf definition which is declared inside the camelContext tag:
<dataFormats>
<jaxb id="jaxbWsDf" prettyPrint="true" contextPath="es.tf.mm.types"/>
</dataFormats>
Also i would like to replicate here out my answer to the fiw second comment. When i compile the wsdl, in the auto-generated Java class, i cant see the #XmlRootElement and i think this is because the wsdl schema is declared in a external .xsd file. Am i right? Any other idea of why that XmlRootElement does not appear after compiling my webservice?
Thanks again!

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.

Consume webservice service in SPRING-WS using wsdl

I have WSDL with me .eg: /sample/hello?wsdl . I want to invoke the service the webservice by configuring in Spring-ws. I passed this wsdl as parameter to tags in springconfig.xml.
Can anyone please tell me how to consume this webservice in Spring-ws.
1. Set up project dependencies
add the following dependencies to the pom file:
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2.5</version>
</dependency>
2. Set up web service application context
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="com.yourcomany.model" />
</bean>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory" />
<property name="marshaller" ref="marshaller"></property>
<property name="unmarshaller" ref="marshaller"></property>
<property name="messageSender">
<bean
class="org.springframework.ws.transport.http.HttpComponentsMessageSender" />
</property>
<property name="defaultUri"
value="http://<hostname>:<portnumber>/sample/hello" />
</bean>
</beans>
3. Set up model classes which would map to your SOAP request/response objects
For example, if your SOAP request XML looked like
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xxx="http://yourcomapny.com">
<soapenv:Header/>
<soapenv:Body>
<xxx:InputParameters>
<xxx:paramONE>1</xxx:paramONE>
</xxx:InputParameters>
</soapenv:Body>
</soapenv:Envelope>
and your SOAP response XML looked like:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header>
...
</env:Header>
<env:Body>
<xxx:OutputParameters xmlns:xxx="http://yourcompany.com">
<xxx:paramONE>0</xxx:paramONE>
</xxx:OutputParameters>
</env:Body>
</env:Envelope>
the corresponding classes (under the package you specified in the marshaller bean: com.yourcompany.model) would be respectively:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "paramONE" })
#XmlRootElement(name = "InputParameters", namespace = "http://yourcompany.com")
public class InputParameters {
#XmlElement(required = true, namespace = "http://yourcompany.com")
private String paramONE;
public String getParamONE() {
return paramONE;
}
public void setParamONE(String paramONE) {
this.paramONE = paramONE;
}
}
and
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "paramONE" })
#XmlRootElement(name = "OutputParameters", namespace = "http://yourcompany.com")
public class OutputParameters {
#XmlElement(required = true, namespace = "http://yourcompany.com")
private BigDecimal paramONE;
public BigDecimal getParamONE() {
return this.paramONE;
}
public void setParamONE(BigDecimal paramONE) {
this.paramONE= paramONE;
}
}
4. Add an Object Factory (under package com.yourcompany.model) to create request/response objects
#XmlRegistry
public class ObjectFactory {
public ObjectFactory() {
}
public InputParameters createYourRequest() {
return new InputParameters();
}
public OutputParameters createYourResponse() {
return new OutputParameters();
}
}
5. Create a client to consume the service
Interface:
public interface YourService {
BigDecimal getValue(String paramOne);
}
Implementation
#Component("yourServiceClient")
public class YourServiceClient implements YourService {
private static final ObjectFactory WS_CLIENT_FACTORY = new ObjectFactory();
private WebServiceTemplate webServiceTemplate;
#Autowired
public YourServiceClient(WebServiceTemplate webServiceTemplate) {
this.webServiceTemplate = webServiceTemplate;
}
#Override
public BigDecimal getValue(String paramOne) {
InputParameters request = WS_CLIENT_FACTORY
.createYourRequest();
request.setParamONE(paramOne);
OutputParameters response = (OutputParameters) webServiceTemplate
.marshalSendAndReceive(request);
return response.getParamONE();
}
}
#Taoufik Mohdit answer is complete!!
To build the input and output objects you can use Webservice-Client: Common approach with Spring WS, JAXB and just one WSDL file? to some how build these objects automatically
Given that this question is still active I thought I would post an update that reflects a number of changes that the recent version of the Spring Web Services framework and Spring in general introduce:
The introduction of Spring Boot allows to leverage 'starter' POMs to simplify your Maven configuration. There is a specific spring-boot-starter-web-services starter for Spring-WS
Instead of specifying Spring configuration files using XML, Spring JavaConfig was introduced which provides a type-safe, pure-Java option for configuring Spring.
Generation of request/response objects based on a given WSDL file can be automated using Maven plugins. The plugin used by the Spring-WS examples is the maven-jaxb2-plugin.
The WebServiceTemplate is still the core class for client-side Web service access. For more information you can check this detailed example on how to consume a web service using Spring-WS starting from a WSDL file that I wrote.

Resources