Asynchronous request-response across two Netty channels in Camel - apache-camel

I have to integrate with a legacy host that uses TCP/IP communication with separate request and response channels. You send a request to the host on one channel where it is the server, and you need to have a server channel open on which it will send the response some time later. The communication is asynchronous, so there is no guarantee that the next message you receive will be the response to the request you just sent - you have to use a correlation key in the response to tie it back to the request.
I have a Camel route that takes the incoming request and sends it out to the host, and another route that listens for the responses. I have a third route that uses an aggregator to tie the response back to the request using a correlation key. Roughly speaking, the routes look like this:
from("direct:myService")
.process(exchange -> exchange.setProperty("CorrelationKey", exchange.getIn().getBody(MyMessage.class).getCorrelationKey())
.to("netty4:tcp://somehost:555")
.to("direct:aggregate");
from("netty4:tcp://localhost:555")
.process(exchange -> exchange.setProperty("CorrelationKey", exchange.getIn().getBody(MyResponse.class).getCorrelationKey())
.to("direct:aggregate");
from("direct:aggregate")
.aggregate(header("CorrelationKey"), (oldEx, newEx) -> {
if (oldEx == null) {
return newEx;
}
oldEx.getOut().setBody(newEx.getIn().getBody());
oldEx.setProperty(Exchange.AGGREGATION_COMPLETE_CURRENT_GROUP, true)
return oldEx;
}).completionTimeout(5000)
.process(exchange -> logger.log("Received response"));
The aggregation strategy works in the sense that I only see the log message once the response has been processed. The problem is that the request route ("direct:myService") doesn't wait for the aggregation - it has already returned back to the caller. What I want is for that route to block until the aggregation strategy has got the response, so that the message received by the netty4 consumer is used as the out message of the direct:myService route. Is that possible?

Related

How do I set up a camel netty4 component for synchronous request-reply?

I need to write a camel component that will send a synchronous request-reply over TCP to a legacy system. I have tried to set up a netty4 component, but there is something weird happening. Here is my route:
from("direct:foo")
.doTry()
.process(exchange -> {
Message message = (Message) exchange.getIn().getBody();
logger.info("Sending message: {}", message);
})
.toF("netty4:tcp://localhost:%d?sync=true&synchronous=true&requestTimeout=%d&encoders=stubEncoder&decoders=stubEncoder", port, timeout)
.process(exchange -> {
Response response = (Response) exchange.getIn().getBody();
logger.info("[{}] Received reply: {}", exchange, response);
})
.endDoTry()
.doCatch(ReadTimeoutException.class)
.process(exchange -> {
// handle timeout...
})
.end();
The remote port and timeout are injected from properties. The input/output of this route will be domain objects from the rest of the application. I have set up a codec that translates from these domain objects to a simple pipe-delimited string protocol that will be sent to the remote server. I also have a stub program to simulate the legacy host, that listens on a server socket, and sends back a response on the client socket. The stub works fine if I connect to it from a test program.
What I see happening is that the route receives the incoming message, calls the codec to encode the message, and then just waits until it hits the timeout, so then goes into the timeout processor in the catch block. In my stub, I see the connection being made, and then the message is received after the timeout. The stub sends the response, but it's too late - the route has already moved on into processing the timeout. I thought that the synchronous=true option would make netty4 send immediately and wait, but that doesn't seem to be happening. Is there some other option that I'm missing?

payment gateway using socket.io and nodejs

I am developing a payment gateway (with 2 factor authorization, 2FA) with node/express for server side and angular js for client side.
The communication from client --> server can be done via a POST request but I want the communication from server --> client to happen via socket.io.
Now for new user who initiates a payment, a POST request will be sent from the client to server, data will be processed and a response will be sent back. For a very very specific reason, the response cannot be sent as part of the res in the received POST request.
app.post('/receive', function(req,res){
data = req.body
res.send('success')
// do something with the data
// Details regarding 2FA will be sent later via socket.io
}
After some processing, data will be sent to the client regarding 2 factor authorization.
io.on('2fa', {name:name, oneTimePassword:qwerty //and additional details})
I can listen to the channel 2fa from the client side, but every message emited on this will go to all the clients. I need a way to send a message to specific clients only who initiate that particular payment process.
Each transaction will be encrypted using ECDH. A UUID will be available for the transaction, initiating customer as well as the order number for which the payment is being done.
Appreciate any guidance for the above.
Socket.io has rooms. If you want, let's say, only the buyer and the seller to hear about the authentication result, put them into a room and emit your messages to the room itself. The client side won't change. You just socket.emit from the client to the server and the server will put the client into a room with all other appropriate parties and all the subsequent communication will only be emitted to the room instead of the channel 2fa.
This is from an html5 multiplayer game I developed, when the client joins a game room, this is what happens on the server side:
socket.on('joingame', function(data){
if(livingplayers[data.room] && livingplayers[data.room][data.id]){
socket.emit('joinfail', 'That name is taken');
} else {
socket.join(data.room);
socket.emit('joingame', data);
}
});
So, as you can see here, I join the client into the game, and I responded from the same channel and I informed the client that he successfully joined the game and on the server side I put the client into the room he joined. Any subsequent in-game actions will be broadcasted only to the room that the client just joined here.

Mule Request Reply: Send Reply After All the Flow Processed

currently I am working with mule. I have 3 flow: RequestFlow, ServiceResponse, and SendResponse.
On the first flow, I processed the request (transform the request parameter, write it into wmq, etc). FYI, wmq on this flow can only be used for write.
On the second flow, I read the response from server via another wmq, transform it into json, and send to VM. FYI, wmq on this flow only can be used for read.
On the third flow, I tried to send back the response to the first flow and generate a file.
To send back the response from flow 3 to flow 1, I try to use request-reply
But, unfortunately, when I tried to send a request, I found out that:
After it reach request-reply component on the first flow, it will directly go to the third flow.
And then, after mule processed all the operation on the third flow, it will send the response back to the request-reply component.
Do some logging (logger component on first flow)
Then, go to the flow, processed all the operation
Processed the third flow again
That's why, after all the process has been finished, my application will:
Generate 2 files (1 contain request xml and 1 contain json response)
Return the request xml to http
However, It's not what I want. The flow that I need is:
Mule processed the operation on first flow until the request-reply component
Go to the second flow and processed all the component
After it finish with second flow, it will goes to third flow. Proceed all the component
Send the reply back to request-reply component on the first flow
Do some logging (logger component in first flow)
And finish
Result from this application should be:
1 File contain JSON response
JSON Response on http
So, how to do so? Thanks in advance.
You don't show the flow that consumes the messages sent to the sender path by the VM outbound endpoint in request-reply: I'm assuming it's a flow that takes care of sending the message to the server.
It seems that all you miss is an VM outbound endpoint in SendResponse that would send the message to the response path, onto which the VM inbound is waiting in the request-reply.
PS. Of course, it's assumed the the server propagates the JMS correlation ID from the request message to the response message, otherwise Mule (nor any client for that matter) could ever reconnect the response to the request and the request-reply would fail.
PPS. You don't need an all router around the single VM outbound endpoint in request-reply.

Spring integration | How to preserve message context when using HTTP outbound gateway?

I need to POST a JSON structure to a REST endpoint and process the data it returns (all of this is with JSON).
I am planning to use a HTTP outbound gateway for this purpose. Now the thing is that after I have transformed my object (payload of the message) into a JSON format and before I transmit it to the endpoint the payload should be dropped into a database so that in case the endpoint is not available the call can be retried.
As I want to
a) set the status accordingly after the call`
b) update the
respective row with a uuid from the REST endpoint
I need to somehow relate the uuid from my object (the business key) as part of the outbound message to the response of the REST endpoint that is placed on the reply channel. As I cannot ask the provider to return my uuid as part of the response how can I achieve this purely on the client side?
You can add a custom advice to the outbound endpoint using the request-handler-advice-chain. Simply subclass AbstractRequestHandlerAdvice. It's effectively an 'around' advice so you can store it in the DB before invoking the handler and update the status afterwards.
See 'Adding Behavior to Endpoints'
and specifically 'Custom Advice Classes'

Camel CXFRS response

camel-fuse 2.8
I have a camel jaxrs server which accepts requests then kicks-off 2 Camel routes.
The first route, consumes requests from cxfrs endpoint/bean and ships them to jms queue inbox.
The second route, consumes requests from jms queue inbox for business logic processing, then ships the results to jms queue outbox.
My question is related to http response and sending the results to jaxrs server consumer.
Is it possible to send an http response back to http client from first route with results from second route? (synchronously)
from("cxfrs:bean:personLookupEndpoint") <-- http client waits for response...
.setExchangePattern(ExchangePattern.InOut)
.process(new RequestProcessor())
.to(inbox);
from(inbox)
.unmarshal(jaxb)
.process(new QueryServiceProcessor())
.to("bean:lookupService?method=processQuery(${body})")
.convertBodyTo(String.class)
.to(outbox); <-- need to send results to font-end consumer synchronously ...
Do you really need to do it using queues? I think that it would be better to use direct: routes instead.
There is a possibility to use the InOut exchange pattern for a JMS endpoint, but it has some limitations: http://fusesource.com/docs/router/2.2/transactions/JMS-Synchronous.html

Resources