Camel Spring-WS. Setting custom SOAP header - java

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

Related

Camel CXF Soap Client calling web service with multiple input parameters

I'm using Camel and have generated code from a WSDL using CXF. I generated a client stub and the implementation appears like this:
SetDeviceDetailsv4 port = ss.getSetDeviceDetailsv4Port();
com.vodafone.gdsp.ws.SetDeviceDetailsv4_Type _setDeviceDetailsv4_parameters = null;
com.vodafone.gdsp.ws.GdspHeader _setDeviceDetailsv4_gdspHeader = null;
com.vodafone.gdsp.ws.SetDeviceDetailsv4Response _setDeviceDetailsv4__return = port.setDeviceDetailsv4(_setDeviceDetailsv4_parameters, _setDeviceDetailsv4_gdspHeader);
System.out.println("setDeviceDetailsv4.result=" + _setDeviceDetailsv4__return);
As you one can see, the port takes two parameters and returns the response, which I want to delegate back to my Camel Route. What's the best way to implement this in Camel? I already have my CXF Enpoint defined, I'm just struggling with the DSL Routing part of it. Should I add a processor like what is found in this link? Apache Camel and web services
Thanks
You can use jax-ws client (implement as bean) and use it in camel DSL. JAX-WS client bean definition takes service class/interface and allow you configure additional properties like SSL config & etc. In route, we can use it as bean. It takes JAXB generated Request object (WSDL request object) as input and returns the JAXB generated Response Object (WSDL response object). To convert you pojo to JAXB classes, Dozer framework can be used or custom mapping can be also used.
Jax-WS client is also flexible to take XML as request and response. In that case, properties need to be set as DATAFORMAT as PAYLOAD.
I'm not sure if this is the correct way to do it but I added both of my "input" objects as a Camel Header, then I wrote a processor that grabbed what I needed and put the two objects that the service call needed as parameters.
public void process(Exchange exchange) throws Exception {
Message inMessage = exchange.getIn();
gdspHeader = inMessage.getHeader(GDSP_HEADER, com.vodafone.gdsp.ws.GdspHeader.class);
commModule = inMessage.getHeader(COMM_MODULE_HEADER, resmed.hi.ngcs.datastore.model.CommModule.class);
SetDeviceDetailsv4_Type deviceDetails = createSetDeviceDetailsv4(commModule);
List<Object> params = new ArrayList<>();
params.add(deviceDetails);
params.add(gdspHeader);
inMessage.setBody(params);
}
`

Camel: Bean Proxy to CXF Endpoint

I'm currently trying to get familiar with Servicemix, Camel, CXF, etc. and have basically the same question as somebody had four years ago here:
How do I convert my BeanInvocation object in camel to a message body and headers?
Unfortunately, the answer there don't help me much. As one of the answers mentions: all examples on the Camel website concern themselves with sending something to a bean from CXF.
I have a bean proxy endpoint that I'm using in a POJO, injected via
#Produce(uri ="direct:start")
MyService producer; //public interface example.MyService { void myMethod(MyObject o);}
When I use another bean endpoint at the other end, implementing a consumer for that interface, this all works fine. What I now would like to do is to use camel-cxf to consume a web service implementing that interface instead. I created a cxfEndpoint via:
<cxf:cxfEndpoint id="cxfEndpoint"
address="http://localhost:8080/MyService/services/MyService"
wsdlURL="http://localhost:8080/MyService/services/MyService?wsdl"
serviceName="s:MyService"
serviceClass="example.MyService"
endpointName="s:MyService"
xmlns:s="http://example" />
What I'm now basically trying to do is, in a RouteBuilder:
from( "direct:start" ).to( "cxf:bean:cxfEndpoint" );
but get an Exception, when trying invoke something on the proxy object:
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Part
{http://example}o should be of type example.MyObject, not
org.apache.camel.component.bean.BeanInvocation
From what I understand, the Spring proxy object generates a BeanInvocation object that can be consumed by another bean endpoint, and I have to transform this into a way the cxf can generate a SOAP request out of it (or is there some automatic conversion?).
But I'm kind of stuck doing that:
I tried soap marshalling as described at http://camel.apache.org/soap.html or writing my own Processor, but I'm not even sure if I just failed, or if that's not how it's supposed to work. I also tried to set the cxfEndpoint into the different message modes without success.
Any pointers what I should be generally doing would be greatly appreciated!
So after a week of trial and error, I found that the answer is quite simple. If the cxfEndpoint is set to POJO mode (the default), the solution is to just grab the invocation parameters and stuff them into the message body instead:
from( "direct:start" ).process( new Processor() {
#Override
public void process( Exchange e) throws Exception {
final BeanInvocation bi = e.getIn().getBody( BeanInvocation.class );
e.getIn().setBody( bi.getArgs() );
}
} ).to( "cxf:bean:cxfEndpoint" )
I guess this could be done more elegantly somehow though.

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>

Serve all font files with an additional header in Spring MVC?

I'd like to add the following HTTP header to all responses stemming from a request for a font-file in Spring MVC:
Access-Control-Allow-Origin: *
So, I know how to set up a simple static request mapping...
<mvc:resources mapping="/fonts/**" location="/fonts/" />
But how can I add the necessary header? I know that I could implement a controller that responds to all /fonts/ requests and adds the header, but that seems like major overkill. Is there something simpler/more lightweight?
There is no need to implement a special controller for this. You can use an interceptor which extends HandlerInterceptorAdapter.
The postHandle method is passed the HttpServletResponse. You can set the header there. This interceptor can be configured to apply to requests to a specific path.
See the Spring docs here.

Custom annotations to set HTTP response headers in a JAX-RS service

I have a JAX-RS web service for which I would like to disable the same-origin policy via the new CORS HTTP headers. (I am fully aware of the security implications.)
I'd like to have a custom annotation that lets me set HTTP response headers. For example,
#ResponseHeaders({"Access-Control-Allow-Origin: *",
"Access-Control-Allow-Methods: GET"})
// Or, alternatively:
#AllowOrigins({"*"})
public String resourceMethod() { ... }
This approach minimizes boilerplate code, but I'm not sure if there's a subtle technical limitation; JAX-RS provides many annotations to handle the HTTP request but not the response, with #Produces seeming to be the sole exception.
I also prefer to stay away from too much web.xml configuration, if possible. Without explicitly needing to use a ResponseBuilder (it's OK if an annotation uses one), is there a clean way to set custom HTTP response headers?
To clarify, I'm looking for annotations that integrate with the various ways of setting HTTP response headers in order to minimize boilerplate code.
Perhaps the only spec driven approach is to use a custom MessageBodyWriter. In the writeTo() method, you are passed in a MultivaluedMap which you can set response headers on. You are also passed the annotations on the resource method invoked (so you can get whatever custom annotation you want). So read the annotations, set the headers via MultivaluedMap, and then use the OutputStream passed in to write the message body.
In Apache Wink and possibly other JAX-RS frameworks, you can create custom server side handlers that can also read the annotations on the resource method and do whatever you want (like setting response headers by default).

Categories

Resources