ActiveMQ: How to Start/Stop Camel Routes on an ActiveMQ Slave
Join the DZone community and get the full member experience.
Join For FreeDo you have a business requirement in which you need ActiveMQ to deploy
your Camel routes but you have come to realize that in a Master/Slave
configuration the Camel Context is always started on the slave broker?
In this example I will show you how you can configure ActiveMQ to deploy
Camel routes as well as how to control when these routes should be
started. In this example we have a master broker with routes that start
when the broker is started. Additionally, we will have a slave broker
which will have routes that we only want to start when the slave becomes
the master.
I am currently using the apache-activemq-5.5.1-fuse-04-01, which is the latest release of ActiveMQ from FuseSource at the time of this writing. You can grab the binaries/source from the following link: apache-activemq-5.5.1-fuse-04-01
So you may be wondering how you might be able to accomplish this, right?
Well luckily, we can easily have something working with just a little
code and a little configuration.
Code
The code we need to implement is rather simple. We just need to create a class that implements the ActiveMQ Service interface. Below is the simple example I created to demonstrate how this works:
package com.fusesource.example; import org.apache.activemq.Service; import org.apache.camel.spring.SpringCamelContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Example used to start and stop the camel context using the ActiveMQ Service interface * */ public class CamelContextService implements Service { private final Logger LOG = LoggerFactory.getLogger(CamelContextService.class); SpringCamelContext camel; @Override public void start() throws Exception { try { camel.start(); } catch (Exception e) { LOG.error("Unable to start camel context: " + camel); e.printStackTrace(); } } @Override public void stop() throws Exception { try { camel.stop(); } catch (Exception e) { LOG.error("Unable to stop camel context: " + camel); e.printStackTrace(); } } public SpringCamelContext getCamel() { return camel; } public void setCamel(SpringCamelContext camel) { this.camel = camel; } }
The magic behind all this is in the Service interface. When this class
is registered as a service with the broker, the start method will be
called when the broker is fully initialized. Remember to copy the jar
file to the lib directory of the broker where you want this code to be
invoked.
Configuration
First let's have a look at how we deploy Camel routes from ActiveMQ's
broker configuration file. In the installation directory of the broker
you will find the conf directory which holds multiple examples of
different broker configuration files and such. One such file is called
camel.xml which defines a simple route. In our example we will import
this file in our broker's activemq.xml as follows which will start the
camel context and associated route.
<import resource="camel.xml"/>
This should be added just after the ending broker element where you will see that the configuration is already importing jetty.xml.
Now that we have added a Camel route to the master broker it can be started. Once started, you should see that the Camel Context was picked up and one route was started:
INFO | Apache Camel 2.8.0-fuse-04-01 (CamelContext: camel) is starting INFO | JMX enabled. Using ManagedManagementStrategy. INFO | Found 3 packages with 14 @Converter classes to load INFO | Loaded 163 core type converters (total 163 type converters) INFO | Loaded 3 @Converter classes INFO | Loaded additional 4 type converters (total 167 type converters) in 0.006 seconds WARN | Broker localhost not started so using broker1 instead INFO | Connector vm://localhost Started INFO | Route: route1 started and consuming from: Endpoint[activemq://example.A] INFO | Total 1 routes, of which 1 is started. INFO | Apache Camel 2.8.0-fuse-04-01 (CamelContext: camel) started in 0.532 seconds
So now that we have the master broker up and running with a Camel route
deployed we are going to do the same to the slave broker, but this time
we are going to edit the camel.xml slightly as follows to set the Camel
Context id to camelBackup and most importantly we are going add an
attribute autoStartup and set it to false to prevent the route from starting when the Camel Context is discovered:
<camelContext id="camelBackup" xmlns="http://camel.apache.org/schema/spring" autoStartup="false">
One last thing we need to do to the slave broker is configure the new
service we created from the above code. Copy the following
configuration to your slave broker's activemq.xml:
<services> <bean xmlns="http://www.springframework.org/schema/beans" class="com.fusesource.example.CamelContextService"> <property name="camel" ref="camelBackup"/> </bean> </services>
From the above configuration and sample code, you can see Spring is
being used to inject the camel property into our Service class.
Additionally, notice the ref has been set to camelBackup which is the
id we used for the CamelContext in the slave's camel.xml file.
Additionally, the broker has been configured as a slave so the broker
will only be fully initialized when the master broker fails. If you
want more information on configuring ActiveMQ Master/Slave brokers, take
at look at one of my early posts on Master/Slave Broker Configuration.
If you haven't done it all ready, copy the jar that was created from
packaging up the code from the example above to the slave broker's lib
directory.
Note: In a production system you might want to configure this on both
the master and slave broker to keep the configurations mirrored, as the
routes will be started on the master as well once it is fully
initialized. In this post I am keeping the required configuration to the
slave broker just to demonstrate the behavior.
Test Run
Now that we have the code and configuration done, let's give this a test run by starting up the slave broker.
INFO | Apache Camel 2.8.0-fuse-04-01 (CamelContext: camelBackup) is starting INFO | JMX enabled. Using ManagedManagementStrategy. INFO | Found 3 packages with 14 @Converter classes to load INFO | Loaded 163 core type converters (total 163 type converters) INFO | Loaded 3 @Converter classes INFO | Loaded additional 4 type converters (total 167 type converters) in 0.007 seconds INFO | Total 1 routes, of which 0 is started. INFO | Apache Camel 2.8.0-fuse-04-01 (CamelContext: camelBackup) started in 0.512 seconds
Looking at the output from the slave broker you can see the CamelContext
is still started, however the route is not (remember we
set autoStartup="false). Now, in the terminal where the master broker
is running, issue a kill to stop the broker.
If you have a look at the slave broker's output again, you can see the
connector was started, openwire in this case, and the Camel route is now
started.
ERROR | Network connection between vm://broker2#0 and tcp://localhost/127.0.0.1:61616 shutdown: null java.io.EOFException at java.io.DataInputStream.readInt(DataInputStream.java:375) at org.apache.activemq.openwire.OpenWireFormat.unmarshal(OpenWireFormat.java:275) at org.apache.activemq.transport.tcp.TcpTransport.readCommand(TcpTransport.java:222) at org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:214) at org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:197) at java.lang.Thread.run(Thread.java:680) WARN | Master Failed - starting all connectors INFO | Listening for connections at: tcp://macbookpro-251a.home:62616 INFO | Connector openwire Started INFO | Apache Camel 2.8.0-fuse-04-01 (CamelContext: camelBackup) is starting WARN | Broker localhost not started so using broker2 instead INFO | Connector vm://localhost Started INFO | Route: route1 started and consuming from: Endpoint[activemq://example.A] INFO | Total 1 routes, of which 1 is started. INFO | Apache Camel 2.8.0-fuse-04-01 (CamelContext: camelBackup) started in 0.036 seconds
Conclusion
That's all there is to it, and I think you would agree starting a route
from ActiveMQ slave is rather simple and easy to implement. I'd also
like to thank Hiram Chirino, an Apache ActiveMQ Founder, for pointing me in the direction of using the ActiveMQ Service Interface.
Published at DZone with permission of Jason Sherman, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments