Loading a Service on startup of the ServiceMix 4.3 which consumes another service using ActiveMQ/Apache Camel - 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.

Related

Camel JMS component, how to get a callback on subscriptions?

I am developing a service where I need to subscribe to JMS Queues dynamically with Camel JMS 2.17.0.
So I create a listening route at runtime using a bean:
// Bean: createCustomListenerNow
String endpoint = "jms:queue:" + queueName +
"&testConnectionOnStartup=true&exceptionListener=#listenerBuilder";
// camelContext.addRoutes uses this RouteBuilder
#Override
public void configure() throws Exception {
from(endpoint)
.id(id)
.to("seda:processReply");
}
My actual broker is Websphere MQ and I may get an exception if my account doesn't have the right permissions. Unfortunately this happens after my route has been built.
My route goes like this:
<route>
<from uri="direct:mock" />
<to uri="bean:createCustomListenerNow" /> <!-- I want to block here until subscription is OK -->
<to uri="bean:doSomethingThatRequiresTheListenerToBeProperlyRunning" />
</route>
Is there a way to "block" until the JMS component creates a new connections and successfully subscribes? I didn't manage to find a callback or a simple way to get this information.
What I do now is registering a javax.jms.ExceptionListener and waiting for a JMSException. If the exception is not thrown before a timeout I suppose the subscription did succeed.
I don't think this approach is rock-solid, any suggestion is appreciated.
I cannot block until I receive a message on the listener because it can arrive hours later and I need to finish processing the current Exchange with a reply like "Setup of listener is ok" or "Setup of listener failed".

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.

Camel Blueprint Testing - how can I dynamically replace/proxy a remote service referenced in Blueprint in order to prevent use of the real service?

I have the following scenario:
I have an OSGI bundle that has a service reference defined in the blueprint XML that references an interface in a remote bundle, and a bean that uses one of the impl's methods to populate a Properties object.
Relevant snippet from Bundle #1's XML (the consumer):
...
<!-- other bean definitions, namespace stuff, etc -->
<!-- reference to the fetching service -->
<reference id="fetchingService" interface="company.path.to.fetching.bundle.FetchingService" />
<!-- bean to hold the actual Properties object: the getConfigProperties method is one of the overridden interface methods -->
<bean id="fetchedProperties" class="java.util.Properties" factory-ref="fetchingService" factory-method="getProperties" />
<camelContext id="contextThatNeedsProperties" xmlns="http://camel.apache.org/schema/blueprint">
<propertyPlaceholder id="properties" location="ref:fetchedProperties" />
...
<!-- the rest of the context stuff - routes and so on -->
</camelContext>
Remote Bundle's blueprint.xml:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:camel="http://camel.apache.org/schema/blueprint"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">
<cm:property-placeholder id="config-properties" persistent-id="company.path.configfetcher" />
<bean id="fetchingService" class="company.path.to.fetching.bundle.impl.FetchingServiceImpl" scope="singleton" init-method="createLoader" depends-on="config-properties">
<property name="environment" value="${environment}" />
<property name="pathToRestService" value="${restPath}" />
</bean>
<service ref="fetchingService" interface="company.path.to.fetching.bundle.FetchingService" />
<!-- END TESTING -->
</blueprint>
From Impl Class:
public synchronized Properties getProperties() {
if(!IS_RUNNING) {
// timer task that regularly calls the REST api to check for updates
timer.schedule(updateTimerTask, 0, pollInterval);
IS_RUNNING = true;
}
//Map<String, Properties> to return matching object if it's there
if(PROPERTIES_BY_KEY.containsKey(environment)) {
return PROPERTIES_BY_KEY.get(environment);
}
/* if nothing, return an empty Properties object - if this is the case, then whatever bundle is relying on these
* properties is going to fail and we'll see it in the logs
*/
return new Properties();
}
The issue:
I have a test class (extending CamelBlueprintTestSupport) and there are a lot of moving parts such that I can't really change the order of things. Unfortunately, the properties bean method gets called before the CamelContext is started. Not that big a deal because in the test environment there is no config file to read the necessary properties from so the retrieval fails and we get back an empty properties object [note: we're overriding the properties component with fakes since it's not that class being tested], but in a perfect world, I'd like to be able to do two things:
1) replace the service with a new Impl()
2) intercept calls to the getProperties method OR tie the bean to the new service so that the calls return the properties from the fake impl
Thoughts?
Edit #1:
Here's one of the things I'm doing as a workaround right now:
try {
ServiceReference sr = this.getBundleContext().getServiceReference(FetchingService.class);
if(sr != null) {
((FetchingServiceImpl)this.getBundleContext().getService(sr)).setEnvironment(env);
((FetchingServiceImpl)this.getBundleContext().getService(sr)).setPath(path);
}
} catch(Exception e) {
log.error("Error getting Fetching service: {}", e.getMessage());
}
The biggest problem here is that I have to wait until the createCamelContext is called for a BundleContext to exist; therefore, the getProperties call has already happened once. As I said, since in the testing environment no config for the FetchingService class exists to provide the environment and path strings, that first call will fail (resulting in an empty Properties object). The second time around, the code above has set the properties in the impl class and we're off to the races. This is not a question about something that isn't working. Rather, it is about a better, more elegant solution that can be applied in other scenarios.
Oh, and for clarification before anyone asks, the point of this service is so that we don't have to have a .cfg file for every OSGI bundle deployed to our Servicemix instance - this central service will go and fetch the configs that the other bundles need and the only .cfg file that need exist is for the Fetcher.
Other pertinent details:
Camel 2.13.2 - wish it was 2.14 because they've added more property-placeholder tools to that version that would probably make this easier
Servicemix - 5.3.1
Have you tried overriding CamelBlueprintTestSupport's addServicesOnStartup in your test (see "Adding services on startup" http://camel.apache.org/blueprint-testing.html)?
In your case something like:
#Override
protected void addServicesOnStartup(Map<String, KeyValueHolder<Object, Dictionary>> services) {
services.put(FetchingService.class.getName(), asService(new FetchServiceImpl(), null));
}

Do we need #InOnly annotation or <setExchangePattern pattern="InOnly"/> if we are using sendBody method of ProducerTemplate?

For POJO producing, it is mentioned in Camel docs that InOut is the default.
But, if we are using the sendBody() of ProducerTemplate, is there any need of setting #InOnly also as in the example below.
public class MyBean {
#Produce(uri = "direct:hello")
private ProducerTemplate producerTemplate;
#InOnly
void someInOnlyMethod()
{
...
producerTemplate.sendBody("mystr");
...
}
}
Similarly in the example below, if direct:hello gets a message from the above MyBean object configured without the #InOnly annotation, do we need the setExchangePattern element?
<route>
<from uri="direct:hello"/>
<setExchangePattern pattern="InOnly"/>
<to uri="mock:result"/>
</route>
1)
No when you use the producer template then the MEP is set accordingly to the method you use on the template. eg all send methods is InOnly, and all request methods is InOut. Though some methods has a pattern parameter where you can specify the MEP.
2)
No you often dont have to set/change the MEP. Though in situations where you send a message to a endpoint which supports both InOnly or InOut (and behaves differently) you may want to set the MEP explicit to your needs.
See about these EIPs for details
http://camel.apache.org/event-message.html
http://camel.apache.org/request-reply.html
for example a JMS endpoint can do InOnly (send only to a queue) or InOut (do request/reply over JMS) etc.

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.

Resources