Reusing routes for inbound/outbound communication - java

I have the following two routes:
Route 1
from("netty:tcp://localhost:5050?textline=true&encoder=#customStringEncoder&decoder=#customDecoder")
.routeId("inboundSocketRoute")
.doTry()
.unmarshal(beanIO)
.bean(inboundInterfaceProcessor, "processInterfaceData")
.doCatch(ValidationException.class, UnidentifiedRecordException.class, InvalidRecordException.class)
.bean(inboundInterfaceProcessor, "processInterfaceDataError")
.end();
Route 2
String server = "123.45.67.89:5050";
from("jms://queue:evOutboundDataInterface")
.routeId("outboundInterfaceRoute")
.doTry()
.bean(outboundInterfaceProcessor, "processOutboundData")
.to("netty:tcp://" + server + "?textline=true&requestTimeout=10000&encoder=#customStringEncoder&decoder=#customDecoder")
.bean(outboundInterfaceProcessor, "processSequence")
.doCatch(ReadTimeoutException.class)
.bean(outboundInterfaceProcessor, "handleTimeout")
.doCatch(ConnectException.class)
.bean(outboundInterfaceProcessor, "handleConnectionError")
.end();
Route 1 is triggered by incoming data from an external server. The incoming data is parsed by BeanIO and eventually ends up in my InterfaceProcessor, which treats inbound data. Route 2 is triggered by a JMS message (which is sent by my software) and it should send a message back to the external server on the same port.
In my current setup, I start my own server (route 1) and client (route 2). I don't think this will work, as both connections are always active. This way, when I want to send a message to the external server from route 2, it probably won't be able to connect to the external server. However, when I receive a message on route 1, I am able to send a message back to the external server (from within the processor that is bound to that route) by having inboundInterfaceProcessor.processInterfaceData() return the String that will be sent to the external server.
Because I'm able to send a message back to the external server from route 1, I'm thinking about triggering inboundInterfaceProcessor from outboundInterfaceProcessor to send a message to the external server from route 2. How should I do this? Is this even possible with Camel/Netty? Or should I use another approach to this problem?

I solved this by having the external server poll for data that would normally be sent through the second route. Every message is answered with an acknowledge message, so when this acknowledgement is received by the inbound interface, I can check for more outgoing interface data. It isn't ideal, but it works. :)

Related

Using Apache Camel with multiple websocket endpoints

I have a Jetty endpoint receiving push-messages of different types from several data-providers - let´s call them type1, type2.
I also provide set of websocket-endpoints - one for each type - where clients can subscribe to push-updates of the different message-types (/ws_type1, /ws_type2).
The dataproviders and the clients are not the same.
Based on the type of incoming message, I am trying to get the camel route to push updates only to the clients subscribing to the specific messagetype.
My code is something like this:
from("jetty:http://0.0.0.0:8888/incoming_1?matchOnUriPrefix=true")
.log("incoming_1")
.to("websocket://ws_type1?sendToAll=true");
from("jetty:http://0.0.0.0:8888/incoming_2?matchOnUriPrefix=true")
.log("incoming_2")
.to("websocket://ws_type2?sendToAll=true");
from("jetty:http://0.0.0.0:8080/incoming?matchOnUriPrefix=true")
.log("incoming")
.to("websocket://woot?sendToAll=true");
from("websocket://ws_type1")
.log("ws_type1")
.to("websocket://ws_type1")
;
from("websocket://ws_type2")
.log("ws_type2")
.to("websocket://ws_type2")
;
The result of this is that ALL messages of ALL types are sent to ALL the connected clients - regardless if they are connected to the /ws_type1 or /ws_type2 endpoints.
Is it possible to filter the updates to different sets of clients? I.e. messages of type1 are pushed to the clients connected to /ws_type1, and messages of type2 are pushed to the clients connected to /ws_type2.
EDIT:
Using the code above, I am able to connect websockets to ws-endpoints "ws_type1", "ws_type2", and "woot". However - when I post data to the "/incoming_1" jetty-endpoint ALL three websockets receive the data, but only "incoming_1" is logged.
Am I missing something?
EDIT 2:
I looked at the camel-websocket source, and it seems like this is by design. There is no filtering on which websockets the message-contents are sent to, and the content is written directly to the socket. This also means that the camel-routes "ws_type1" and "ws_type2" in the example above will not be called when the server pushes data.
....
Collection<DefaultWebsocket> websockets = store.getAll();
Exception exception = null;
for (DefaultWebsocket websocket : websockets) {
try {
sendMessage(websocket, message);
...
Full source

Jetty Websockets - Correctly sending async messages when handling unreliable connections

I'm using Jetty 9.3.5 and I would like to know what is the proper way to handle unreliable connections when sending websocket messages, specifically: I noticed cases when a websocket connection does not close normally so, even though the client side is down, it takes a lot of time until onClose() is triggered on the server (for ex. a user closes the laptop lid and puts it in standby - it can take 1-2 hours until the close event is received on the server side).
Thus, because the client is still registered, the server keeps sending messages that begin to build up. This becomes an issue when sending a large number of messages.
I've tested sending byte messages with:
Session.getRemote().sendBytes(ByteBuffer, WriteCallback)
Session.getRemote().sendBytesByFuture(ByteBuffer);
To simulate the connection down on one side (ie. user puts laptop in standby), on Linux, I assigned an IP address to eth0 interface, started sending the messages and then brought it down:
ifconfig eth0 192.168.1.1
ifconfig eth0 up
--- start sending messages (simple incremented numbers) and connect using Chrome browser and print them ---
ifconfig eth0 down
This way: the messages were still being sent by Jetty, the Chrome client did not receive them, the onCllose or onError was not triggered on server-side
My questions regarding Jetty are:
Is there a way to clear queued messages that were not delivered?
I've tried, but with no luck:
Session.getRemote().flush();
Can a max number of queued messages be set?
I've tried:
WebSocketServletFactory.getPolicy().setMaxBinaryMessageBufferSize(1)
Can I detect if the client does not receive the message? (or if the connection is in abnormal state let's say)
I've tried:
session.getRemote().sendBytes(bb, new WriteCallback() {
#Override
public void writeSuccess() {
//print success }
#Override
public void writeFailed(Throwable arg0) {
//print fail
}
});
But this prints success even though the messages are not received.
I also tried to use, but couldn't find a solution:
factory.getPolicy().setIdleTimeout(...);
factory.getPolicy().setAsyncWriteTimeout(3000);
sendPing()
Thanks in advance!
Unfortunately, the WebSocket protocol, being a message passing protocol isn't really designed for this level of nuance between messages.
The first message MUST complete before you can even think of sending the next message. So if you have a message in process, then there is no way to safely cancel that message.
At best, an API could exist to truncate that message with a CONTINUATION / empty payload / fin=true.
But even then the remote endpoint wouldn't know that you canceled the message, it would just see a partial message.
Detecting connectivity issues is best handled with either OS level events (like Android's Connectivity intents), or via periodic websocket PING (which inserts itself in front of the line for outgoing websocket frames.
However, even with PING, if your outgoing websocket frame is in-progress, even the PING cannot be sent until that websocket frame is done sending.
RemoteEndpoint.flush() will attempt to flush any pending messages (and frames), not clear out pending messages (or frames).
As for detecting if client got the message, you'll need to implement some sort of message ACK into your own layer to verify that, the protocol has no such concept. (Some libs/apis built on top of websocket have implemented message ACK in that layer. The cometd message ack extension comes to mind as a real world example)
What sort of situation are you attempting to solve for?
Perhaps using the RemoteEndpoint.sendPartialString(String, boolean) or RemoteEndpoint.sendPartialBytes(ByteBuffer, boolean) to send smaller frames of the whole message could be useful to you. However, the other side might not have an API that can read those partial frames (eg: Javascript in a browser).

RabbitMQ RPC tutorial query

I was going through the tutorial shared by RabbitMQ here
I am assuming that the client code below
while (true)
{
var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
if (ea.BasicProperties.CorrelationId == corrId)
{
return Encoding.UTF8.GetString(ea.Body);
}
}
Would receive all messages on the queue and will unnecessarily iterate through messages not designated for it. Is their anyway we can avoid it i.e we can modify the client to only receive the messages intended for it only.
The basic work that i intend to achieve through RabbitMQ is Request-Response pattern where a request would be received by web-service which will send data in a queue the data object would have a unique reference number . This would be received by an asynchronous tcp-client which will send data on a tcp/ip layer based on message it had received.
On receiving reply from the asynchronous channel of tcp/ip the channel would parse the data and respond back on the queue with the corresponding request reference number.
The RPC approach is well suited for it but the client code shared have this shortcoming would appreciate feedback on it.
Actually I didn’t understand well your aim, but when you create an RPC model, you have to create an “reply queue”, this queue is bound only to the client.
It means that you will receive back only the client messages, and not all messages.
Since the Rabbitmq RPC model is asynchronous you can execute more than one request without wait the responses and replies could not have the same publish order.
The correlation id is necessary to map your client requests with the replies, so there are not "unnecessarily" messages
hope it helps

Springs integration's reply correlation process details

I can't find documentation for reply processing with gateways and service activators.
If I have gateway which:
1) sends requests to channel ReqChannel
2) accepts replies on channel RepChannel
ReqChannel is connected to router, that routes incoming messages to one of some service activators, let say AServiceActivator and BServiceActivator and that service activators have a configured output-channel="RepChannel".
And if I execute more than one method call on gateway's interface asynchronously or simultaneously from different threads, how gateway will correlate incoming replies to actual service caller?
The gateway creates a temporary reply channel and puts it in the header of the message. This mechanism provides the necessary correlation because each message gets its own reply channel.
If the final consumer (say a service-activator) has no output-channel, the framework automatically sends the reply to the replyChannel header.
For this reason, it is generally not necessary to declare a reply-channel on the gateway for the final consumer to send to.
However, there are times when this is useful - such as if you want to wire-tap the reply channel, or make it a publish-subscribe channel, so the result goes to multiple places.
In this case (when there is a reply-channel on the gateway, and the final consumer sends a message there), the framework simply bridges the explicitly declared reply-channel to the temporary reply channel in the message header.
For this reason, it is critical to retain the replyChannel header in your flow. You can't send some arbitrary reply to a reply-channel, unless you include the original message's replyChannel header.

How to make a JMS Synchronous request

I have an webapp that is expected to fetch and display data from an External App which is accessible only via messaging (JMS).
So, if a user submits a request on a browser, the same HTTP request thread will have to interact with the Messaging system (MQ Series) such that the same request thread can display the data received from the Messaging System.
Is there a pattern I can make use of here? I saw some vague references on the net that use "Correlation ID" in this way:
Msg m = new TextMsg("findDataXYZ");
String cr_id = m.setCorrelationID(id);
sendQueue.send(m).
// now start listening to the Queue for a msg that bears that specific cr_id
Response r = receiverQueue.receive(cr_id);
Is there something better out there? The other patterns I found expect the response to be received asynchronously.. which is not an option for me, since I have to send the response back on the same HTTP request.
The request/reply messaging pattern is useful for your requirement. You typically use a CorrelationId to relate request & reply messages.
While sending request message you set JMSReplyTo destination on the message. Typically a temporary queue is used as JMSReplyTo destination. When creating a consumer to receive response use a selector with JMSCorrelationId, something like
cons = session.createConsumer(tempDestination,"JMSCorrelationId="+requestMsg.JMSMessageId);
At the other end, the application that is processing the request message must use the JMSReplyTo destination to send response. It must also use the MessageId of the request message and set it as CorrelationId of the response message.
First, open the response queue. Then pass that object to the set reply-to method on the message. That way the service responding to your request knows where to send the reply. Typically the service will copy the message ID to the correlation ID field so when you send the message, take the message ID you get back and use that to listen on the reply queue. Of course if you use a dynamic reply-to queue even that isn't neessary - just listen for the next message on the queue.
There's sample code that shows all of this. If you installed to the default location, the sample code lives at "C:\Program Files (x86)\IBM\WebSphere MQ\tools\jms\samples\simple\SimpleRequestor.java" on a Windows box or /var/mqm/toolsjms/samples/simple/SimpleRequestor.java on a *nix box.
And on the off chance you are wondering "install what, exactly?" the WMQ client install is downloadable for free as SupportPac MQC71.

Categories