Get SOAP request as XML in Spring MVC - java

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>

Related

How to have spring cache store the ResponseBody and not the intermediary object

I use spring cache with this method which returns the queried value as JSON:
#RequestMapping("/getById")
#ResponseBody
#Cacheable
public HugeValue getHugeValueFromSlowFoo( #RequestParam(value = "id", defaultValue = "") String id ) {
return Foo.getById( id );
}
This works fine and the HugeValue-object is stored in the cache (Hazelcast in this case). I want to further improve this because the time taken to create JSON from the HugeValue is quite high.
Can I tell spring cache to cache the JSON-ified version of my object ?
I use Jackson with Spring Boot 1.2 and Spring 4.1
Without really knowing your exact use case (I'm not yet allowed to add comments for asking unfortunately), I try to give a short summary on the ideas I have in mind. They all assume that you use Jackson for json mapping and at least Spring 3.1.
There is no enableResponseBodyCaching feature in SpringMVC as far as I know.
First alternative: Use http caching because it seems like you really want to cache the whole http response. Spring offers a straight forward way of global configuration:
<mvc:interceptors>
<bean id="webContentInterceptor"
class="org.springframework.web.servlet.mvc.WebContentInterceptor">
<property name="cacheSeconds" value="0"/>
<property name="useExpiresHeader" value="true"/>
<property name="useCacheControlHeader" value="true"/>
<property name="useCacheControlNoStore" value="true"/>
</bean>
</mvc:interceptors>
If you wish to control this per Controller, inherit from Spring AbstractController and set cacheSeconds property according to javaDoc.
True power of http caching comes with a http proxy in front of your server of course.
Second idea: Implement your own subclass of MappingJackson2HttpMessageConverter. In writeInternal() you could add some logic which accesses a cache to retrieve an already mapped version instead of mapping the input object. This approach means that you will hit your services in order to retrieve the java object behind the Json stream. If this is fine for you because there is also caching at some point, this approach is worth a try imho.
Third idea: Do the json mapping on your own in a dedicated wrapper service which provides raw json strings/streams. You can easily inject the Jackson mapper (class name ObjectMapper) and gain full control over the mapping. Annotating this service then allows you to cache the results. In your Controller you only provide a ResponseEntity of the according type you whish to use (String or some stream). This would even prevent deeper service access if there is a cached result present.
Edit: Probably the MappingJackson2JsonView could also get handy. To be honest, I never worked with it before so I can't really say something about its usage.
Hope that helps and/or gives inspiration!
Cheers
I think this could work.
public interface HugeValueService{
public String getHugeValueFromSlowFoo(String id);
}
public class HugeValueServiceImpl implements HugeValueService{
#Cacheable
public String getHugeValueFromSlowFoo(String id ) {
return Foo.getById( id );
}
}
Your Class
----------
#RequestMapping("/getById")
#ResponseBody
public HugeValue getHugeValueFromSlowFoo( #RequestParam(value = "id", defaultValue = "") String id ) {
return hugeValueService.getHugeValueFromSlowFoo(id);
}
Good Luck!!!
Cache the json and put it in the response manually.
public void getHugeValueFromSlowFoo( #RequestParam(value = "id", defaultValue = "") String id, ServletResponse resp )
{
String jsonSerializedHugeValue = Bar.getById(id); // serialize manually with ObjectMapper
resp.getWriter().write(jsonSerializedHugeValue);
resp.setContentType("application/json");
resp.flushBuffer();
}

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.

#Consumes(MediaType.APPLICATION_JSON) annotation but getting request body as string

I am currently working on a project which was maintained by some other team and now i need to maintain it. While i was going through the project i found some thing below:
In jax-rs controller it was annotated by #Consumes(MediaType.APPLICATION_JSON) but the method takes request body as String rather than JSON. Then what is the use of the annotation? Does it help in content negotiation anyway?
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response createCake(final String requestBody){.......}
How it is converting a JSON body to string?
My technology stack if it anyway helps to answer:
JAX-RS
Spring 3.2
Jersey 2.4
The #Consumes serves the following purpose. It restricts the mapping for your handlers. For example, you may have two handlers for the path /resource, one mapped to consume XML and the other mapped to consume json. The dispatcher will choose the right one based on the request's content-type.
The parameter type can be anything as long as there is an appropriate converter for the specified media type to the parameter type itself. In this case, there's very likely a converter from any media type to String.

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

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

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.

Categories

Resources