Spring-WS: how to use WebserviceTemplate with pre-generated SOAP-envelope - java

Can you use a Spring-WS WebserviceTemplate for calling a webservice and avoid that it generates a SOAP-envelope? That is, the message already contains an SOAP-Envelope and I don't want that the WebserviceTemplate wraps another one around it. :-)
The reason I want this is that I'd like to call a webservice that uses ws-security and do not want to put the ws-security stuff into the WebserviceTemplate, but just want to feed it a message with pre-generated ws-security information in the SOAP-envelope. I tried calling the method sendSourceAndReceiveToResult with a Source already contains a Soap-Envelope with the WS-Security stuff and the webservice template wraps around another Soap-Envelope and thus destroys the message.

You're using ws-security in a strange way... I guess that you're trying to avoid ws-security dependancy by using pre-generated messages - for simple client might make sense, although it's definitely not by-the-book.
You can configure WebServiceTemplate to use plain XML without SOAP by setting messageFactory on WebServiceTemplate to this bean:
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<property name="messageFactory" ref="poxMessageFactory" />
</bean>
<bean id="poxMessageFactory" class="org.springframework.ws.pox.dom.DomPoxMessageFactory" />

Interceptors can come in handy for the sort of thing you are trying to do. Take a look at the Interceptor hierarchy here: http://static.springframework.org/spring-ws/docs/1.0-m1/api/org/springframework/ws/EndpointInterceptor.html
You can register an EndpointInterceptor with spring-ws and manipulate the response to your liking.

Related

Spring-integration: how correctly to call SOAP web-service?

SOAP WS with trivial characteristics:
- WSDL address:
http://<ip>:<port>/service/name?wsdl
- target method name:
getData(...)
Part of Spring-integration config ('spring-context.xml') for calling WS:
...
<int:chain input-channel="data" output-channel="stdout">
<!-- transform message to request -->
<int:transformer
ref="soapRequestTransformer"
method="createReq">
</int:transformer>
<!-- define the WS method to be called (getData) -->
<int:header-enricher>
<int:header
name="SoapAction"
value="http://<ip>:<port>/service/name/getData"/>
</int:header-enricher>
<!-- call WS -->
<int-ws:outbound-gateway uri="http://<ip>:<port>/service/name"/>
</int:chain>
...
After transformer (successful) i have request object:
request.getHeaders(): {sequenceNumber=1, correlationId=2bfb560c-
96ba-9c35-96dc-ba16104604b9, id=32f43a24-dee2-461c-a9b9-92c8ee37aaec,
sequenceSize=12, timestamp=1532509142590}
request.getPayload(): org.types.GetDataRequest#77409e4b
How to properly setup the spring-integration to correctly call the WS?
Problem is that WS is not called.
How does this problem look? We can't say anything without at least some stack trace.
Is the name of the WS method used correctly?
The SoapAction value is vendor-specific. You have to consult the WS provided to determine what and how should be presented in that header.
How are the parameters passed?
Not sure about nay parameter. Yes, you can send some query params as well, but doesn't look like that is your questions. Please, be more specific.
By the way, if you send some POJO (e.g. your org.types.GetDataRequest), you need to use a marshaling gateway instead of simple one:
<int-ws:outbound-gateway marshaller="someMarshaller" uri="http://<ip>:<port>/service/name"/>
Not sure, of course, if you have a good XSD-genarated model, based on the provided info in that WSDL...

Automatically converting JSON bytes from RabbitMQ queue to object

Is it possible to configure Spring AMQP so it will automatically convert messages from queue (which are essentially JSON strings) into the objects of desired type?
What I have tried so far:
1) My cfg:
<rabbit:listener-container connection-factory="rabbitConnectionFactory" message-converter="jsonMessageConverter">
<rabbit:listener ref="foo" method="listen" queue-names="test_queue"/>
</rabbit:listener-container>
<bean id="foo" class="foo.FooListener"/>
<bean id="jsonMessageConverter" class="org.springframework.amqp.support.converter.JsonMessageConverter"/>
2) My listener FooListener has method listen(FooMessage foo) { ... }
and
3) My FooMessage is just simple POJO and messages in my test_queue are just serialized instances of FooMessage in JSON format.
So it doesn't work, Spring claims for listen(byte[] msg) method:
java.lang.NoSuchMethodException: foo.FooListener.listen([B).
Is it possible to do?
Well, actually this thing works like a charm with Spring AMQP even prior version 1.6. The problem was with incorrect content-type of my message, it was just text/plain.
If message with json is sent in appropriate way, say
template.convertAndSend("test_queue", "test_queue", new FooMessage("blablabla","blabla"));
with correctly setup template:
<rabbit:template id="amqpTemplate"
connection-factory="rabbitConnectionFactory"
message-converter="jsonMessageConverter"
/>
<bean id="jsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter"/>
so method listen(FooMessage foo) { ... } is invoked with automatically created object of type FooMessage from message.
Please, read the documentation for the Jackson2JsonMessageConverter.
And pay attention to the note:
In versions prior to 1.6, if type information is not present, conversion would fail. Starting with version 1.6, if type information is missing, the converter will convert the JSON using Jackson defaults (usually a map).
So, independently of the target argument type of the POJO handler, the MessageProperties must contain __TypeId__ information.
To meet your requirement consider to use #RabbitListener instead.
See the same section for news in the Spring AMQP 1.6.
Although feel free to raise a JIRA issue and we may consider some hooks for general MessageListenerAdapter where we know the method via listener ref="foo" method="listen" and, therefore, can infer the target argument type before conversion.

Accessing API exposed via wsdl in spring REST based client

How can we access a wsdl of a soap project whose war is deployed on the same server, by a Rest based project using spring maven. Basically , I have to access an API that is exposed via wsdl and I have to access this API, the response than needs to be returned as json from a rest POST method. It will be like a REST post method, accepting the inputs and invoking this API (from wsdl) and manipulating the response as JSON,
I have to jump into the WebServices and Spring framework, without through knowledge. So, any help or directions to learn these things fast would be appreciated.
You will need to do the following:
Create the client code from the WSDL
This can be acomplished in Spring with the following technique:Spring - Consuming a SOAP service. It will generate the Java classes that you will need to call the service from your REST API code. In this case that you are calling another service in the same server, all you have to do it set the endpoint url to your server.
Create your REST API
You can use Spring MVC to design your REST API and call the SOAP service.You will need to develop a Controller class with the different endpoints and the proper request and response objects. Spring MVC will automatically convert those request and response objects to JSON using the Jackson framework. Use this guide: Building a RESTful Web Service
That is a generic way of consuming a SOAP service from a Java REST API. If the goal is to simply expose the SOAP service as a REST service then you can just return the response object that was generated from the WSDL. If it is an option, I would seriosly consider refactoring the SOAP service code and expose the it as a REST API.
Note: In the good old days consuming a SOAP was acomplished by using JAX-WS directly and exposing JSON objects was done through Jackson.
Hi I have used the following approach to implement the above requirement:
http://myshittycode.com/2013/10/01/using-spring-web-services-and-jaxb-to-invoke-web-service-based-on-wsdl/
1. changed the pom to add spring-ws dependency and plugin.
2. build the classes and it generated the classes from the wsdl.
3. changed the application xml :
<!--Generating web sources-->
<!-- Define the SOAP version used by the WSDL -->
<bean id="soapMessageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">
<property name="soapVersion">
<util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_12"/>
</property>
</bean>
<!-- The location of the generated Java files -->
<oxm:jaxb2-marshaller id="marshaller" contextPath="com.pb.pims.generatedsources"/>
<!-- Configure Spring Web Services -->
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="soapMessageFactory"/>
<property name="marshaller" ref="marshaller"/>
<property name="unmarshaller" ref="marshaller"/>
<property name="defaultUri" value="http://localhost/HSWS/services/HSService?wsdl"/>
</bean>
4. Created the Service class;
#Service
public class HSService {
#Autowired
private WebServiceTemplate webServiceTemplate;
public List<HSChild> getHSChildren(String hscode, String country,String limit) {
GetHSChildren getHSChildren= new ObjectFactory().createGetHSChildren();
getHSChildren.setCountry(country);
getHSChildren.setHsCode(hscode);
getHSChildren.setLimit(Integer.parseInt(limit));
GetHSChildrenResponse response = (GetHSChildrenResponse) webServiceTemplate.marshalSendAndReceive(getHSChildren);
return response.getGetHSChildrenReturn();
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
HSService hsService = context.getBean(HSService.class);
}
}
So, I am able to call this aPI from the wsdl via my client. But I am always getting the values of the getGetHSChildrenReturn. hscode and getGetHSChildrenReturn.description as null.
Please find below the getGetHSChildrenReturn.class generated in the Step1 :
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"getHSChildrenReturn"
})
#XmlRootElement(name = "getHSChildrenResponse")
public class GetHSChildrenResponse {
#XmlElement(required = true)
protected List<HSChild> getHSChildrenReturn;
public List<HSChild> getGetHSChildrenReturn() {
if (getHSChildrenReturn == null) {
getHSChildrenReturn = new ArrayList<HSChild>();
}
return this.getHSChildrenReturn;
}
Also, I verified in the service code , which we are invoking via this wsdl by putting logging, that the correct request is going and it is returning the expected response at service end. But while coming to the client, the values are set as null.
Please help, what's wrong here in the client side.
Thanks in advance.

Get SOAP request as XML in Spring MVC

I need to get the XML Soap request coming to a url on my server. I wrote something like
#RequestMapping("/SomeService")
#ResponseBody
public String index(#RequestBody String request) {
mailService.sendMail("someone#gmail.com", "Subject", request);
return "";
}
But the request body is coming as blank. I'm using spring 3.2. I just need the XML SOAP content. How do I proceed in this case. Thanks in advance.
Let me suggest a few ways of doing this #Akhil:
If you want to expose a robust SOAP based endpoint, then the best approach would be to use one of the full-fledged JAX-WS implementations(say Apache CXF or Axis2).
If you want to be a little more lightweight and want to be completely under the Spring umbrella of projects, I would highly recommend using Spring-WS as the endpoint to consume the SOAP request and serve out a response - Spring WS is here: http://static.springsource.org/spring-ws/sites/2.0/reference/html/index.html
If this is an ad-hoc endpoint that you are using not for real business reasons but say just for capturing info and do not really care for other features that SOAP brings - say security, validation etc, marshalling, unmarshalling support etc, then an option would be to use Spring's HttpRequestHandlingServlet, this way you can implement a HttpRequestHandler which takes HttpServletRequest as a parameter and grab the POSTed content from it.
These as you have probably noted is all outside of Spring MVC, the reason is the framework tends to consume the raw http request and provides the parameters once it has pre-processed them, so even if you write a #RequestMapped method expecting a raw string, you will not get it cleanly.
Try below code. I think you have to define this method as a POST. And define the headers as well. It will support to define this as GET but in the server side it will not identify the Body contents in the #RequestBody .
#RequestMapping(value = "/SomeService", method = RequestMethod.POST, headers = "Accept=application/xml, application/json")
#ResponseBody
public String index(#RequestBody String request) {
mailService.sendMail("someone#gmail.com", "Subject", request);
return "";
}
Add the following bean definition in your beans definition file. My bean definition has beans prefix.
<beans:bean name="handlerAdapter"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<beans:property name="messageConverters">
<util:list>
<beans:bean
class="org.springframework.http.converter.StringHttpMessageConverter" />
</util:list>
</beans:property>
</beans:bean>

Camel Spring-WS. Setting custom SOAP header

I have configured Camel SOAP proxy service using Spring DSL. Everything was working nice untill I found that I need to set a custom header inside for SOAP response message.
I am using spring-ws component and latest Camel 2.10.0.
Here is an example of my spring route (I skipped some irrelevant transformations):
<bean id="ahc_binding" class="ru.fabit.ExampleAHCBinding"/>
<bean id="response_assembler" class="ru.fabit.ExampleResponseAssembler"/>
<camel:camelContext id="get_regions">
<camel:dataFormats>
<camel:jaxb id="main_jaxb" prettyPrint="true"
contextPath="ru.fabit.rosstelecom.webservice.models.smev" />
</camel:dataFormats>
<camel:route>
<camel:from uri="spring-ws:rootqname:{http://fabit.ru/service}getRegionsRequest?endpointMapping=#endpointMapping"/>
<camel:unmarshal ref="main_jaxb"/>
<camel:to uri="ahc:http://localhost:9001/service/regions"/>
<camel:unmarshal ref="main_jaxb"/>
<camel:process ref="response_assembler"/>
</camel:route>
</camel:camelContext>
And here is the code for ExampleResponseAssembler.java ("response_assembler" bean). It is the last element in the route. And it's responsibility to get unmarshalled response object from some external service (from AHC component, actually) and assemble the proper SOAP response for overall route.
public class ExampleResponseAssembler implements Processor {
#Override
public void process(final Exchange exchange) throws Exception {
final Object responseMessage = exchange.getIn().getBody();
final GetRegionsResponse regionsResponse = new GetRegionsResponse();
final MessageDataType messageData = new MessageDataType();
final AppDataType appData = new AppDataType();
appData.setAny(responseMessage);
messageData.setAppData(appData);
regionsResponse.setMessageData(messageData);
exchange.getOut().setBody(regionsResponse);
final HeaderType header = exchange.getProperty("exampleHeader", HeaderType.class);
exchange.getOut().setHeader("CamelSpringWebServiceSoapHeader", header);
}
}
When I set the Body that way it is parsed correctly. I can see it in SaopUI. But header is not there. That was a naive approach to set the SOAP header I guess.
And I can't find any relevant info about this.
Although I was able to find some JIRA tickets regarding this problem - link, it is still unclear how to handle with setting some custom SOAP headers. And ticket is marked as "unresolved".
Maybe I need some override voodoo magick here (override MessageFactory, MessageSender or something else). Seems like a minor issue, but...
Seems to be an issue with a patch attached that might fix this.
https://issues.apache.org/jira/browse/CAMEL-4515
You could probably just implement your own component which derives from camel-spring-ws (not tested it though).
In your overrided SpringWebServiceConsumer add the setting of headers (copy from exchange).
In your overrided SpringWebServiceEndpoint override createConsumer to create your custom Consumer.
In your overrided SpringWebServiceComponent override createEndpoint to create your custom endpoint.
Should do the trick, maybe with some other code around it.
Then just create your routes from your new component.
You might want to download camel source and look at spring ws component, it's not that much code in it.
Unfortunately, the current implementation does not support multiple SOAP headers. I mean:
<soap-env:Header>
<MyFirstHeader>...</MyFirstHeader>
<MySecondHeader>...</MySecondHeader>
</soap-env:Header>
How can I submit a patch?
By the way, there is a workaround on Spring-WS side:
http://leakfromjavaheap.blogspot.com/2014/05/multiple-soap-headers-in-apache-camels.html

Categories

Resources