Spring Boot Camel Test - apache-camel

I am writing a test for Camel using Spring boot.
Below is configuration on the test class
#RunWith(CamelSpringBootRunner.class)
#SpringBootApplication
#ComponentScan(basePackages = ["example.test"])
#UseAdviceWith
#BootstrapWith(SpringBootTestContextBootstrapper)
#DirtiesContext
class RouteTest {
private static final Logger LOGGER = LoggerFactory.getLogger(RouteTest.class)
#Autowired ModelCamelContext camelContext
#Test
void "flow"() {
camelContext.getRouteDefinition(route.routeId).adviceWith(camelContext, new AdviceWithRouteBuilder() {
#Override
void configure() throws Exception {
}
}
LOGGER.info("IN TEST: ******* Camel Status: "+camelContext.getStatus())
}
I expect camel should not be started. But when I run the test it is already started.
I noticed that CamelSpringBootRunner does start camel context in CamelSpringBootExecutionListener.
How do I force not to start the camel context.

In the latest version of camel there is an option for autoStartup of camel. You an achieve what you want by adding autoStartup option.
For example the route below is configured autoStartup=false to prevent Camel starting when Spring starts.
<camelContext id="myCamel" xmlns="http://camel.apache.org/schema/spring" autoStartup="false">
<route>
<from uri="direct:start"/>
<to uri="mock:result"/>
</route>
</camelContext>
You can manually start Camel later by invoking its start method as shown below:
ApplicationContext ac = ...
SpringCamelContext camel = (SpringCamelContext) ac.getBean("myCamel");
// now start Camel manually
camel.start();

If you are using older version of camel then autoStartup option will not work try using shouldStartContext instead.
Sometimes starting camel after setting shouldStartContext doesn't work so I have put the work around in below example. Try this :
setting shouldStartContext manually before starting the context from the code:
((SpringCamelContext)camelContext).setShouldStartContext(true);
camelContext.start();
Example context:
<camel:camelContext id="ids.camel.context" shouldStartContext="false">
<!-- Queue endpoints. -->
<camel:endpoint id="defaultInQueue" uri="jms:queue:${default.in.queue.name}"/>
<camel:endpoint id="defaultOutQueue" uri="jms:queue:${default.out.queue.name}"/>
<!-- Route to send messages to IDS -->
<camel:route id="out" trace="true">
<camel:from uri="direct:sender"/>
<!-- Do not expect answer (fire and forget) -->
<camel:setExchangePattern pattern="InOnly"/>
<camel:to ref="defaultOutQueue"/>
</camel:route>
<!-- Route to receive -->
<camel:route id ="in" trace="true">
<camel:from ref="defaultInQueue"/>
<camel:to uri="bean:defaultTextAdapter?method=onMessage"/>
</camel:route>
</camel:camelContext>

Using Camel 2.20.1 solved the issue

Related

Apache Camel onExceptionOccurred component not redelivering as mentioned times

I have an Apache Camel route which invokes restlet component. To better understand what is happening at runtime I also keep the execution state in a database (number of retries, last execution state). I would like to use the redelivery mechanism from the exception handler but which performs some processing on every failure to update my database record.
<bean class="com.tcl.ExceptionOccurredRefProcessor" id="exProc" />
<camelContext errorHandlerRef="eh">
<errorHandler id="eh" onExceptionOccurredRef="exProc"> <redeliveryPolicy
maximumRedeliveries="3" maximumRedeliveryDelay="2000"
retryAttemptedLogLevel="WARN" />
</errorHandler>
<to uri="-------"/> //Invoke rest service
<onException id="_onException1" onExceptionOccurredRef="exProc" useOriginalMessage="true">
<exception>java.lang.Exception</exception>
<handled>
<simple>true</simple>
</handled>
...........
</onException>
</camelContext>
For this I tried to use onExceptionOccurred component with redelivery component and referred on camelContext as in above code but the message got delivered only once and stopped the execution.
Any suggestions please?
First of all: the code you pasted is a bit confusing because there is no Camel route, just a plain to element that cannot be alone in the Camel context.
However, I guess your problem is a "hidden" Camel handling. As documented in the Camel Docs of the Exception Clause, the onException clause overrides the maximumRedeliveries to 0 by default unless you explicitly set the option.
<onException>
<exception>java.lang.Exception</exception> // max. redelivery = 0
</onException>
<onException>
<exception>java.lang.Exception</exception>
<redeliveryPolicy maximumRedeliveries="3"/> // max. redelivery = 3
</onException>
I an not sure about the other redelivery options like maximumRedeliveryDelay, but at least the maximumRedeliveries is overridden if not explicitly set.

Camel ActiveMQ how to destroy/purge a virtual topic queue without consumers

My application consists of many OSGi bundles running inside JBoss Fuse 6.2.1. Each bundle has a Camel route consuming from an ActiveMQ endpoint. Data is exchanged using VirtualTopics.
ProducerBundle publishes to topic VirtualTopic.MyTopic
ConsumerBundle A consumes from queue Consumer.A.VirtualTopic.MyTopic
ConsumerBundle B consumes from queue Consumer.B.VirtualTopic.MyTopic
ConsumerBundle C consumes from queue Consumer.C.VirtualTopic.MyTopic
At a certain moment in time Consumer C is closed, its bundle uninstalled and will never come back. Howewer, messages are still enqueued into Consumer.C.VirtualTopic.MyTopic queue.
How do I destroy such queue?
ActiveMQ pauses the Producer when the queue fills up, and I cannot set a small time to live on the message as other consumers may take a while to process each message. I cannot modify the VirtualTopic structure. I have full access to
ActiveMQ configuration and Camel routes.
Are there any other options to handle the situation?
<!-- producer route -->
<route id="ProducerRoute"/>
<from uri="direct:trigger"/>
<to uri="activemq:topic:VirtualTopic.MyTopic"/>
</route>
<!-- each consumer route -->
<route id="ConsumerARoute">
<from uri="activemq:Consumer.A.VirtualTopic.MyTopic"/>
<to uri="bean:myProcessor"/>
</route>
Look at the Apache ActiveMQ documentation how you can delete inactive queues/topics such as: http://activemq.apache.org/delete-inactive-destinations.html
I went for the aggressive solution: I hook into OSGi bundle lifecycle, when it is stopped I use JMX MBeanServer to destroy the now unneeded queues.
Since my bundle is managed using blueprint, I opted for a bean with a destroy method.
Here's an example implementation:
My bean
package org.darugna.osgi;
import javax.management.MBeanServer;
import javax.management.ObjectName;
public class QueueDestroyer {
private static final String[] QUEUES_TO_DESTROY = {
"Consumer.A.VirtualTopic.MyTopic"
};
private MBeanServer mBeanServer;
public void setMbeanServer(MBeanServer mBeanServer) {
this.mBeanServer = mBeanServer;
}
public void destroy() throws Exception {
ObjectName brokerName = new ObjectName("org.apache.activemq:type=Broker,brokerName=amq");
for (String queueName : QUEUES_TO_DESTROY) {
Object returnValue = mBeanServer.invoke(brokerName,
"removeQueue",
new Object[]{queueName},
new String[]{String.class.getName()});
}
}
}
Blueprint.xml
<blueprint>
<reference id="mbeanServer" interface="javax.management.MBeanServer"
availability="mandatory"/>
<bean id="queueDestroyer" class="org.darugna.osgi.QueueDestroyer"
destroy-method="destroy">
<property name="mbeanServer" ref="mbeanServer"/>
</bean>
<camelContext>
<route>
<from uri="activemq:Consumer.A.VirtualTopic.MyTopic"/>
<to uri="bean:myProcessor"/>
</route>
<camelContext>
</blueprint>
I had a similar situation, but I couldn't use the Claus's suggestion because in my broker there are other queues that have no consumer and I doesn't want to delete them.
In my case I'm running a JBoss Fuse 6.1.0 with fabric (I think that is the same with newer version of Fuse): I just removed the consumer (in my case I just removed the profile with the consumer) and after that I deleted the queue with the delete button in the hawtio console.

How to properly handle Exceptions in Apache Camel using OnException

I am new to Apache Camel. I am able to send JMS message from one queue to another queue. I would like to know how to handle the exception. I learned onException, But it is not working for me.
I changed my jms queue to jms1. However, when I execute the code i get an exception.
My expectation is whenever I get exception my bean class should be invoked, but it is not.
Exception:
org.apache.camel.RuntimeCamelException:
org.apache.camel.FailedToCreateRouteException: Failed to create route
route1 at: >>> To[jms1:queue:FinalQSource] <<< in route:
Route[[From[jms:queue:testQSource]] -> [OnException[[class j...
because of Failed to resolve endpoint: jms1://queue:FinalQSource due
to: No component found with scheme: jms1
Code
<camelContext id="jmsContext" xmlns="http://camel.apache.org/schema/spring">
<onException>
<exception>org.apache.camel.RuntimeCamelException</exception>
<exception>org.apache.camel.ResolveEndpointFailedException</exception>
<!-- <handled><constant>true</constant></handled> -->
<bean ref="exceptionListener" method="orderFailed" />
</onException>
<route>
<from uri="jms:queue:testQSource" />
<to uri="jms1:queue:FinalQSource" /><!--
</route>
</camelContext>
This will not work as the exception is being thrown by camel when it tries to parse your route. The onException block will only catch exceptions that are thrown during execution of your route.
To test exception handling use the proper camel testing guide - http://camel.apache.org/testing.html
I would recommend mocking one of your endpoints to return an exception, example here - https://github.com/christian-posta/camel-sandbox/blob/master/one-off/src/test/java/posta/TestMockExceptions.java
MockEndpoint mockException = MockEndpoint.resolve(context, "mock:exception");
mockException.whenAnyExchangeReceived(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println("i got here...");
throw new RuntimeException("fail!");
}
});

Apache Camel Endpoint injection to direct route "No consumers available on endpoint"

I want to use Camel to take a message from ActiveMQ and then, based on the message contents (a protobuf), send one or more messages to Twitter. I've written a bean that is called from within a route and which uses injection to send multiple messages to a "direct:xyz" endpoint.
However, Camel is complaining at runtime that:
2012-11-16 09:56:33,376 | WARN | ication.twitter] | DirectProducer | 160 - org.apache.camel.camel-core - 2.10.2 | No consumers available on endpoint: Endpoint[direct://twitter] to process: Exchange[Message: hello world]
If I instead inject directly to the Twitter endpoint from within the bean, it works fine. However, in order to ease testing, simplify configuration etc, I'd like to keep the actual Twitter config separate, hence wanting to send to a separate route.
The camel context config looks like:-
<camelContext id="NotificationTwitter"
trace="false" xmlns="http://camel.apache.org/schema/blueprint">
<dataFormats>
<protobuf id="notificationProto" instanceClass="org.abc.schemas.protobuf.NotificationDef$NotificationMsg" />
</dataFormats>
<route id="TwitterPreparation">
<from uri="activemq:notification.twitter" />
<unmarshal ref="notificationProto" />
<log logName="abc" loggingLevel="INFO"
message="Twitter request received: ${body}" />
<bean ref="NotificationTweeter" method="createTweets" />
</route>
<route id="Twitter">
<from uri="direct:twitter" />
<log logName="abc" loggingLevel="INFO"
message="Tweeting: ${body}" />
<to uri="twitter://timeline/user?consumerKey=itsasecret&consumerSecret=itsasecret&accessToken=itsasecret&accessTokenSecret=itsasecret" />
</route>
</camelContext>
The bean looks like:-
public class NotificationTweeter {
#EndpointInject(uri = "direct:twitter")
private ProducerTemplate producerTemplate;
public void createTweets(NotificationMsg notification) {
String tweet = notification.getMessageDetail().getTitle();
try {
// only send tweets where the notification message contains the Twitter mechanism
for (MechanismMsg mechanism : notification.getMechanismList()) {
if (mechanism.getType() == MechanismTypeEnum.TWITTER) {
// Cycle round the recipients
for (RecipientMsg recipient : mechanism.getRecipientList()) {
tweet = "#" + recipient.getIdentifier() + " " + tweet;
producerTemplate.sendBody(tweet);
}
// TODO exceptions if no recipients found, etc
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
I've had this problem in other routes (it's certainly not related to the Twitter feature) but have just worked around it. This time, however, I'd like to actually understand what the issue is! Any help gratefully received, thanks.
According to your setup, it might also depend on the CamelContext you have picked up. I got the same error message because I was sending messages on a route that existed in another CamelContext than the one I actually was using.
(Although the previous answer was already accepted, this might be the working solution for other people searching for that error message.)
It sounds like a problem with the startup ordering of your routes. See more detail here http://camel.apache.org/configuring-route-startup-ordering-and-autostartup.html
You can configure the "direct" route to start before the other route, then that issue should be resolved.
For others coming here, this error can also be caused by an OSGI error for a dependency that has not been deployed.
A bit late to the party but this error happened to me when I had two separate blueprint files, one for normal running and one for test. In my test I was referring to the test blueprint but noticed that the normal one was also automatically started which caused errors.
In the documentation http://camel.apache.org/blueprint-testing.html it says you can disable certain bundles from starting up. That helped me in my case.
This can also be caused by having a . in the route name. Replace my.Route.Name with myRouteName fixed the issue for me.

Loading a Service on startup of the ServiceMix 4.3 which consumes another service using ActiveMQ/Apache Camel

We are trying with ServiceA calling ServiceB as soon as bundle loads during the SericeMix startup. Service2 having activemq endpoints we need to invoke a method of that particular service. I tried by spring init-method attribute in the bean tag which helps in auto trigger a method in ServiceA in that method I am calling the serviceB’s method. I am getting Exception like No Consumer available for the endpoint. I assume that as soon as the Service1 is up it is not getting the instance of the service2 which needs to get initialized using #Produce annotation activemq endpoint. The same services work fine in the other normal scenarios.
Exception:
Caused by: org.apache.camel.CamelExchangeException: No consumers available on endpoint: Endpoint[direct://ServiceB]. Exchange[Message: BeanInvocation public java.lang.String java.lang.Object.toString() with null]]
at org.apache.camel.component.direct.DirectProducer.process(DirectProducer.java:46)
at org.apache.camel.component.bean.CamelInvocationHandler.invoke(CamelInvocationHandler.java:64)
... 35 more
I am copy pasting the Code Block for your reference.
public class ServiceA{
#Produce(uri = "direct:ServiceB") //Active MQ endpoint
private ServiceB serviceB;
public void start()
{
Object obj = serviceB.getData(); }
. . .
.....
}
}
**bundle-context.xml**
//Changes for method to auto trigger during spring bean load
<bean id="serviceA" class="com.test.serviceA" init-method="start">
</bean>
**bundle-context-camel.xml**
<osgi:camelContext id="ServiceA"
xmlns="http://camel.apache.org/schema/spring">
<template id="producerTemplate" />
<!-- These routes are outbound to other services -->
<route>
<from uri="ServiceB" />
<bean ref="enrichOutboundExchangeRef" />
<to uri="activemq:ServiceB?transferException=true" />
</route>
..............
</osgi:camelContext>
Or is their anyother way if i need to achieve this requirement? where i can load a service(consumes other services) automatically during the servicemix bootup.
You can use seda instead of direct as it's queue based and thus consumers can come and go.
Also try using springs depends-on attribute
<bean id="serviceA" depends-on="myCamel" .../>
<osgi:camelContext id="myCamel" ...>
We tried the above approach but we still we are getting exceptions, we resolved it by adding a listener to the onCamelContextStarted() during th init of the serviceA.
Thanks
Ravi
If you are getting "no consumers available on endpoint", it means that messages are being routed to an endpoint that hasn't been initialized. I recommend decoupling the services using a JMS queue between them. That way serviceA can put messages in the queue (independent of serviceB's availability) and serviceB can then act as a polling consumer against that queue whenever its ready.

Resources