How to browse messages from a queue using Apache Camel? - apache-camel

I need to browse messages from an active mq using Camel route without consuming the messages.
The messages in the JMS queue are to be read(only browsed and not consumed) and moved to a database while ensuring that the original queue remains intact.
public class CamelStarter {
private static CamelContext camelContext;
public static void main(String[] args) throws Exception {
camelContext = new DefaultCamelContext();
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnectionFactory.DEFAULT_BROKER_URL);
camelContext.addComponent("jms", JmsComponent.jmsComponent(connectionFactory));
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("jms:queue:testQueue").to("browse:orderReceived") .to("jms:queue:testQueue1");
}
}
);
camelContext.start();
Thread.sleep(1000);
inspectReceivedOrders();
camelContext.stop();
}
public static void inspectReceivedOrders() {
BrowsableEndpoint browse = camelContext.getEndpoint("browse:orderReceived", BrowsableEndpoint.class);
List<Exchange> exchanges = browse.getExchanges();
System.out.println("Browsing queue: "+ browse.getEndpointUri() + " size: " + exchanges.size());
for (Exchange exchange : exchanges) {
String payload = exchange.getIn().getBody(String.class);
String msgId = exchange.getIn().getHeader("JMSMessageID", String.class);
System.out.println(msgId + "=" +payload);
}

As far as I know, not possible in Camel to read (without consuming !) JMS messages...
The only workaround I found (in a JEE app) was to define a startup EJB with a timer, holding a QueueBrowser, and delegating the msg processing to a Camel route:
#Singleton
#Startup
public class MyQueueBrowser {
private TimerService timerService;
#Resource(mappedName="java:/jms/queue/com.company.myqueue")
private Queue sourceQueue;
#Inject
#JMSConnectionFactory("java:/ConnectionFactory")
private JMSContext jmsContext;
#Inject
#Uri("direct:readMessage")
private ProducerTemplate camelEndpoint;
#PostConstruct
private void init() {
TimerConfig timerConfig = new TimerConfig(null, false);
ScheduleExpression se = new ScheduleExpression().hour("*").minute("*/"+frequencyInMin);
timerService.createCalendarTimer(se, timerConfig);
}
#Timeout
public void scheduledExecution(Timer timer) throws Exception {
QueueBrowser browser = null;
try {
browser = jmsContext.createBrowser(sourceQueue);
Enumeration<Message> msgs = browser.getEnumeration();
while ( msgs.hasMoreElements() ) {
Message jmsMsg = msgs.nextElement();
// + here: read body and/or properties of jmsMsg
camelEndpoint.sendBodyAndHeader(body, myHeaderName, myHeaderValue);
}
} catch (JMSRuntimeException jmsException) {
...
} finally {
browser.close();
}
}
}

Apache camel browse component is exactly designed for that. Check here for the documentation.
Can't say more since you have not provided any other information.
Let's asssume you have a route like this
from("activemq:somequeue).to("bean:someBean")
or
from("activemq:somequeue).process(exchange -> {})
All you got to do it put a browse endpoint in between like this
from("activemq:somequeue).to("browse:someHandler").to("bean:someBean")
Then write a class like this
#Component
public class BrowseQueue {
#Autowired
CamelContext camelContext;
public void inspect() {
BrowsableEndpoint browse = camelContext.getEndpoint("browse:someHandler", BrowsableEndpoint.class);
List<Exchange> exchanges = browse.getExchanges();
for (Exchange exchange : exchanges) {
......
}
}
}

Related

Mock not found in the registry for Camel route test

I am trying to test a Camel route (polling messages from an SQS queue) containing
.bean("messageParserProcessor")
where messageParserProcessor is a Processor.
The test:
public class SomeTest extends CamelTestSupport {
private final String queueName = ...;
private final String producerTemplateUri = "aws-sqs://" + queueName + ...;
private static final String MESSAGE_PARSER_PROCESSOR_MOCK_ENDPOINT = "mock:messageParserProcessor";
#EndpointInject(uri = MESSAGE_PARSER_PROCESSOR_MOCK_ENDPOINT)
protected MockEndpoint messageParserProcessor;
#Override
public boolean isUseAdviceWith() {
return true;
}
#Before
public void setUpContext() throws Exception {
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
interceptSendToEndpoint("bean:messageParserProcessor")
.skipSendToOriginalEndpoint()
.process(MESSAGE_PARSER_PROCESSOR_MOCK_ENDPOINT);
}
});
}
#Test
public void testParser() throws Exception {
context.start();
String expectedBody = "test";
messageParserProcessor.expectedBodiesReceived(expectedBody);
ProducerTemplate template = context.createProducerTemplate();
template.sendBody(producerTemplateUri, expectedBody);
messageParserProcessor.assertIsSatisfied();
context.stop();
}
}
When I run the test I get this error:
org.apache.camel.FailedToCreateRouteException:
Failed to create route route1 at:
>>> InterceptSendToEndpoint[bean:messageParserProcessor -> [process[ref:mock:messageParserProcessor]]] <<< in route: Route(route1)[[From[aws-sqs://xxx...
because of No bean could be found in the registry for: mock:messageParserProcessor of type: org.apache.camel.Processor
Same error if I replace interceptSendToEndpoint(...) with mockEndpointsAndSkip("bean:messageParserProcessor")
The test can be executed (but obviously doesn't pass) when I don't use a mock:
interceptSendToEndpoint("bean:messageParserProcessor")
.skipSendToOriginalEndpoint()
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {}
});
So the problem is the mock that is not found, what is wrong in the way I create it?
So I found a workaround to retrieve mocks from the registry:
interceptSendToEndpoint("bean:messageParserProcessor")
.skipSendToOriginalEndpoint()
.bean(getMockEndpoint(MESSAGE_PARSER_PROCESSOR_MOCK_ENDPOINT));
// Instead of
// .process(MESSAGE_PARSER_PROCESSOR_MOCK_ENDPOINT);
But I still don't understand why using .process("mock:someBean") doesn't work...

Akka + Camel + Context-Provider

Dear Camel/Akka/Netty Masters!
I've created UntypedConsumerActor which consumes tcp connection:
public class TcpEndpoint extends UntypedConsumerActor {
private static final Logger log = LoggerFactory.getLogger(TcpEndpoint.class);
public static Props props = Props.create(TcpEndpoint.class);
#Override
public String getEndpointUri() {
return "netty4:tcp://localhost:8000?decoders=#fdDecoder,#fdHandler";
}
#Override
public void onReceive(Object message) throws Throwable {
log.error("onReceived");
}
}
In case to configure decoders for netty component, I've created ContextProvider:
public class FDCamelContext implements ContextProvider {
public DefaultCamelContext getContext(ExtendedActorSystem system) {
JndiRegistry registry = new JndiRegistry();
registry.bind("fdDecoder", new FDDecoder());
registry.bind("fdHandler", new FDHandler());
DefaultCamelContext context = new DefaultCamelContext(registry);
return context;
}
}
Now, when I send message there is no call on onReceive method. Why? When I set DefaultContextProvider and configure netty to consumes textlines everything works as expected.
Ok, I found problem. Maybe it helps someone:
It is necesarry to fire channelRead event:
ctx.fireChannelRead(msg);

Doing bean inject in camel test

I have a camel app which look something like below which has a route like below:-
from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders");
The entry point of the code look something like below:
public class OrderMainApp {
public static void main(String... args) throws Exception {
OrderMainApp orderMainApp = new OrderMainApp();
DefaultCamelContext camelContext = new DefaultCamelContext();
ProducerTemplate producer = camelContext.createProducerTemplate();
camelContext.setRegistry(orderMainApp.createRegistry(producer));
camelContext.addRoutes(new OrderRouteBuilder(producer));
camelContext.start();
}
protected JndiRegistry createRegistry(ProducerTemplate producer) throws Exception {
JndiRegistry jndi = new JndiRegistry();
OrderHelper orderHelper = new OrderHelper();
orderHelper.setProducer(producer);
jndi.bind("orderHelper", orderHelper);
return jndi;
}
}
In OrderRouteBuilder configure has routes like below:-
//processor is a custom JSONProcessor extending Processor
from("jetty:http://localhost:8888/orchestratorservice").process(processor);
from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders");
My goal is to test the response I receive from bean:orderHelper?method=getMarketplaceOrders when I place a request on direct:getMarketplaceOrders
orderHelper.getMarketplaceOrders looks like below:-
public OrderResponse getMarketplaceOrders(GetMarketplaceOrdersRequest requestParam) throws Exception
My test class look something like below:-
public class OrderMainAppTest extends CamelTestSupport {
#Produce(uri = "direct:getMarketplaceOrders")
protected ProducerTemplate template;
#EndpointInject(uri = "bean:orderHelper?method=getMarketplaceOrders")
protected MockEndpoint resultEndpoint;
#Test
public void testSendMatchingMessage() throws Exception {
String expectedBody = "<matched/>";
template.sendBody("{\"fromDateTime\": \"2016-01-11 10:12:13\"}");
resultEndpoint.expectedBodiesReceived(expectedBody);
resultEndpoint.assertIsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders");
}
};
}
}
Whenever I am running the test I am getting the below exception:-
java.lang.IllegalArgumentException: Invalid type: org.apache.camel.component.mock.MockEndpoint which cannot be injected via #EndpointInject/#Produce for: Endpoint[bean://orderHelper?method=getMarketplaceOrders]
I am guessing this is because I am not able to pass on OrderHelper to the camel test context. Can some one let me know how can I inject the bean in the mock result end point?
EDIT:-
I tried modifying my test class as follows:-
public class OrderMainAppTest extends CamelTestSupport {
protected OrderHelper orderHelper = new OrderHelper();
#Produce(uri = "direct:getMarketplaceOrders")
protected ProducerTemplate template;
#EndpointInject(uri = "mock:intercepted")
MockEndpoint mockEndpoint;
#Before
public void preSetup() throws Exception {
orderHelper.setProducer(template);
};
#Test
public void testSendMatchingMessage() throws Exception {
GetMarketplaceOrdersRequest request = new GetMarketplaceOrdersRequest();
request.setFromDateTime("2016-01-11 10:12:13");
request.setApikey("secret_key");
request.setMethod("getMarketplaceOrders");
request.setLimit(10);
request.setOffset(2);
template.sendBody(request);
mockEndpoint.expectedBodiesReceived("{\"success\":\"false\"");
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
interceptSendToEndpoint("bean:orderHelper?method=getMarketplaceOrders")
.to("mock:intercepted"); from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders");
}
};
}
#Override
protected JndiRegistry createRegistry() throws Exception {
return getRegistry();
}
protected JndiRegistry getRegistry() {
JndiRegistry jndi = new JndiRegistry();
jndi.bind("orderHelper", orderHelper);
return jndi;
}
}
The above code is making the request correctly and is flowing through my app correctly. But I am not able to intercept the response of orderHelper.getMarketplaceOrders. The above code is intercepting only the request. I tried changing to template.requestBody(request). But still no luck.
This error means you can't inject a bean: endpoint into a MockEndpoint.
If you want to "intercept" the call into your OrderHelper, you can use interceptSendToEndpoint in your route :
#EndpointInject(uri = "mock:intercepted")
MockEndpoint mockEndpoint;
...
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
interceptSendToEndpoint("bean:orderHelper?method=getMarketplaceOrders")
.to("mock:intercepted");
from("direct:getMarketplaceOrders")
.to("bean:orderHelper?method=getMarketplaceOrders");
}
};
See : http://camel.apache.org/intercept.html
By updating my createRouteBuilder as shown below. I am able to intercept the response and send it to a mock endpoint where I can do the assertion.
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders").onCompletion()
.to("mock:intercepted");
}
};
}

Bidirectional communication from Camel to Vertx socket Server

I am trying to use Camel NettyComponent to communicate with a SocketServer written in Vert.x.
This is my server code:
public class NettyExampleServer {
public final Vertx vertx;
public static final Logger logger = LoggerFactory.getLogger(NettyExampleServer.class);
public static int LISTENING_PORT = 15692;
public NettyExampleServer(Vertx vertx) {
this.vertx = vertx;
}
private NetServer netServer;
private List<String> remoteAddresses = new CopyOnWriteArrayList<String>();
private final AtomicInteger disconnections = new AtomicInteger();
public int getDisconnections(){
return disconnections.get();
}
public List<String> getRemoteAddresses(){
return Collections.unmodifiableList(remoteAddresses);
}
public void run(){
netServer = vertx.createNetServer();
netServer.connectHandler(new Handler<NetSocket>() {
#Override
public void handle(final NetSocket socket) {
remoteAddresses.add(socket.remoteAddress().toString());
socket.closeHandler(new Handler<Void>() {
#Override
public void handle(Void event) {
disconnections.incrementAndGet();
}
});
socket.dataHandler(new Handler<Buffer>() {
#Override
public void handle(Buffer event) {
logger.info("I received {}",event);
socket.write("I am answering");
}
});
}
});
netServer.listen(LISTENING_PORT);
}
public void stop(){
netServer.close();
}
}
I tried to build a Route like the following:
public class NettyRouteBuilder extends RouteBuilder {
public static final String PRODUCER_BUS_NAME = "producerBus";
public static final String CONSUMER_BUS_NAME = "receiverBus";
private Processor processor = new Processor(){
#Override
public void process(Exchange exchange) throws Exception {
exchange.setPattern(ExchangePattern.InOut);
}
};
#Override
public void configure() throws Exception {
from("vertx:" + PRODUCER_BUS_NAME).process(processor).to("netty:tcp://localhost:"+ NettyExampleServer.LISTENING_PORT + "?textline=true&lazyChannelCreation=true&option.child.keepAlive=true").to("vertx:"+CONSUMER_BUS_NAME);
}
}
My tests shows that:
If I eliminate the processor on the route, the delivery succeed but there is no answer by the server
If I keep the processor, the data is delivered to the server but an exception raise because no data is received.
I have created a small project here: https://github.com/edmondo1984/netty-camel-vertx . How do I use Camel Netty Component to create a bidirectional route ?
To communicate Vertx and Camel the best tool is to use one of this:
Camel Vertex endpoint
Vertex Camel connector
You can find an example here
If you could or have another requeriments it is possible also to use a common connector like Netty on the both sides.

Stop/Remove Route Dynamically/Programmatically Doesn't Remove the Corresponding Thread

During the processing of an Exchange received from JMS I'm creating dynamically a route that fetches a file from FTP to the file system and when the batch is done I need to remove that same route. The following code fragment shows how I do this:
public void execute() {
try {
context.addRoutes(createFetchIndexRoute(routeId()));
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
private RouteBuilder createFetchIndexRoute(final String routeId) {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("ftp://" + getRemoteQuarterDirectory() +
"?fileName=" + location.getFileName() +
"&binary=true" +
"&localWorkDirectory=" + localWorkDirectory)
.to("file://" + getLocalQuarterDirectory())
.process(new Processor() {
RouteTerminator terminator;
#Override
public void process(Exchange exchange) throws Exception {
if (camelBatchComplete(exchange)) {
terminator = new RouteTerminator(routeId,
exchange.getContext());
terminator.start();
}
}
})
.routeId(routeId);
}
};
}
I'm Using a thread to stop a route from a route, which is an approach recommended in the Camel Documentation - How can I stop a route from a route
public class RouteTerminator extends Thread {
private String routeId;
private CamelContext camelContext;
public RouteTerminator(String routeId, CamelContext camelContext) {
this.routeId = routeId;
this.camelContext = camelContext;
}
#Override
public void run() {
try {
camelContext.stopRoute(routeId);
camelContext.removeRoute(routeId);
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
}
In result the route does stop. But what I see in the jconsole is that the thread that corresponds to the route isn't removed. Thus in time these abandoned threads just keep accumulating.
Is there a way to properly stop/remove a route dynamically/programmatically and also to release the route's thread, so that they don't accumulate through time?
This is fixed in the next Camel release 2.9.2 and 2.10. Fixed by this ticket:
https://issues.apache.org/jira/browse/CAMEL-5072

Resources