Camel CXF asynchronous request and reply - cxf

I would like to set up a Camel CXF endpont, and have the SOAP response asynchronous to much of my Camel Route. The route will have a lot of processing steps, and I do not want to have the response generated at the very end.
An example endpoint:
<cxf:cxfEndpoint id="someEndpoint"
address="/Service"
serviceClass="com.SomeImpl" />
An example route:
public class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("cxf:bean:someEndpoint")
to("bean:processingStep1")
to("bean:replyToSOAP") // I would like to respond to cxf:cxfEndpoint here!
to("bean:processingStep2")
to("bean:processingStep3")
to("bean:processingStep4");
// The response to cxf:cxfEndpoint actually happens here.
}
}
I have tried many options in MyRouteBuilder to "fork" the process (i.e. bean:replyToSOAP):
.multicast().parallelProcessing()
In-memory Asynchronous messaging ("seda" and "vm")
I have NOT tried JMS. It could be overkill for what I want to do.
I can get the route steps to process in parallel, but all steps must be completed before the response is generated.
In addition to the answer Claus gives below, I'd like to add that the placement of wireTap is important. Using:
.wireTap("bean:replyToSOAP")
will not get the desired behavior. What will is:
public class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("cxf:bean:someEndpoint")
to("bean:processingStep1")
.wireTap("direct:furtherProcessing")
to("bean:replyToSOAP") // respond to cxf:cxfEndpoint here
from("direct:furtherProcessing") // steps happen independantly of beann:replyToSOAP
to("bean:processingStep2")
to("bean:processingStep3")
to("bean:processingStep4");
}
}

There is the WireTap EIP which can spin of a copy of the message to be processed independently from the current route: http://camel.apache.org/wire-tap

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
*/
}
}

Using Camel Endpoint DSL with #EndpointInject creating different endpoints

What is the proper way to use endpoint DSL and then reference the endpoint with ProducerTemplate? When creating a route and using endpoint DSL, it seems that Camel is creating a different uri for the endpoint. My EndpointRouteBuilder class:
#Component
public class MyRoutes extends EndpointRouteBuilder {
#Override
public void configure() throws Exception {
from(seda("STATUS_ENDPOINT"))
.routeId("stateChangeRoute")
.to(activemq("topic:statusTopic"))
}
}
and then injecting the endpoint to ProducerTemplate
#Component
public class StateChangePublisher {
#EndpointInject(value="seda:STATUS_ENDPOINT")
private ProducerTemplate producer;
public void publish(String str) {
try {
producer.sendBody(str);
} catch(CamelExecutionException e) {
e.printStackTrace();
}
}
}
When camel starts, I see two entries in the log:
o.a.camel.component.seda.SedaEndpoint : Endpoint seda:STATUS_ENDPOINT is using shared queue: seda:STATUS_ENDPOINT with size: 1000
o.a.camel.component.seda.SedaEndpoint : Endpoint seda://STATUS_ENDPOINT is using shared queue: seda://STATUS_ENDPOINT with size: 1000
The queue eventually fills up and nothing gets delivered to the "to" endpoint.
If I define the route without using the endpoint DSL method "seda()"
from("seda:STATUS_ENDPOINT")
then it works.
Is this a bug or am I doing something wrong?
I'm using camel 3.2.0 and
This was a bug in the endpoint dsl. Try upgrading to camel 3.3.0. I think it was fixed in the new release.
https://issues.apache.org/jira/browse/CAMEL-14859

Will this Camel Route acknowledge message in exception scenario?

I have a Camel route which converts JSON to an object and passes it on to a processor class. Code is below. This ActiveMQ consumer is not acknowledging some messages, causing the topic to get backed up. The code does not explicitly set acknowledgement mode but a breakpoint shows these values -
acknowledgementMode = -1
acknowledgementModeName = null
What should be changed to ensure acknowledgements are sent on both successful processing and when an exception occurs inside the processor class?
#Component
public class MyRoute extends RouteBuilder {
private String mySubscription;
private MyProcessor myProcessor;
public MyRoute(#Value("${my.topic}") String tripSubscription, MyProcessor myProcessor) {
this.mySubscription = mySubscription;
this.myProcessor = myProcessor;
}
#Override
public void configure() {
from(mySubscription)
.unmarshal().json(JsonLibrary.Jackson, MyDTO.class)
.bean(myProcessor, "process(${body})")
.end();
}
}
The processor class -
#Slf4j
#Component
#AllArgsConstructor
public class MyProcessor {
public void process(MyDTO dto) {
//code that throws exception
}
}
The Camel JMS component docs at Github says that the default acknowledge mode is AUTO_ ACKNOWLEDGE.
However, the older docs at camel.apache.org says the default is -1 what corresponds to the value you see. Either the default was changed in a recent version or the new docs at Github are wrong.
The value -1 is somehow invalid because it is none of the defined modes.
Therefore you could give it a try to explicitly set acknowledgementModeName=AUTO_ACKNOWLEDGE on your consumer.
Side note… looks like you’re not setting “tripSubscription” into and instance variable if that was your intent…

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.

Correlating messages on two Camel Routes

In my application I have a generic Camel Route such as the following
from("direct:something").to("direct:outgoing")
and then dynamically in my code I deploy another route:
from("direct:outgoing").process(processor)
When flowing from route 1 to route 2 a new Exchange will be created. Is there an idiomatic way to correlate both? Should I set EXCHANGE.Correlation_ID header on the first route before sending it out?
This should definitely all be processed on the one exchange. Run this test and you'll see the same camel Exchange, with the same properties, etc.
public class CamelExchangeTest {
public static void main(String[] args) throws Exception {
final Processor showExchangeIdProcessor = new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println(exchange.getExchangeId());
}
};
Main camelMain = new Main();
camelMain.addRouteBuilder(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("timer:foo?period=1s&repeatCount=1")
.log("excgabge created!")
.process(showExchangeIdProcessor)
.to("direct:outgoing")
;
from("direct:outgoing")
.log("outgoing!")
.process(showExchangeIdProcessor)
;
}
});
camelMain.run();
}
}
Output:
ID-MYPC-55760-1411129552791-0-2
ID-MYPC-55760-1411129552791-0-2
So something else is going on. When you say "direct:outgoing", do you mean exactly that or is it something different - a different component perhaps?
When you say the route is created dynamically, how exactly is that done, and when (and why?)
From the Camel doc:
Some EIP patterns will spin off a sub message, and in those cases, Camel will add a correlation id on the Exchange as a property with they key Exchange.CORRELATION_ID, which links back to the source Exchange. For example the Splitter, Multicast, Recipient List, and Wire Tap EIP does this.
Thus, Exchange.CORRELATION_ID is set by Camel and should not be set by your application. But feel free to set a custom header or property if you need to such as:
exchange.getIn().setProperty("myProperty", myIdentifier);

Resources