Aggregation Program - java

I am trying to learn more about Apache Camel. I found the documentation somewhat helpful but leaves alot of guessing for beginner Camel riders that do not know how small code segments should easily fit into fully functioning programs. Hopefully most people know what I am trying to describe. I have been lost lots of other times in some programming books where segments of code are shown outside of the context of a fully running program.
Anyway here is my program that does not aggregate messages for some reason. I was hoping it would aggregate all my messages but this program does not do that. When running the program I receive an empty file as the output which is not my goal.
package laser.helmet.camel.friend;
import org.apache.camel.builder.RouteBuilder;
public class AggregatingMessagesRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:start")
.aggregate().constant(true)
.completionTimeout(100L)
.groupExchanges()
.to("file:target/this_folder/result?allowNullBody=true");
}
}
Then I call this program from the below class which has the main method of course.
package laser.helmet.camel.friend;
import org.apache.camel.CamelContext;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.ProducerTemplate;
public class Main {
public static void main(String[] args) throws Exception {
CamelContext c = new DefaultCamelContext();
c.addRoutes(new AggregatingMessagesRoute());
ProducerTemplate pt = c.createProducerTemplate();
c.start();
pt.sendBody("direct:start", "1");
pt.sendBody("direct:start", "2");
Thread.sleep(5000);
c.stop();
}
}
I was expecting the body of the two messages I create with the ProducerTemplate to be both in the file after the route finishes but it is just a blank file. I had to add the part allowNullBody=true to the route because for some reason the body is null when running this program.
Also if you are a beginner and wondering. I am bringing in the dependencies with Maven instead of putting the camel.jars on my Java classpath.
Thank-you for reading this everyone. :D
So how can I start aggregating messages Stackoverflow? 0_o
Peace,
user_loser

This is an easy one since you provided the code snippet, nice one!
Remove .groupExchanges() since it is becoming deprecated and it is hardly adequate. You always need a AggregationStrategy to have fine grained control over how to aggregate your exchanges. So, add the following class:
class StringAggregationStrategy implements AggregationStrategy {
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
if (oldExchange == null) {
return newExchange;
}
String oldBody = oldExchange.getIn().getBody(String.class);
String newBody = newExchange.getIn().getBody(String.class);
oldExchange.getIn().setBody(oldBody + "+" + newBody);
return oldExchange;
}
}
And then reference it in your route; so it becomes something like this:
public class AggregatingMessagesRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:start")
.aggregate().constant(true)
.completionTimeout(100L)
.aggregationStrategy(new StringAggregationStrategy())
.to("file:target/this_folder/result?allowNullBody=true");
}
}
Hope this helps!
R.

Related

How to create a test for DeadLetter Kafka

In my little microservice, I created a Producer Kafka to send the messages with errors (messages having errors in the JSON format) inside the DeadLetter in this way :
#Component
public class KafkaProducer {
#Autowired
private KafkaTemplate<String, String> kafkaTemplate;
public void sendDeadLetter(String message) {
kafkaTemplate.send("DeadLetter", message);
}
}
I would like to create a JUnitTest for the completeness of the project, but I have no idea how to create the eventuality of a possible JSON error in order to create the test. I thank everyone for any possible help and advice
To create a JUnitTest consistent with your code. I should recreate the case where you pass it a warped or invalid JSON. In your case, I would opt to configure a MockConsumer from which to read any message that the logic of your code will be invited to the dead letter.
To have a usable test structure, I recommend something like this:
#KafkaListener(topics = "yourTopic")
public void listen(String message) {
messages.add(message);
}
For testing a basic structure could be
#Test
public void testDeadLetter(){
//Set up a mockConsumer
MockConsumer<String,String> yourMockConsumer = new MockConsumer<String,String> (OffsetResetStrategy.EARLIEST);
yourMockConsumer.subscribe(Collections.singletonList("yourTopic"));
//Sending message on embedded Kafka broker
String error = "ERRORE";
kafkaTemplate.send("yourTopic", error);
//Reading the message may take a second
Thread.sleep(1000);
//Create an Assert that checks you that the message is equal to the error specified above
}
I hope it will be useful to you!
You can create Kafka topic using testcontainers and write your tests on top of that.
Sharing an example on how to use testcontainers https://github.com/0001vrn/testcontainers-example

MockEndpoint fails to run route

Using mock to launch a salesforce streaming route as shown here fails for the following route:
from("salesforce:AccountUpdateTopic?notifyForFields=ALL&notifyForOperations=ALL")
.tracing().convertBodyTo(String.class).to("file:D:/tmp/")
.to("mock:output")
.log("SObject ID: ${body}");
in
package org.apache.camel.component.salesforce;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.component.salesforce.internal.OperationName;
import org.junit.Test;
public class StreamingApiIntegrationTest extends AbstractSalesforceTestBase {
#Test
public void testSubscribeAndReceive() throws Exception {
MockEndpoint mock = getMockEndpoint("mock:AccountUpdateTopic");
mock.start();
Thread.sleep(10000);
mock.stop();
}
#Override
protected RouteBuilder doCreateRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
// test topic subscription
from("salesforce:AccountUpdateTopic?notifyForFields=ALL&notifyForOperations=ALL").tracing().convertBodyTo(String.class).to("file:D:/tmp/").to("mock:output").log("SObject ID: ${body}");
}
};
}
}
Running this test does not start the route (updates are not fetched from Salesforce and stored in /tmp/).
Can mock run a route and wait for updates from Salesforce? Is there a shorter example that allows for testing salesforce routes without making use of spring?
You misunderstood the Camel Mock component. Mocks are not starting anything. They are just endpoints who record and assert the messages they receive.
To trigger a Camel route you have to send a message to it. You can do this easily using a ProducerTemplate.
It is this line from the example you mention that does exactly that.
CreateSObjectResult result = template().requestBody(
"direct:upsertSObject", merchandise, CreateSObjectResult.class);
template is the ProducerTemplate and requestBody the method to send a message to the endpoint direct:upsertSObject and wait for a response. See the Javadocs of ProducerTemplate for the various existing signatures.

How to send a message to an actor from outside in Play Framework 2?

I am new to Akka and trying to write some code in Play Framework 2 in Java and use Akka.
To create an actor and send a test message to it, I have:
public class Global extends GlobalSettings {
#Override
public void onStart(Application app) {
final ActorRef testActor = Akka.system().actorOf(Props.create(TestActor.class), "testActor");
testActor.tell("hi", ActorRef.noSender());
}
}
This work perfectly fine and I see that my actor received my message, here is the code for my actor:
public class TestActor extends UntypedActor {
#Override
public void onReceive(Object message) throws Exception {
if(message.toString().equals("hi")){
System.out.println("I received a HI");
}else{
unhandled(message);
}
}
}
Very simple.
However, If I try to send a message from a controller:
public static Result index() {
final ActorRef testActor = Akka.system().actorFor("testActor");
testActor.tell("hi", ActorRef.noSender());
return ok(index.render("Your new application is ready."));
}
I get this message on terminal:
[INFO] [09/20/2014 11:40:30.850] [application-akka.actor.default-dispatcher-4] [akka://application/testActor] Message [java.lang.String] from Actor[akka://application/deadLetters] to Actor[akka://application/testActor] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
Can someone help me with this? why the first usage works and the second one fails? Thanks
The actorFor method requires the entire path, and your actor lives in the user space, so you have to use actorFor("/user/testActor"). Currently you are sending it to application/testActor, which would be a top-level actor in the ActorSystem itself.
By the way, actorFor is deprecated (at least in the Scala API) and replaced by actorSelection.
For more information, refer to the excellent documentation.
actorFor should get the path to the actor which is probably "akka://System/user/testActor". It also does not create the actor, meaning it should be exist.
Anyway, is there a reason that inside the controller you use the actorFor and not the actorOf? It had been deprecated and shouldn't be use.

Camel: how to check response http response

I am pretty new with Camel. I have been trying to submit a data (Json from a file) to a webservice. This is my code:
public static void main(String args[]) throws Exception {
// create CamelContext
CamelContext context = new DefaultCamelContext();
// add our route to the CamelContext
context.addRoutes(new RouteBuilder() {
#Override
public void configure() {
from("file:data/inbox?noop=true")
.marshal()
.string()
.setHeader(Exchange.CONTENT_TYPE,constant("application/json"))
.to("http://www.a-service.com");
}
});
// start the route and let it do its work
context.start();
Thread.sleep(10000);
// stop the CamelContext
context.stop();
}
Then the webservice will response with Json which can be
{result:OK}
or
{result:FAIL}
Now, if a response has responseCode as 200, Camel will consider as success.
My question is, how can I have a validating process for responsed JSon so that if it is FAIL, Camel should not consider as success?
Solution Credit #Namphibian:
By adding processor and the end. This code has been tested:
from("file:data/inbox?noop=true")
.marshal()
.string("UTF-8")
.setHeader(Exchange.CONTENT_TYPE,constant("application/json"))
.to("http://monrif-test.userspike.com/monrif/rss/monrif_-all-global")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Message in = exchange.getIn();
String msg = in.getBody(String.class);
System.out.println("Response: " + msg);
if(msg.contains("OK")){
// go to party
}else{
throw new Exception("test exception");
}
}
});
There are two broad strategies you can use to achieve this.
Processor Based:
Add a processor to the end of the route. In this processor do the check if the webservice then responds with a true or false value.
A processor would look something like this:
package com.example;
import java.util.Map;
import org.apache.camel.Body;
import org.apache.camel.Exchange;
import org.apache.camel.Handler;
import org.apache.camel.Headers;
import org.apache.camel.Message;
public class GeneralProcessor {
#Handler
public void PrepapreErrorImportReport
(
#Headers Map hdr
, Exchange exch
)
{
//todo: Get the message as a string;
Message in = exch.getIn();
String msg = (String)in.getBody();
// Now check if body contains failed or ok.
if(msg.contains("OK")){
//todo: go party the message was OK
}
else{
//todo: Oh Oh! Houston we have a problem
}
}
}
You can then modify your route to use this processor.
The Simple Expression Language
This is one way the other way is to use the simple expression language. See the example below on how to use this.
from("file:data/inbox?noop=true")
.marshal()
.string()
.setHeader(Exchange.CONTENT_TYPE,constant("application/json"))
.to("http://www.a-service.com")
.choice()
.when(simple("${body} contains 'OK'")).to("activemq:okqueue")
.otherwise().to("activemq:queue:other");
Notice the simple("${body} contains 'OK'") piece of code. That is the power of simple.
Both approaches have uses.
In the Process method , you can use below method and it will work
LOGGER.info("Response code " + message.getHeader(exchange.HTTP_RESPONSE_CODE, Integer.class));

ClassNotFoundException with JGroups

I have an issue with JGroups where after building my project, running it produces this error:
Caused by: java.lang.ClassNotFoundException: org.jgroups.ReceiverAdapter
My class looks something like this -
import org.jgroups.ReceiverAdapter;
import org.jgroups.Channel;
import org.jgroups.JChannel;
public class MyClass extends ReceiverAdapter implements MyInterface {
Channel channel;
String state = "state";
public MyClass() {
super();
start();
}
public void start() {
try {
channel = new JChannel();
channel.setReceiver(this);
channel.connect("ServerCluster");
channel.getState(null, 0);
System.out.println("Connected to cluster");
} catch (Exception e) {
System.out.println("Failed to connect to cluster");
}
}
public void getState(OutputStream output) throws Exception {
System.out.println("get response");
}
public void setState(InputStream input) throws Exception {
System.out.println("set test");
}
}
Running the project from IntelliJ produces no errors, but does not produce the desired prints from getState() and setState() either. I tried creating a brand new project in the Eclipse IDE, but the same is happening there too. Connecting has been working fine, states is a new addition to my project.
Running java MyClass from the command line fires the error seen at the start of this question. The JGroups jar seems to be added to the classpath properly as org.jgroups.Channel and org.jgroups.Channel (among others) are being found.
There is a SimpleChat program provided by the JGroup devs, but when I created a new project for this I encountered the same problem.
Edit
So it turns out I have to explicitly set the classpath when running from the CLI. But still, when running the code it seems like the getState() and setState() methods are never called as there are no print statements. SimpleChat doesn't print received state... like it is meant to.
Does anyone have a solution?
Best.
So, I on the JChannel I was using RpcDispatcher and it seems I can't use the dispatcher and the getState() and setState() methods on the same channel. Simple solution: create a second channel. Seems my knowledge on the fundamentals of JGroups is lacking!

Categories

Resources