When I sent messages in a Camel Context Component to its endpoint, I have to wait for a response message with acknowledgement. If no response is received within timeout time, an exception shall be thrown back to the camel route.
I tried to implement it the following way:
I used a multicast to generate a timeout response while the original message is sent to the endpoint. The timeout response is delayed and if no response is received after this timeout, a timeout exception shall be thrown back on the route.
So I have the following route:
private final String internalRespUri = "direct:internal_resp";
private final String internalRespTimeout = "seda:internaltimeout";
#Override
public void configure() {
SendController send_controller = new SendController();
TimeoutResponse resp = new TimeoutResponse();
from(Endpoints.MESSAGE_IN.direct())
.errorHandler(noErrorHandler())
.routeId(Endpoints.MESSAGE_IN.atsm())
.log("Incoming message at segment in")
.process(send_controller)
.log("Message after send controller")
.multicast().parallelProcessing()
.log("After wiretap")
.to(internalRespTimeout, Endpoints.SEGMENT_OUT.direct());
from(internalRespTimeout)
.errorHandler(noErrorHandler())
.routeId(internalRespTimeout)
.log("begin response route")
.log("timeout response route")
.process(resp)
.log("modify message to response")
.delay(1000)
.log("after delay")
.to(internalRespUri);
from(Endpoints.SEGMENT_IN.seda())
.routeId(Endpoints.SEGMENT_IN.atsm())
.to(internalRespUri);
from(internalRespUri)
.errorHandler(noErrorHandler())
.routeId(internalRespUri)
.log("after response gathering point")
.choice()
.when(header(HeaderKeys.TYPE.key()).isEqualTo(UserMessageType.RESP.toString()))
.log("process responses")
.process(send_controller)
.otherwise()
.log("no response")
.to(Endpoints.MESSAGE_OUT.direct());
}
The problem is that the exception thrown in the SendController is not propagated over the SEDA endpoint internalRespTimeout.
If I use a direct endpoint instead it works, but then I have another problem:
The delay blocks the route while a received response message from endpoint Endpoints.SEGMENT_IN.seda() may not be transmitted.
Are SEDA endpoint generally not able to propagate exceptions?
How can I achieve a solution to my problem?
Thanks,
Sven
I have an idea:
Instead of throwing an exception, I possibly could use transactions for timeout.
Could this work?
I am currently not aware of a way to propagate and exception back over a SEDA endpoint in camel. The way the error handling works is based on channels between endpoints. When you use a SEDA endpoint the code will keep processing and not wait for the code since it will keep processing. I am having a bit of trouble understanding what you would like to accomplish, but I will list some similar alternatives you might be able to use.
-The first is to use a route level error handler in your SEDA based route and store the exception using a unique Id that you can lookup later.
-The second is to pass the data into a Java Bean where you have full control of what you are doing and could even consider something like using a Guava's Futures to run the code asynchronously while doing other tasks.
If you can explain what you are trying to accomplish a bit better I might be able to make a clearer suggestion.
Related
I'm new to RabbitMQ and want to implement asynchronous messaging of SAGA with RabbitMQ.So I used RPC example of RabbitMQ to do the task. I've one orchestrator ( RPCClient) and multiple microservices ( RPCServer). Orchestrator uses unique queues to command microservices.And each microservice uses a common queue ( Reply_ Queue) to reply orchestrator. To keep log I want to get notifications in orchestrator side, when any microservice is down for any configurable time.
I read about consumer cancellation,but it only works when I delete the queue.How to get notifications in JAVA with keeping queue messages? And is it correct way to implement saga asynchronous messaging?
To implement a reliable RPC is hard, I can't give a detail guide about how to do this. If we ignore same special failure situation, I can give a simple workaround:
First, we assume that RPCClient never fail, RPCServer may fail anytime.
RPCClient need to know which request is timeout, so it can send request message with a TTL. After RPCServer receive request message and send response message, it should ACK the request message.
If RPCServer:
has failed before consume request message
OR
has failed before send response message
The request message will be republish to Dead Letter Exchange, so RPCClient can consume to some queue binded with that exchange, it can know which request is timeout.
I have to implement an error handler that uses the Camel Redelivery Policy over a business process which send a SOAP request and process its response. During the process part, a special exception (RetryException) could be thrown. This exception is caught (thanks to onException(RetryException.class)) on the error handler level.
That was the easy part.
Now I want to ignore exceptions that could be thrown by the cxf endpoint (in case of connection error per example) and process them.
So I try :
(1) One main route that has the onException clause with retry strategy
(2) One sub route that aggregates 2 routes (and has noErrorHandler) to be sure to replay the 2 routes and not only the processing one that throw the exception :
(3) The route which send the SOAP request
(4) The route which process the response and can throw the retryException.
In order to ignore the exception thrown by the cxf endpoint I implement the route (3) like that :
public void configure() {
from(ROUTE_NAME).
.handleFault() // To handle Soap fault
.onException(Fault.class)
.continued(true)
.end()
.to("cxf:[...]")
;
}
It works like a charm, the response processor perform some checks before throwing the RetryException... But the continued instruction throws away all the informations about redelivery that I previously had and Camel believe that this exception is the first one. So the route enters into a kind of retry forever loop.
Exchange headers before onException(Fault) :
Headers: {breadcrumbId=ID-ITEM-S28636-63117-1469800853403-0-1, CamelRedelivered=true, CamelRedeliveryCounter=1, CamelRedeliveryMaxCounter=2, operationName=[...]}
Exchange headers after onException(Fault) :
Headers: {breadcrumbId=ID-ITEM-S28636-63117-1469800853403-0-1, operationName=[...]}
Did you have any solution to ignore some sub-route exception without erasing the upper route retry strategy?
I guess you are connecting the routes via direct endpoint; if that's the case, I think your main issue is that you're marking Fault as continued which implies handled, which is most likely what's causing your redelivery headers to be cleared. You could try using seda or a message queue, just not the direct endpoint since it will basically link your routes into one.
We are using Camel fluent builders to set up a series of complex routes, in which we are using dynamic routing using the RecipientList functionality.
We've encountered issues where in some cases, the recipient list contains a messaging endpoint that doesn't exist (for example, something like seda:notThere).
A simple example is something like this:
from("seda:SomeSource")....to("seda:notThere");
How can I configure the route so that if the exchange tries to route to an endpoint that doesn't already exist, an error is thrown?
I'm using Camel 2.9.x, and I've already experimented with the Dead Letter Channel and various Error Handler implementations, with (seemingly) no errors or warnings logged.
The only logging I see indicates that Camel is (attempting to) send to the endpoint which doesn't exist:
2013-07-03 16:07:08,030|main|DEBUG|o.a.c.p.SendProcessor|>>>> Endpoint[seda://notThere] Exchange[Message: x.y.Z#293b9fae]
Thanks in advance!
All endpoints behave differently in this case.
If you attempt to write to a ftp server that does not exist, you certainly get an error (connection refused or otherwise)..
This is also true for a number of endpoints.
SEDA queues gets created if the do not exist and the message will be left there. So your route actually sends to "notThere" and the message will still be there until the application restarts or someone starts to consume messages from seda:notThere. This is the way seda queues are designed. If you set the size of the seda queue by to("seda:notThere?size=100"), then if there is noone reading (or reading slowly) you will get exceptions on message 101 and forward.
If you need to be sure some route is consuming your messages, use "direct" instead of "seda". You can even have some middle layer to use the features of seda with respect to staging and the features of direct knowing there is a consumer active (if sent from recipient list with perhaps user input (god forbid).
from("whatever").recipentList( ... ); // "direct:ep1" work, "direct:ep2" throws exception
from("direct:ep1").to("seda:ep1");
from("seda:ep1").doRealStagedStuffHere();
I want to understand a java program and need to modify which was developed using jms spring framework. Typically it has JMS receiver & sender, it receives a message from request queue and will invoke a job (another java program) once the job is completed the sender will send response to response queue. Have couple of questions which are below,
The request message is not deleted until response posted into response queue successfully. How its been achieved what is the logic behind it.
I want to write a functionality of writing response into flat file when sender fails to send message (by catching JMS exception). Once the sender queue is up and running i will read flat file and will send responses. The reason i need is because its involved in job processing could be in hours if job failed then input message will be read again by receiver. I want to avoid duplicate processing. Please suggest your ideas here.
Without seeing the configuration it's hard to answer these questions, but best guess is that #1 is because the app is using a transactional session. This means all updates on that session are not completed until the transaction is committed.
Just catch the exception and write the data; as long as the transaction commits (because you caught the exception) the input message will be removed.
I would like to have an advice for this issue:
I am using Jbos 5.1.0, EJB3.0
I have system, which sending requests via UDP'S to remote modems, and suppose to wait for an answer from the target modem.
the remote modems support only UDP calls, therefor I o design asynchronous mechanism. (also coz I want to request X modems parallel)
this is what I try to do:
all calls are retrieved from Data Base, then each call will be added as a message to JMS QUE.
let's say i will set X MDB'S on that que, so I can work asynchronous. now each MDB will send UDP request to the IP-address(remote modem) which will be parsed from the que message.
so basicly each MDB, which takes a message is sending a udp request to the remote modem and [b]waiting [/b]for an answer from that modem.
[u]now here is the BUG:[/u]
could happen a scenario where MDB will get an answer, but not from the right modem( which it requested in first place).
that bad scenario cause two wrong things:
a. the sender which sent the message will wait forever since the message never returned to him(it got accepted by another MDB).
b. the MDB which received the message is not the right one, and probablly if it was on a "listener" mode, then it supposed to wait for an answer from diffrent sender.(else it wouldnt get any messages)
so ofcourse I can handle everything with a RETRY mechanisem. so both mdb's(the one who got message from the wrong sender, and the one who never got the answer) will try again, to do thire operation with a hope that next time it will success.
This is the mechanism, mybe you could tell me if there is any design pattren, or any other effective solution for this problem?
Thanks,
ray.
It's tough to define an exacting solution without knowing the details, but I will assume that when a response is received from a modem (either the correct one or not), it is possible to determine which exact modem the request came from.
If this is the case, I would separate out the request handler from the response handler:
RequestMDB receives a message from the [existing] queue, dispatches the request and returns.
A new component (call it the ResponseHandler) handles all incoming responses from the modems. The response sender is identified (a modem ID ?) and packages the response into a JMS message which is sent to a JMS Response Queue.
A new MDB (ResponseMDB) listens on the JMS Response Queue and processes the response for which the modem ID is now known.
In short, by separating concerns, you remove the need for the response processing MDB to only be able to process responses from a specific modem and can now process any response that is queued by the ResponseHandler.
The ResponseHandler (listening for responses from the modems) would need to be a multithreaded service. You could implement this as a JBoss ServiceMBean with some sort of ThreadPool support. It will need a reference to the JMS QueueConnectionFactory and the JMS response queue.
In order to handle request timeouts, I propose you create a scheduled task, one for each modem, named after the modem ID. When a request is sent, the task is scheduled for execution after a delay of the timeout period. When a response is received by the ResponseHandler, the ResponseHandler queues the response and then cancels the named task. If the timeout period elapsed without a cancellation, the scheduled task executes and queues another request (an reschedules the timeout task).
Easier said than done, I suppose, but I hope this helps.
//Nicholas