Setting custom Soap-Header to pojo-message in Camel-CXF - cxf

I have a Problem with cxf soap-headers. I set up an cxf-project with the Contract-firs-development method. I want to call a web-service with an cxf-component, which looks like this.
<cxf:cxfEndpoint id="ICCSCustomerService"
address="http://localhost:8080/iccs-xsoap/CustomerService/updateCustomer"
serviceClass="de.iccs.xsoap.customer.v1.CustomerServiceImpl" >
</cxf:cxfEndpoint>
I want to send an pojo-message throw an direct-component as an request for the ws. My route looks like this:
<route id="CustomerServiceUpdateCustomerTest">
<camel:from uri="direct:iccsUpdateCustomerRequest"/>
<camel:process ref="addCredentials"/>
<to uri="cxf:bean:ICCSCustomerService"/>
<camel:to uri="stream:out"/>
</route>
I need to implement a soap header like this one:
<ns2:Header>
<simpleAuth xmlns="http://xsoap.iccs.de/v1" password="abc" username="xxx"/>
</ns2:Header>
To archive this I wrote an processor like this one (see also example at http://camel.apache.org/cxf.html):
#Override
public void process(Exchange exchange) throws Exception {
List<SoapHeader> soapHeaders = CastUtils.cast((List<?)exchange.getOut().getHeader(Header.HEADER_LIST));
// Insert a new header
String xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><outofbandHeader "
+ "xmlns=\"http://cxf.apache.org/outofband/Header\" hdrAttribute=\"testHdrAttribute\" "
+ "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" soap:mustUnderstand=\"1\">"
+ "<name>simpleAuth username=\"xxx\" password=\"abc\" xmlns=\"http://xsoap.iccs.de/v1\"</name></outofbandHeader>";
SoapHeader newHeader = new SoapHeader(new QName("http://xsoap.iccs.de/v1", "simpleAuth"),
DOMUtils.readXml(new StringReader(xml)).getDocumentElement());
// make sure direction is OUT since it is a response message.
newHeader.setDirection(Direction.DIRECTION_OUT);
//newHeader.setMustUnderstand(false);
soapHeaders.add(newHeader);
}
Unfortunately I get an null-pointer Exception in this statement:
List soapHeaders = CastUtils.cast((List
Obviously there are no soap-headers in this message. And it seems not to be an soap message at all.
Marschaling like this
<camel:marshal>
<soapjaxb contextPath="de.iccs.xsoap.customer.v1" />
</camel:marshal>
<camel:process ref="addCredentials"/>
does not work, as it produces only an soap-envelope without an soap-header. (and of corse this dosen't work as cxf-endpoint works in pogo-mode)
Could you provide me an example how to set an soap message (with soap-header) from an pojo-message.
Thank you
Gabriel

Don't know if you solved your problem already, but I experienced something similar as well, so maybe someone will benefit.
If your NPE is due to there being no existing headers, it is perfectly acceptable to create a new list if needed.
if (message.getHeader(Header.HEADER_LIST) == null) {
message.setHeader(Header.HEADER_LIST, new ArrayList<SoapHeader>());
}
But you may have another problem with the hand-crafted XML being used to populate the SoapHeader. You're still using the outofbandHeader element from the original CXF example; this is a specific header in the example, not a general wrapper for out-of-band headers. Also, your simpleAuth is not marked up as an element (although it's difficult to read...).
If you have annotated class (generated or created) with an #XMLRootElement for your simpleAuth element, you can use the SoapHeader constructor that takes a JAXBDataBinding.
CXF will marshal the header for you.
#Override
public void process(Exchange exchange) throws Exception {
Message in = exchange.getIn();
if (in.getHeader(Header.HEADER_LIST) == null) {
in.setHeader(Header.HEADER_LIST, new ArrayList<SoapHeader>());
}
List<SoapHeader> headers = CastUtils.cast((List<?>)in.getHeader(Header.HEADER_LIST));
SimpleAuth auth = new SimpleAuth();
auth.setUsername("xxx");
auth.setPassword("abc");
try {
SoapHeader header = new SoapHeader(new QName("http://xsoap.iccs.de/v1", "simpleAuth"),
auth, new JAXBDataBinding(SimpleAuth.class));
header.setDirection(Direction.DIRECTION_OUT);
header.setMustUnderstand(true);
soapHeaders.add(header);
} catch (JAXBException e) {
e.printStackTrace();
}
}

Related

Camel ProducerTemplate and ActiveMQ message and persistence issues

Issue:
I have multiple route sending messages to an ActiveMQ queue which later gets processed by a processor to save status information
The same message sending from ProducerTemplate to ActiveMQ queue somehow breakes the same code by not triggering logs on console, and saving status information to a randomly generated file name.
Desired Behavior:
Both sending method gets the messages processed the same way
Code Explanation:
On below codes the Save processor is the one producing the weird behavior where logs dont show up on console and writes to some random file, file name is basically the clientID from ActiveMQ
StartRoute calling the activemq:save works correctly
Code:
public class Save implements Processor {
public void process(Exchange exchange) throws Exception {
try {
Map<String, Object> map = new HashMap<String, Object>() {};
map.put(".....", ".....");
map.put(".....", ".....");
map.put(".....", ".....");
map.put(".....", ".....");
ProducerTemplate template = exchange.getContext().createProducerTemplate();
String response = template.requestBodyAndHeaders("activemq:save", "Batch Job Started", map, String.class);
FluentProducerTemplate FluentTemplate = exchange.getContext().createFluentProducerTemplate();
String result = FluentTemplate
.withHeader(".....", ".....")
.withHeader(".....", ".....")
.withHeader(".....", ".....")
.withHeader(".....", ".....")
.withHeader(".....", ".....")
.to("activemq:save")
.request(String.class);
} catch (Exception e) {
.....
}
}
}
public class StartRoute extends RouteBuilder {
restConfiguration()
.component("servlet")
.enableCORS(true);
rest("/start").id("start")
.post("/{filename}")
.route()
.....
.process(new Save())
.wireTap(startBackgroundProcessRoute)
.to("activemq:save")
.endRest();
}
public class SaveRoute extends RouteBuilder {
from("activemq:save")
.log("started saving")
.process(new FormatText())
.to("file:///status");
}
This question came from my original problem described here:
Camel Multicast Route call order
Solution can be found also there:
Camel Multicast Route call order
However to satisfy this question:
It seems Camel have few bugs regarding producer templates and maybe ActiveMQ, this is my initial conclusion.
The Only way i was able to use ProducerTemplate without issue is to use the send function with Exchanges, send() sends the messages correctly to the ActiveMQ same way as the to() however for whatever reason the content was still not written to the file.
After I dropped the ActiveMQ between the routes, everything started to work consistently. So possible miss configuration on ActiveMQ component or possible another camel framework bug.
If anyone knows the exact answer i would be happy to hear / see the reason for this behavior.
Code example:
public class SaveProcessor implements Processor {
public void process(Exchange exchange) throws Exception {
ProducerTemplate template = exchange.getContext().createProducerTemplate();
template.send(Utilities.trackBatchJobStatus, exchange);
/** NOTE
* DO NOT USE any other functions which are not working with EXCHANGES.
* Functions that uses body, header values and such are bugged
*/
}
}

Load Camel's body dynamically depending on header

Hi I want to set to my body an SQL statement in order to be used by the jdbc component afterwards. I have my sql scripts in the resources, thus i can take advantage of simple language to load sth from resources with the usage of resource:classpath. The problem is that url is not static and I want to load different files depending on a header that I've got (``).
<!-- INSERT data -->
<setBody>
<simple>resource:classpath:sql/${header.CamelCustomer}/Insert.sql</simple>
</setBody>
Assuming that I have 2 customers A, and B. That mean that I also have 2 directories
resources/sql/A/Insert.sql
resources/sql/b/Insert.sql
How it is possible to load the different files with the usage of the header?
Thanks in advance!
You can use Content Enricher integration pattern and wrap your simple expression with Language component to get it evaluated.
This should return expected results:
Java DSL:
.enrich().simple("language:simple:resource:classpath:sql/${header.CamelCustomer}/Insert.sql")
XML DSL:
<enrich><simple>language:simple:resource:classpath:sql/${header.CamelCustomer}/Insert.sql</simple></enrich>
I have created sample unit test for demonstration.
src/test/resources/sql/A/Insert.sql
INSERT something INTO A;
src/test/resources/sql/B/Insert.sql
INSERT something INTO B;
DynamicLoadResourceTest
public class DynamicLoadResourceTest extends CamelTestSupport {
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start")
.enrich().simple("language:simple:resource:classpath:sql/${header.CamelCustomer}/Insert.sql")
.to("mock:done");
}
};
}
#Test
public void testContentEnrichResource() throws Exception {
MockEndpoint done = getMockEndpoint("mock:done");
Map<String, Object> headers = new HashMap<>();
headers.put("CamelCustomer", "A");
sendBody("direct:start", null, headers);
headers.put("CamelCustomer", "B");
sendBody("direct:start", null, headers);
done.setExpectedCount(2);
done.assertIsSatisfied();
Assert.assertEquals(
"INSERT something INTO A;",
done.getExchanges().get(0).getIn().getBody()
);
Assert.assertEquals(
"INSERT something INTO B;",
done.getExchanges().get(1).getIn().getBody()
);
}
}

How to use Apache Camel exchange messages?

I am newcomer in Apache Camel. Please have a look to my code bellow:
I have a service which exposed as cxf webservice:
interface CxfService{
public OutputType hello(InputType input);
}
This is my route:
from("cxf:/test?serviceClass=" + CxfService.class.getName())
.to("log:cxfLog1")
.recipientList(simple("direct:${header.operationName}"));
from("direct:hello")
.process(new Processor(){
public void process(Exchange exchange) throws Exception {
InputType file = exchange.getIn().getBody(InputType.class);
exchange.getOut().setBody(new OutputType());
}
});
The code works as expected, it consume InputType and produce OutputType.
I want to borrow my body to do another stuffs, so i rewrite that like this:
from("cxf:/test?serviceClass=" + CxfService.class.getName())
.to("log:cxfLog1")
.recipientList(simple("direct:${header.operationName}"));
from("direct:hello")
.process(new Processor(){
public void process(Exchange exchange) throws Exception {
InputType file = exchange.getIn().getBody(InputType.class);
exchange.getOut().setHeader("header.temporary", new OutputType());
}
})
.to("some endpoint")
.setBody(simple("${header.temporary}"));
This webservice consume InputType and produce nothing. What wrong with that?
In your second piece of code, when setting the header.temporary, you should change two things:
setHeader("temporary", new OutputType()) - the 'header' prefix isn't
needed - you're addressing headers directly via the method call.
Use getIn() instead of getOut(). The input will get copied to the
output. You may want to do some research into the procedure for
Camel building the out message for details - I'm not 100% sure of
this one.
Change
exchange.getOut().setHeader("header.temporary", new OutputType());
To
exchange.getIn().setHeader("temporary"), new OutputType());
.setHeader() is when you use the simple language. In 99% of the cases getIn() is sufficient.

OnException in Camel not returning a custom response

I am having this issue in my apache camel code which I am trying to resolve from last two days, but I could not resolve it.
I have two routes. First route is as below:
public class XXXRoute1 extends RouteBuilder {
public void configure() {
String endpointUri = "cxf:/XXX;
String logEndpoint = "log:" + XXX() + "?level=DEBUG";
from(endpointUri)
.to(logEndpoint).to(ROUTE2.ENDPOINT_URI)
.to(logEndpoint);
}
Second Route:(In second route , I am catching some exception through onException)
public class Route2 extends RouteBuilder {
public void configure() throws Exception{
String integrationEndpoint = "xxx.integration";
onException(RuntimeException.class).handled(true).onWhen(exceptionMessage().contains("Invalid")).bean(translator, "translateSomeError(${property.XXX})").end();
from(ENDPOINT_URI)
.
.
. so on
Now , In my case, the object which I am populating through onException
public SomeObjectResponse translateSomeError(Object someObject) throws Exception{
SomeObjectResponse someObjectResponse = new SomeObjectResponse();
someObjectResponse.setError("someError");
return someObjectResponse ;
}
However, In SOAP UI while testing I am getting an empty SOAP envelop
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body/>
</soap:Envelope>
I am using cxf component in apache camel.
Can you post your route completely ? Where you have the onException ?
Also before you use your custom class, can you try this ? Just want to make sure anything set at onException is propagated back to CXF endpoint:
onException(RuntimeException.class).handled(true).setBody().simple("</Unexpected exception> ");
The onException() block appears to be in your second route and is consequently not in scope of your first route.
Make the onException() block global or add it to your CXF route so it's in scope.

apache camel #Produce method with Object argument instead of String

I am using Camel's POJO producing e.g.
{
public interface MyListener {
String sayHello(String name);
}
public class MyBean {
#Produce(uri = "activemq:foo")
protected MyListener producer;
public void doSomething() {
// lets send a message
String response = producer.sayHello("James");
}
}
}
The interfaces using method sayHello with string object which used as body in the camel. However, If i try to use any other Object here i get exception from camel saying no TypeConvertor found for BeanInvocation for Conversion java.io.InputStream.
I know is the object was allowed it would have been mentioned somewhere. But i want to reason why it has been done like that and if there's a way to work-around this.
I havent really used POJO messaging as yet. Maybe, an experienced user can help you better with this.
But from what I understand, it should be able to support any kind of object not just string.
The error that you're talking of seems to arise out of a mismatch down the route. I'm guessing there is some kind of issue with the consumption.
Can you please post the exact error stacktrace and the consumer method?
Thanks!
Struggling with the same problem right now. The only obvious workaround so far is to use #EndpointInject instead of #Produce - then you get ProducerTemplate and publish any object:
#EndpointInject(uri = "seda:report-send")
ProducerTemplate reportSender;
Now you can do
Object myObject = new Object();
reportSender.sendBody(myObject);
Or even
Object myObject = new Object();
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("Subject", "Mail subject");
headers.put("contentType", "text/plain");
reportSender.sendBodyAndHeaders(myObject, headers);

Resources