What exactly does multicast() do? - apache-camel

What's the exact difference between
from("stream:in")
.to("stream:out", "stream:err");
and
from("stream:in")
.multicast()
.to("stream:out", "stream:err");
?

In this case - no real difference, since the incoming message body for the stream camel component seems to always be sent onwards as the outgoing message body ;)
Imagine however a more substantial case, for example:
from("stream:in")
.to("direct:one", "direct:two");
In this case, whatever is received on the stream is first sent to route direct:one. Now, if that route modifies the message in some way (e.g. setBody(constant("modified")), then route direct:two will received the modified outgoing message from the route direct:one.
Think of it like this: stream:in -> direct:one -> direct:two.
Multicast
from("stream:in")
.multicast()
.to("direct:one", "direct:two");
In contrast, with multicast, whatever is received on the stream is firstly sent to direct:one, and that same message body from the stream (as a copy) is sent to direct:two - regardless of what direct:one sets as it's outgoing message body.
We can think of the multicast like this:
stream:in -----> direct:one
\----> direct:two

Related

RxFrameNtf, TxFrameNtf and Ntf.data in unetpy

I am using Unetstack software along with Unetpy. I wish to retrieve transmit and recieve notifications when I run .py file which imports Unetpy python library. I followed this tutorial
I am successfully able to connect to the localhost and print values like phy.MTU and so on. When I transmit a packet I also receive a reply saying AGREE on the command prompt.output_of_my_script
my_script
Can you please help me in receiving Txframentf and rxframentf along with data payload.
I have made changes posted in bug reports suggested in this linkeven.
Please guide me on how to print notifications for rxframe and txframe.
Thank you``
Your script is fine until the last line:
print(phy << org_arl_unet_phy.TxFrameNtf())
Here you are trying to send a TxFrameNtf to the physical agent. This does not make sense, as it is the physical agent who sends you such a notification when a transmission is completed.
By the time you reach this line, you should have already received the notification as txntf as long as the transmission was completed within 5 seconds (timeout=5000). To print out the notification, all you need to do is:
print(txntf)
I just tested this against the 3-node-network.groovy sample. I am using unetpy-1.3b5 and fjagepy-1.4.2b3. Here's the modified code:
from unetpy import *
modem = UnetGateway('localhost', 1102)
phy = modem.agentForService(Services.PHYSICAL)
print(phy.MTU)
print(phy.basebandRate)
print(phy << org_arl_unet_phy.TxFrameReq(to=3, data=[1,2,3,4]))
txntf = modem.receive(timeout=5000)
print(txntf)
and the output:
16
4096
AGREE
TxFrameNtf:INFORM[type:1]
You can see that the TxFrameNtf is correctly received.
For reception, you need to subscribe to the agent's notifications and then receive a frame:
modem.subscribe(phy)
rxntf = modem.receive(org_arl_unet_phy.RxFrameNtf, timeout=5000)
print(rxntf)
Assuming you receive a frame within the 5 second timeout specified (in this example, on node 3), this should print out something like:
RxFrameNtf:INFORM[type:CONTROL from:1 to:3 protocol:0 rxTime:34587658 (4 bytes)]
You sent a datagram through some agent that supports the DATAGRAM service. There may be many agents that support this service (not just the physical layer). In any case, that datagram would be received on a different node, and so you wouldn't expect to receive DatagramNtf on the transmitting node.
The RangeReq should yield a RangeNtf if successful, but that might take more than the default receive timeout of 1 second, depending on how far node 2 is. So you might want to try a longer receive timeout to see if you get your notification.
To access the data from payload from the rxntf, you can try print(rxntf.data).

How to split a message do some extra processing on one of them and aggregate them back

I need to configure some camel routes based on some configuration files.
All configured routes will need to split a message into one or two sub messages then do some JMS integration work on the first one and then aggregate together the JMS reply with the optional second message. In a simplified picture it will look like below:
message -- > split --> message 1 --> JMS request/reply --> aggregate --> more processing
\--> message 2 /
The aggregation will be done on completion size which I am able to know upfront if it is going to be 1 or 2 depending of the route meta data. When the second message is present no other processing is needed before being merged back with the JMS reply.
Si in short I need a split followed by a routing followed by an aggregation which is quite a common pattern. The only particularity is is that in case the second split message is present I don't need to do anything on it before aggregating it back.
In java DSL it will looks something like this:
from("direct:abc")
// The splitter below will set the JmsIntegration flag
.split().method(MySplitter.class, "split")
.choice()
.when(header("JmsIntegration"))
.inOut("jms:someQueue"))
.otherwise()
// what should I have on here?
.to(???)
.end()
.aggregate(...)to(...);
So my questions would be:
What should I put on the otherwise branch?
What I need in fact is an if: if the split message needs JMS go to JMS and then move to aggregator if it is not just go straight to the aggregator. I am considering creating a dummy processor which will actually do nothing but this seems to me a naive approach.
Am I on a wrong path. If so what would be the alternative
Initially I was thinking about a message enricher but I would not like to sent the original message to the JMS
I also considered putting my aggregation strategy inside my splitter but again I could not put it all together.
Based off your post it looks like you are trying to have the return of your enrichment merge with the original message, but you want to send a custom message to the jms endpoint. I would recommend storing your original message in either a bean or a cache or something of the sort, leveraging all of your conversions with camel and then have your aggregation strategy leverage your storage to return your desired format.
from("direct:abc")
.split().method(MySplitter.class, "split")
.choice()
.when(header("JmsIntegration"))
.beanRef("MyStorageBean", "storeOriginal")
.convertBodyTo(MyJmsFormat.class)
//This aggregation strategy could have a reference
//to your storage bean and retrieve the instance
.enrich("jms:someQueue", myCustomAggreationStrategyInstance)
.otherwise()
.end()
.aggregate(...)
.to("direct:continueProcessing");
Option #2: Based off of your comment saying you needed the "original message that the direct:abc endpoint received this can be simplified a lot. In this example we can use camel's existing Original message store to retrieve the message that was passed into direct:abc. If Your message after the split has a JmsIntegration header we will convert the body to the desired format for the jms call, leverage the enrich statement to make the jms call and a custom aggregator that gives you access to the message used to call the jms endpoint, the message that came back, and the original message direct:abc has. If your flow does not have a JmsIntegration header the message will go to the Otherwise statement in your route which does no additional processing before ending the choice statement and then the spit messages are aggregated back together with whatever custom strategy you need.
from("direct:abc")
.split().method(MySplitter.class, "split")
.choice()
.when(header("JmsIntegration"))
.convertBodyTo(MyJmsFormat.class)
//See aggregationStrategy sample below
.enrich("jms:someQueue", myAggStrat)
.otherwise()
//Non JmsIntegration header messages come here,
//but receive no work and are passed on.
.end()
.aggregate(...)
.to("direct:continueProcessing");
//Your Custom Aggregator
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
//This logic will retrieve the original message passed into direct:abc
Message originalMessage =(Message)exchange.getUnitOfWork().getOriginalInMessage();
//TODO logic for manipulating your exchanges and returning the desired result
}
You said you considered using Enricher, but you don't want to send raw message. You can resolve this neatly by using a pre-JMS route:
from("direct:abc")
.enrich("direct:sendToJms", new MyAggregation());
.to("direct:continue");
from("direct:sendToJms")
// do marshalling or conversion here as necessary
.convertBodyTo(MyJmsRequest.class)
.to("jms:someQueue");
public class MyAggregation implements AggregationStrategy {
public Exchange aggregate(Exchange original, Exchange resource) {
MyBody originalBody = original.getIn().getBody(MyBody.class);
MyJmsResponse resourceResponse = resource.getIn().getBody(MyJmsResponse.class);
Object mergeResult = ... // combine original body and resource response
original.getIn().setBody(mergeResult);
return original;
}
}
Splitter automatically aggregates split exchanges back together. However, default (since 2.3) aggregation strategy is to return the original exchange. You can easily override the default strategy with your own by specifying it directly on the Splitter. Furthermore, if you don't have an alternative flow for your Choice, then it's much easier to use Filter. Example:
from("direct:abc")
.split().method(MySplitter.class, "split").aggregationStrategy(new MyStrategy())
.filter(header("JmsIntegration"))
.inOut("jms:someQueue"))
.end()
.end()
.to(...);
You still need to implement MyStrategy to combine the two messages.

Apache Camel - Send to seda queue only if not currently working with a message?

Would it be possible to achieve the following:
Sending msg a to Incoming, route A handles the message
Sending msg b to Incoming, route B handles the message
Sending msg a to Incoming, but since routeA is still handling the first message, this one starts blocking the incoming route to handle more messages.
Once route A is finished, the incoming router sends the third message to A and keeps getting messages from the incoming queue
from(incomingQueue)
.routeId("Incoming")
.choice()
.when(header("key").isEqualTo("a"))
.to("seda:A")
.when(header("key").isEqualTo("b"))
.to("seda:B")
;
from("seda:A")
.routeId("A")
.process(processor);
from("seda:B")
.routeId("B")
.process(processor);
SEDA will ACK back to the producer immediately and queue requests internally in routes A/B....
To block the producer in your incomingQueue route from sending to A/B routes until they have completed, then make your A/B routes DIRECT (synchronous).
note - I think this is what you asked for, but keep in mind that the A msgs/route would also block all B messages as well using this setup...
another option is to use something like ActiveMQ message groups, which allows single-threaded processing of related messages while allowing for parallel processing of other groups, etc...

Apache C module how to send response to client

I have written an Apache module that handles receiving a file from a client. I now want to send a response back to the client. I want the response to contain a string representing the file path to the file sent to the module. Since I am new to writing Apache modules I am unsure as to whether there is a response struct of some sort I need to use or if everything I need is in the request_rec passed into my handler. I noticed that ap_rprintf sends data to the client. Should I just use that? And if so, how is it sent back to the client (i.e. how can my client extract the string from things sent back to it)?
Thanks!
Edit:
I just stumbled across apr_socket_send() but I don't know if that works in this case. request_rec stores the connection, so could I create a socket to the client and send the data back that way?
Have you checked out the source code for mod_example?
Basically:
r->content_type = "text/html";
ap_send_http_header(r);
ap_rputs(DOCTYPE_HTML_3_2, r);
ap_rputs("<HTML>\n", r);
...
ap_rprintf(r, "Stuff that you want to send in the body");
...

Camel Apache: can I use a retryWhile to re-send a request?

I would like to achieve the following kind of orchestration with CAMEL:
Client sends a HTTP POST request to CAMEL
CAMEL sends HTTP POST request to external endpoint (server)
External server replies with a 200 OK
CAMEL sends HTTP GET request to external endpoint (server)
External server replies
After step 5, I want to check the reply: if the reply is a 200 OK and state = INPROGRESS (this state can be retrieved from the received XML body), I want to re-transmit the HTTP GET to the external endpoint until the state is different from INPROGRESS.
I was thinking to use the retryWhile statement, but I am not sure how to build the routine within the route.
Eg, for checking whether the reply is a 200 OK and state = INPROGRESS, I can easily introduce a Predicate. So the retryWhile already becomes like:
.retryWhile(Is200OKandINPROGRESS)
but where should I place it in the route so that the HTTP GET will be re-transmitted ?
Eg: (only taking step 4 and 5 into account)
from("...")
// here format the message to be sent out
.to("external_server")
// what code should I write here ??
// something like:
// .onException(alwaysDo.class)
// .retryWhile(Is200OKandINPROGRESS)
// .delay(2000)
// .end ()
// or maybe it should not be here ??
I am also a bit confused how the "alwaysDo.class" should look like ??
Or ... should I use something completely different to solve this orchestration ?
(I just want to re-transmit as long as I get a 200 OK with INPROGRESS state ...)
Thanks in advance for your help.
On CAMEL Nabble, someone replied my question. Check out:
http://camel.465427.n5.nabble.com/Camel-Apache-can-I-use-a-retryWhile-to-re-send-a-request-td5498382.html
By using a loop statement, I could re-transmit the HTTP GET request until I received a state different from INPROGRESS. The check on the state needs to be put inside the loop statement using a choice statement. So something like:
.loop(60)
.choice()
.when(not(Is200OKandINPROGRESS)).stop() // if state is not INPROGRESS, then stop the loop
.end() // choice
.log("Received an INPROGRESS reply on QueryTransaction ... retrying in 5 seconds")
.delay(5000)
.to(httpendpoint")
.end() //loop
I never experimented what you are trying to do but it seems does not seem right.
In the code you are showing, the retry will only occur when an alwaysDo Exception is thrown.
The alwaysDo.class you are refering to should be the name of the Java Exception class you are expecting to handle. See http://camel.apache.org/exception-clause.html for more details.
The idea should be to make the call and inspect the response content then do a CBR based on the state attribute. Either call the GET again or terminate/continue the route.
You probably should write a message to the Apache Camel mailing list (or via Nabble) . Commiters are watching it and are very reactive.

Resources