all.
I have some route of camel.
My scenario is below.
1. file size checking on remote server
2. store the file size on header
3. get the file by sftp through pollenrich
4. compare the size of downloaded file with the size of original header value
5. if there is different, retry the download
I know that the value of old header is disappear after pollenrich.
Is there any anything to meet my scenario I can do?
Thank you.
As #burki has said, a custom aggregation strategy will work. Here is an example propagating a message header and an exchange parameter from the initial exchange to the new exchange.
.pollEnrich()
.simple("myUrl?param=${header.myUrlParameter}")
.aggregationStrategy( new AggregationStrategy() {
public Exchange aggregate( Exchange oldExchange, Exchange newExchange )
{
newExchange.getIn().setHeader( "MyHeader", oldExchange.getIn().getHeader( "MyHeader" ) );
newExchange.setProperty( "MyProperty", oldExchange.getProperty( "MyProperty" ) );
return newExchange;
} } )
I hope this helps.
You should be able to use pollEnrich with an AggregationStrategy that implements how the original message and the enrich message are merged.
I guess you are getting the merge result of the default strategy because you don't reference a strategy.
Have a look at the first example of the Enrich Options in the Camel docs (section "Using the Fluent Builders").
I think you should be able to integrate headers from the original message into the merged message so that they are still available after the enrich step.
Related
I am trying to read a digitally signed mail from java code using multipart and mime messaging and fetch the attachments (xml, pdf, txt etc.,) and message details.
My code is working fine for mails having Content-Type as : multipart/signed; protocol="application/x-pkcs7-signature";
But For few mails having Content-Type as : application/pkcs7-mime; smime-type=signed-data; name=smime.p7m it is not fetching the attachments and message details. Can anyone explain what is the difference between both of them and how to resolve it.
I recently came across this issue myself, and although this question is three month old, I leave an answer with my findings, just in case.
Both kinds of messages are instances of S/MIME signed messages as specified in RFC2633 (https://www.rfc-editor.org/rfc/rfc2633).
The multipart/signed; protocol="application/x-pkcs7-signature" indicates a clear-signed message (section 3.4.3.3 of the RFC), meaning you can read the original message content without having S/MIME capabilities in your client code. Hence no problem with these.
The application/pkcs7-mime; smime-type=signed-data; name=smime.p7m indicates an S/MIME signedData email (section 3.4.2) Your client code needs S/MIME capability in order to read the original message (even if you don’t care about the signature).
Easiest way (worked for me) is to use bouncycastle's SMIMESigned class (from the S/MIME API, https://mvnrepository.com/artifact/org.bouncycastle/bcmail-jdk15on), like this:
byte[] content = <the signed data's content as byte[]>;
ByteArrayDataSource dataSource = new ByteArrayDataSource(content,"multipart/signed");
SMIMESigned signedData = new SMIMESigned(new MimeMultipart(dataSource));
MimeBodyPart bodyPart = signedData.getContent();
<you can process the body part as normal from here>
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.
I have some problems with a following route:
// from("cxf:....")...
from("direct:start").process(startRequestProcessor) // STEP 1
.choice()
.when(body().isNull())
.to("direct:finish")
.otherwise()
.split(body()) // STEP 2
.bean(TypeMapper.class) // STEP 3
.log("Goes to DynamicRouter:: routeByTypeHeader with header: ${headers.type}")
.recipientList().method(Endpoint1DynamicRouter.class, "routeByTypeHeader") // STEP 4
.ignoreInvalidEndpoints();
from("direct:endpoint2") // STEP 6
.log("Goes to DynamicRouter::routeByCollectionHeader with header: ${headers.collection}")
.recipientList().method(Endpoint2DynamicRouter.class, "routeByCollectionHeader")
.ignoreInvalidEndpoints();
from("direct:endpoint1.1") // STEP 5
.process(new DateRangeProcessor())
.to("direct:collections");
from("direct:endpoint1.2") // STEP 5
.process(new SingleProcessor())
.to("direct:collections");
from("direct:endpoint2.2") // STEP 7
.aggregate(header("collection" /** endpoint2.2 */), CollectionAggregationStrategy)
.completionSize(exchangeProperty("endpoint22"))
.process(new QueryBuilderProcessor())
.bean(MyService, "getDbCriteria")
.setHeader("collection", constant("endpoint2.1"))
.to("direct:endpoint2.1").end();
from("direct:endpoint2.1") // STEP 8
.aggregate(header("collection" /** endpoint2.1 */), CollectionAggregationStrategy)
.completionSize(exchangeProperty("CamelSplitSize"))
.to("direct:finish").end();
from("direct:finish")
.process(new QueryBuilderProcessor())
.bean(MyRepository, "findAll")
.log("ResponseData: ${body}").
marshal().json(JsonLibrary.Gson).end();
The route
Receives json string an converts it to list (HashSet) of JSONObjects.
split the received list to json objects.
Set corresponding headers according to object content
Routes the messages according to headers to endpoint1.1 or endpoint1.2
Convert messages to mongodb Criteria and send to endpoint2
Endpoint2 routes messages according to another header to endpoint2.1 or endpoint2.2.
Endpoint2.2 aggregates all received messages, processes it to get mongodb Criteria and sends it to endpoint2.1 (completionSize is calculated at step 2 and saved in property "endpoint22").
Enpoint2.1 aggregates ALL messages (CamelSplitSize) converts aggregated messages to Query object and sends it to Repository to retrieve the data.
I can see valid response object in debugger but anyway I get an error:
No message body writer has been found for class java.util.HashSet, ContentType: application/json
The problem is not in response object as it works with other routes and it does not contain HashSets.
My guess is that route sends to the output the HashSet created tat STEP 1...
My questions are:
what is wrong in the route output?
both recipientList() try to forward
messages to invalid endpoint ( I have to use .ignoreInvalidEndpoints() to avoid exception):
org.apache.camel.NoSuchEndpointException: No endpoint could be found for:
org.springframework.data.mongodb.core.query.Criteria#20f55e70, please
check your classpath contains the needed Camel component jar.
Any help would be much appreciated!
Thanks.
I find it very strange, but .aggregate() function does not reply exchange. It uses you aggregation strategy but always reply incoming exchange. This is not clear when reading documentation, but you have to use aggregation strategy along with split() to be able to return exchange.
When sending emails via javamail, the following is always appended to the bottom of each message:
This email and any files transmitted with it are confidential and
intended solely for the use of the individual or entity to whom they
are addressed. If you have received this email in error please notify
the system manager. This message contains confidential information and
is intended only for the individual named. If you are not the named
addressee you should not disseminate, distribute or copy this e-mail.
Please notify the sender immediately by e-mail if you have received
this e-mail by mistake and delete this e-mail from your system. If you
are not the intended recipient you are notified that disclosing,
copying, distributing or taking any action in reliance on the contents
of this information is strictly prohibited.
How does one prevent this?
(NOTE: This problem is extremely frustrating to research on the web due to the fact that a disclaimer of this form is attached to so many indexed documents! :-(
JavaMail is not doing that, it is your outgoing SMTP server appending it to each message, probably set up by IT.
To confirm, you can use gmail's servers (with a personal account) and you will see it does not get added to the messages.
This should work. Pay attention to the form in which email body get parsed. In my case the emailBody string is on one line, so you have to put the "#Your disclaimer Here#" on one line. Answer for who will come in future.
public String deleteDisclaimer(String emailBody) {
String disclaimer = "#Your disclaimer here#";
if (emailBody.contains(disclaimer)) {
System.out.println("Deleting Disclaimer..");
return emailBody.substring(0,emailBody.indexOf(disclaimer));
}
System.out.println("DISCLAIMER NOT FOUND!");
return emailBody;
}
I am trying to connect to rabbitmq-c in centos 5.6 and test its function in c client following the steps of the website: http://www.rabbitmq.com/tutorials/tutorial-one-java.html.
However, it fails when I use the default exchange.
For example, I want to send a message, "Hello world", to a queue named "myqueue" via the default exchange whose name is "(AMQP default)".
In java, here is the code:
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
But in c, when I run rmq_new_task.c (almost the same as amqp_sendstring.c) as the examples on https://github.com/liuhaobupt/rabbitmq_work_queues_demo-with-rabbit-c-client-lib.
queuename="myqueue";
......
die_on_error(amqp_basic_publish(conn, amqp_cstring_bytes(exchange),
amqp_cstring_bytes(routingkey), &props, amqp_cstring_bytes("Hello world")),
"Publishing");
In the java client, we just set the parameter "exchange" to "" to tell the server that we'd send the message to a specified queue named the same as routingkey via the default exchange.
So what value should I give the second parameter "exchange" in c client (using the default exchange)? I tried to set it to "" or "amq.direct". It didnot show any error while running and seemed working well.
However, when I checked in the rabbitmq-management(http://localhost:55672/#/queues), the queue named "myqueue" did not exist!
Would someone please point me to the right direction? I'd really appreciate!
Take a look at http://www.rabbitmq.com/tutorials/amqp-concepts.html and specifically look for the section entitled Default Exchange.
The usage of the default exchange is very simple.
In java you would do:
channel.basicPublish("", "hello", null, message.getBytes());
By specifying "" in says to use the default exchange. (There should be no need to use amq.direct)
As per the article above it states:
The default exchange is a direct exchange with no name (empty string)
pre-declared by the broker. It has one special property that makes it
very useful for simple applications: every queue that is created is
automatically bound to it with a routing key which is the same as the
queue name.
So that means publishing to the default exchange will only work if you have already created the queue that you want to publish to.
So you will need to create your queue before you can publish to the default exchange. Once you've done that you will start seeing your messages.