blob: 1af52cfce5aeab79a95ffdd686c142371ad82a57 [file] [log] [blame]
<div class="wiki-content maincontent"><p>ActiveMQ supports advisory messages which allows you to watch the system using regular CMS messages. A few things that you can currently do with advisory messages are:</p>
<ul><li>See consumers, producers and connections starting and stopping</li><li>See temporary destinations being created and destroyed</li><li>Get notified messages expiring on topics and queues</li><li>Observe brokers sending messages to destinations with no consumers.</li><li>See connections starting and stopping</li></ul>
<p>Advisory messages can be thought as some kind of administrative channel where you receive information regarding what is happening on your JMS provider along with what's happening with producers, consumers and destinations. To learn more about the broker's advisory message support see this <a shape="rect" href="../advisory-message.xml">article</a>.</p>
<h3 id="HandlingAdvisoryMessages-BeforeYouBegin"><strong>Before You Begin</strong></h3>
<p>This tutorial assumes that the reader has a working knowledge of the CMS API and knows how to build basic applications using the ActiveMQ-CPP Library. If you aren't sure how to use the CMS API then you should first read the <a shape="rect" href="cms-api-overview.xml">CMS API Overview</a>. The tutorial is written against the ActiveMQ-CPP 3.0 release API and while it is possible to handle advisory messages using the older ActiveMQ-CPP 2.x clients there may be some code differences which are not shown in this article. </p>
<h3 id="HandlingAdvisoryMessages-SubscribingtoAdvisoryTopics"><strong>Subscribing to Advisory Topics</strong></h3>
<p>To receive advisory message's you first need to subscribe to the Topic that supplies the advisory message you want. You subscribe to these Topics like you would any other destination, the trick is to use the correct name for the Topic that you want to use. Lets first take a look at a couple of the types available for use (this is not a complete set).</p>
<div class="table-wrap"><table class="confluenceTable"><tbody><tr><th colspan="1" rowspan="1" class="confluenceTh"><p>Advisory Topics</p></th><th colspan="1" rowspan="1" class="confluenceTh"><p>Description</p></th><th colspan="1" rowspan="1" class="confluenceTh"><p>properties</p></th><th colspan="1" rowspan="1" class="confluenceTh"><p>Data Structure</p></th></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"><p> ActiveMQ.Advisory.Connection </p></td><td colspan="1" rowspan="1" class="confluenceTd"><p> Connection start &amp; stop messages </p></td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"><p> ActiveMQ.Advisory.Producer.Queue </p></td><td colspan="1" rowspan="1" class="confluenceTd"><p> Producer start &amp; stop messages on a Queue </p></td><td colspan="1" rowspan="1" class="confluenceTd"><p>String='producerCount' - the number of producers</p></td><td colspan="1" rowspan="1" class="confluenceTd"><p> ProducerInfo</p></td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"><p> ActiveMQ.Advisory.Producer.Topic </p></td><td colspan="1" rowspan="1" class="confluenceTd"><p> Producer start &amp; stop messages on a Topic </p></td><td colspan="1" rowspan="1" class="confluenceTd"><p>String='producerCount' - the number of producers</p></td><td colspan="1" rowspan="1" class="confluenceTd"><p>ProducerInfo</p></td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"><p> ActiveMQ.Advisory.Consumer.Queue </p></td><td colspan="1" rowspan="1" class="confluenceTd"><p> Consumer start &amp; stop messages on a Queue </p></td><td colspan="1" rowspan="1" class="confluenceTd"><p>String='consumerCount' - the number of Consumers</p></td><td colspan="1" rowspan="1" class="confluenceTd"><p>ConsumerInfo</p></td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"><p> ActiveMQ.Advisory.Consumer.Topic </p></td><td colspan="1" rowspan="1" class="confluenceTd"><p> Consumer start &amp; stop messages on a Topic </p></td><td colspan="1" rowspan="1" class="confluenceTd"><p>String='consumerCount' - the number of Consumers</p></td><td colspan="1" rowspan="1" class="confluenceTd"><p>ConsumerInfo</p></td></tr></tbody></table></div>
<p>Now, looking at the above list lets pick one of the Topics and figure out how we would create the CMS Topic to subscribe to in order to receive the advisory messages. Using the Java client we could create the Destination using the utility class <strong>AdvisorySupport</strong> but for now ActiveMQ-CPP doesn't provide such a utility class, hopefully we will see this added in a future release (Contributions are of course always welcomed!).</p>
<p>If we had a Topic named TOPIC.FOO and we wanted to know when a producer subscribed to that Topic we would need to create a Topic object whose name is <strong>ActiveMQ.Advisory.Producer.Topic.TOPIC.FOO</strong> in order to receive the advisory message we are interested in. We know this because we can look at the above table and see that the <strong>ActiveMQ.Advisory.Producer.Topic</strong> is informed whenever a Producer starts or stops publishing messages on a Topic and we also know that our Topic is named TOPIC.FOO, so adding them together gets us the name of our Advisory Topic, we also know this because we peaked at the AdvisorySupport.java class, and no, that's not cheating. Below is a code snippet showing the creation of the Topic using a CMS Session:</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Creating an Advisory Topic for Producers on TOPIC.FOO</b></div><div class="codeContent panelContent pdl">
<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[
std::auto_ptr&lt;cms::Topic&gt; advisories( session-&gt;createTopic(
&quot;ActiveMQ.Advisory.Producer.Topic.TOPIC.FOO&quot; ) );
]]></script>
</div></div>
<p>Once we create the Topic for the advisory messages we want to listen for we just need to create a consumer to listen for them, the code snippet below demonstrates this:</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Creating an Consumer for an Advisory Topic.</b></div><div class="codeContent panelContent pdl">
<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[
std::auto_ptr&lt;cms::MessageConsumer&gt; consumer;
consumer.reset( session-&gt;createConsumer( advisories.get() ) );
consumer-&gt;setMessageListener( this );
]]></script>
</div></div>
<p>As you can see there is no difference between subscriptions to advisory topics that subscriptions to any other Destination in CMS. In the above example we registered as an asynchronous listener, you can also use the normal blocking <strong>receive</strong> method but we prefer this method.</p>
<h3 id="HandlingAdvisoryMessages-DealingwithincomingAdvisoryMessages"><strong>Dealing with incoming Advisory Messages</strong></h3>
<p>Every Advisory has the message type 'Advisory' and some predefined message properties, to check a CMS Message for this type you would call the <strong>getCMSType</strong> method on a Message object. In some cases you will know that the only message you are going to receive are advisory messages because your client only subscribes to advisory topics, other times you might have wired multiple MessageConsumers to the same MessageListener in which case you will have to check the message tppe. Once you know you are dealing with an advisory message then you can start to inspect it to determine what sort of message it is and extract the data that is meaningful to your application.</p>
<p>Many of the advisory messages store meaningful data in the Message properties, for instance the Consumer Start / Stop advisory message contains an element with the key <strong>consumerCount</strong> which is populated with the current number of active consumers on the Topic or Queue in question. Lets take a look at a code snippet now that checks the messages received in an onMessage callback to see if its an advisory message and acts on it if it is:</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Using the CMS Type to determine if a Message is an Advisory</b></div><div class="codeContent panelContent pdl">
<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[
void AdvisoryProducer::onMessage( const cms::Message* message ) {
if( message-&gt;getCMSType() == &quot;Advisory&quot; ) {
std::cout &lt;&lt; &quot;Received an Advisory Message!&quot; &lt;&lt; std::endl;
if( message-&gt;propertyExists( &quot;consumerCount&quot; ) ) {
std::string consumerCount = message-&gt;getStringProperty( &quot;consumerCount&quot; );
std::cout &lt;&lt; &quot;Number of Consumers = &quot; &lt;&lt; consumerCount &lt;&lt; std::endl;
// Do Something Meaningful here....
}
} else {
std::cout &lt;&lt; &quot;Received a Non-Advisory Message!&quot; &lt;&lt; std::endl;
}
}
]]></script>
</div></div>
<h3 id="HandlingAdvisoryMessages-CompleteExample:ProducerthatonlyProduceswhenthereisaConsumer"><strong>Complete Example: Producer that only Produces when there is a Consumer</strong></h3>
<p>Now that you've seen the basics of advisory message processing its time to show you a complete example that demonstrates what you can do with advisory messages. The following code shows a class header and source file that implements a basic CMS Producer that will send heart beat message to a Topic called <strong>HEART-BEAT-CHANNEL</strong> only when there are active consumers, otherwise it sits idle.</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>AdvisoryProducer Header file</b></div><div class="codeContent panelContent pdl">
<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[
#ifndef _ACTIVEMQCPP_EXAMPLES_ADVISORIES_ADVISORYPRODUCER_H_
#define _ACTIVEMQCPP_EXAMPLES_ADVISORIES_ADVISORYPRODUCER_H_
#include &lt;string&gt;
#include &lt;memory&gt;
#include &lt;cms/Session.h&gt;
#include &lt;cms/MessageProducer.h&gt;
#include &lt;cms/MessageConsumer.h&gt;
#include &lt;cms/MessageListener.h&gt;
#include &lt;decaf/lang/Runnable.h&gt;
#include &lt;decaf/util/concurrent/CountDownLatch.h&gt;
namespace activemqcpp {
namespace examples {
namespace advisories {
/**
* A sample Producer that will only send Messages on its Topic when it has
* received an advisory indicating that there is an active MessageConsumer
* on the Topic. Once another message comes in indicating that there is no
* longer a consumer then this Producer stops producing again.
*
* @since 3.0
*/
class AdvisoryProducer : public decaf::lang::Runnable,
public cms::MessageListener {
private:
volatile bool consumerOnline;
volatile bool shutdown;
decaf::util::concurrent::CountDownLatch shutdownLatch;
cms::Session* session;
std::auto_ptr&lt;cms::MessageConsumer&gt; consumer;
std::auto_ptr&lt;cms::MessageProducer&gt; producer;
public:
AdvisoryProducer( cms::Session* session );
virtual ~AdvisoryProducer();
/**
* Shut down the processing that occurs in the Run method.
*/
void stop();
/**
* Run the producer code.
*/
virtual void run();
/**
* Async Message callback.
*/
virtual void onMessage( const cms::Message* message );
};
}}}
#endif /* _ACTIVEMQCPP_EXAMPLES_ADVISORIES_ADVISORYPRODUCER_H_ */
]]></script>
</div></div>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>AdvisoryProducer Source file</b></div><div class="codeContent panelContent pdl">
<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[
#include &quot;AdvisoryProducer.h&quot;
#include &lt;cms/Topic.h&gt;
#include &lt;cms/Message.h&gt;
#include &lt;cms/TextMessage.h&gt;
#include &lt;decaf/lang/exceptions/NullPointerException.h&gt;
#include &lt;decaf/lang/Integer.h&gt;
using namespace std;
using namespace activemqcpp;
using namespace activemqcpp::examples;
using namespace activemqcpp::examples::advisories;
using namespace decaf;
using namespace decaf::lang;
using namespace decaf::lang::exceptions;
////////////////////////////////////////////////////////////////////////////////
AdvisoryProducer::AdvisoryProducer( cms::Session* session ) : shutdownLatch(1) {
if( session == NULL ) {
throw NullPointerException(
__FILE__, __LINE__, &quot;Session Object passed was Null.&quot; );
}
std::auto_ptr&lt;cms::Topic&gt; destination( session-&gt;createTopic(
&quot;HEART-BEAT-CHANNEL&quot; ) );
std::auto_ptr&lt;cms::Topic&gt; advisories( session-&gt;createTopic(
&quot;ActiveMQ.Advisory.Consumer.Topic.HEART-BEAT-CHANNEL&quot; ) );
this-&gt;shutdown = false;
this-&gt;consumerOnline = false;
this-&gt;session = session;
this-&gt;producer.reset( session-&gt;createProducer( destination.get() ) );
this-&gt;consumer.reset( session-&gt;createConsumer( advisories.get() ) );
this-&gt;consumer-&gt;setMessageListener( this );
}
////////////////////////////////////////////////////////////////////////////////
AdvisoryProducer::~AdvisoryProducer() {
}
////////////////////////////////////////////////////////////////////////////////
void AdvisoryProducer::stop() {
this-&gt;shutdown = true;
this-&gt;shutdownLatch.await( 3000 );
}
////////////////////////////////////////////////////////////////////////////////
void AdvisoryProducer::run() {
while( !this-&gt;shutdown ) {
if( this-&gt;consumerOnline ) {
std::auto_ptr&lt;cms::TextMessage&gt; message(
this-&gt;session-&gt;createTextMessage( &quot;Alive&quot; ) );
this-&gt;producer-&gt;send( message.get() );
Thread::sleep( 1000 );
}
}
this-&gt;shutdownLatch.countDown();
}
////////////////////////////////////////////////////////////////////////////////
void AdvisoryProducer::onMessage( const cms::Message* message ) {
if( message-&gt;getCMSType() == &quot;Advisory&quot; ) {
std::cout &lt;&lt; &quot;Received an Advisory Message!&quot; &lt;&lt; std::endl;
if( message-&gt;propertyExists( &quot;consumerCount&quot; ) ) {
std::string consumerCount = message-&gt;getStringProperty( &quot;consumerCount&quot; );
std::cout &lt;&lt; &quot;Number of Consumers = &quot; &lt;&lt; consumerCount &lt;&lt; std::endl;
this-&gt;consumerOnline = Integer::parseInt( consumerCount ) &gt; 0 ? true : false;
}
} else {
std::cout &lt;&lt; &quot;Received a Non-Advisory Message!&quot; &lt;&lt; std::endl;
}
}
]]></script>
</div></div>
<h3 id="HandlingAdvisoryMessages-*AdvancedTopic*DealingwithCommandObjectsinAdvisories">*<strong>Advanced Topic</strong>* Dealing with Command Objects in Advisories </h3>
<p>If you read the ActiveMQ Advisory Message <a shape="rect" href="../advisory-message.xml">article</a> mentioned at the start of this tutorial then you know that certain advisory messages can contain an embedded command object. If you didn't read that <a shape="rect" href="../advisory-message.xml">article</a> then this section is going to be very confusing, so go read it. We can access those command object in CMS with a little work which means we can take full advantage of the Advisory Message feature.</p>
<p>All Advisory Messages are sent as a basic ActiveMQMessage to your client. The underlying type hierarchy in ActiveMQ-CPP is the same as that of ActiveMQ so the names of the embedded command objects you saw in the Advisory <a shape="rect" href="../advisory-message.xml">article</a> are the same and they contain mostly the same information, although sometimes the information is encoded in a more C++ friendly or unfriendly way depending on your point of view. </p>
<p>To demonstrate how we can access the command objects lets try and create a client application that listens to the Broker for advisories that indicate that Temporary Destinations have either been created or destroyed. The Broker will publish advisory messages to the "ActiveMQ.Advisory.TempTopic" and "ActiveMQ.Advisory.TempQueue" Topics whenever the corresponding Temporary Destination is created or destroyed and the command object will be of type DestinationInfo. The DestinationInfo object contains a Destination object describing the Destination in question and an Operation Type value telling whether the command is a create or destroy command. First lets look at how we subscribe to this Advisory Topic:</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Subscribing to a Composite Advisory Topic</b></div><div class="codeContent panelContent pdl">
<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[
std::auto_ptr&lt;cms::Topic&gt; advisories( session-&gt;createTopic(
&quot;ActiveMQ.Advisory.TempTopic,ActiveMQ.Advisory.TempQueue&quot; ) );
std::auto_ptr&lt;cms::MessageConsumer&gt; consumer;
consumer.reset( session-&gt;createConsumer( advisories.get() ) );
consumer-&gt;setMessageListener( this );
]]></script>
</div></div>
<p>As you can see in the above code snippet we just create a new Topic object whose name is a composite of the two Topics we want to subscribe on, this will allow our single <strong>MessageConsumer</strong> instance to receive both Temporary Topic and Temporary Queue advisories. As before we also create a <strong>MessageConsumer</strong> and register our class' instance as the asynchronous listener. Now all that's left is to implement the <strong>onMessage</strong> method of the <strong>MessageListener</strong> interface, lets take a look at that code now:</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Processing an Advisory message with an embedded command object</b></div><div class="codeContent panelContent pdl">
<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[
////////////////////////////////////////////////////////////////////////////////
void TempDestinationAdvisoryConsumer::onMessage( const cms::Message* message ) {
if( message-&gt;getCMSType() == &quot;Advisory&quot; ) {
std::cout &lt;&lt; &quot;Received an Advisory Message!&quot; &lt;&lt; std::endl;
const ActiveMQMessage* amqMessage =
dynamic_cast&lt;const ActiveMQMessage*&gt;( message );
if( amqMessage != NULL &amp;&amp; amqMessage-&gt;getDataStructure() != NULL ) {
std::cout &lt;&lt; &quot;Advisory Message contains a Command Object!&quot; &lt;&lt; std::endl;
try {
Pointer&lt;DestinationInfo&gt; info =
amqMessage-&gt;getDataStructure().dynamicCast&lt;DestinationInfo&gt;();
unsigned char operationType = info-&gt;getOperationType();
if( operationType == ActiveMQConstants::DESTINATION_REMOVE_OPERATION ) {
std::cout &lt;&lt; &quot;Temporary Destination {&quot;
&lt;&lt; info-&gt;getDestination()-&gt;getPhysicalName()
&lt;&lt; &quot;} Removed.&quot;
&lt;&lt; std::endl;
} else if( operationType == ActiveMQConstants::DESTINATION_ADD_OPERATION ) {
std::cout &lt;&lt; &quot;Temporary Destination {&quot;
&lt;&lt; info-&gt;getDestination()-&gt;getPhysicalName()
&lt;&lt; &quot;} Added.&quot;
&lt;&lt; std::endl;
} else {
std::cout &lt;&lt; &quot;ERROR: I have no Idea what just happened!&quot;
&lt;&lt; std::endl;
}
} catch( ClassCastException&amp; ex ) {
std::cout &lt;&lt; &quot;ERROR: Expected the Command to be a DestinationInfo, &quot;
&lt;&lt; &quot;it wasn&#39;t so PANIC!!&quot;
&lt;&lt; std::endl;
}
}
} else {
std::cout &lt;&lt; &quot;Received a Non-Advisory Message!&quot; &lt;&lt; std::endl;
}
}
]]></script>
</div></div>
<p>Fortunately for use the code above looks more complicated than it really is, lets walk through it a bit more slowly now to understand what is going on:</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Getting to the ActiveMQMessage object</b></div><div class="codeContent panelContent pdl">
<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[
if( message-&gt;getCMSType() == &quot;Advisory&quot; ) {
std::cout &lt;&lt; &quot;Received an Advisory Message!&quot; &lt;&lt; std::endl;
const ActiveMQMessage* amqMessage =
dynamic_cast&lt;const ActiveMQMessage*&gt;( message );
... Other scary code comes next...
else {
std::cout &lt;&lt; &quot;Received a Non-Advisory Message!&quot; &lt;&lt; std::endl;
}
]]></script>
</div></div>
<p>The first thing we need to do is check that we received an advisory message, ActiveMQ encodes the Message Type as "Advisory" so that's easy enough. We don't technically need to do this here since our consumer only listens on an advisory Topic but its not a bad idea to check. Once we know its an advisory message we know that the message pointer should be of type ActiveMQMessage under that generic cms::Message disguise its wearing so we use a <strong>dynamic_cast</strong> to convert it. Now that we've converted to an ActiveMQMessage what's next, well lets take a look:</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Checking for an embedded command object</b></div><div class="codeContent panelContent pdl">
<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[
if( amqMessage != NULL &amp;&amp; amqMessage-&gt;getDataStructure() != NULL ) {
std::cout &lt;&lt; &quot;Advisory Message contains a Command Object!&quot; &lt;&lt; std::endl;
]]></script>
</div></div>
<p>Every ActiveMQMessage derived object has a method called <strong>getDataStructure</strong> which can be used for all sorts of useful things, here we are trying to see if there is a command object contained in this message, and you guessed it, the getDataStructure method will tell us if there is one. If there is then we can move onto checking for a DestinationInfo object:</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Getting to the DestinationInfo object</b></div><div class="codeContent panelContent pdl">
<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[
try{
Pointer&lt;DestinationInfo&gt; info =
amqMessage-&gt;getDataStructure().dynamicCast&lt;DestinationInfo&gt;();
unsigned char operationType = info-&gt;getOperationType();
if( operationType == ActiveMQConstants::DESTINATION_REMOVE_OPERATION ) {
std::cout &lt;&lt; &quot;Temporary Destination {&quot;
&lt;&lt; info-&gt;getDestination()-&gt;getPhysicalName()
&lt;&lt; &quot;} Removed.&quot;
&lt;&lt; std::endl;
} else if( operationType == ActiveMQConstants::DESTINATION_ADD_OPERATION ) {
std::cout &lt;&lt; &quot;Temporary Destination {&quot;
&lt;&lt; info-&gt;getDestination()-&gt;getPhysicalName()
&lt;&lt; &quot;} Added.&quot;
&lt;&lt; std::endl;
} else {
std::cout &lt;&lt; &quot;ERROR: I have no Idea what just happened!&quot;
&lt;&lt; std::endl;
}
} catch( ClassCastException&amp; ex ) {
std::cout &lt;&lt; &quot;ERROR: Expected the Command to be a DestinationInfo, &quot;
&lt;&lt; &quot;it wasn&#39;t so PANIC!!&quot;
&lt;&lt; std::endl;
}
]]></script>
</div></div>
<p>First thing you are probably asking about this code snippet is "what is that Pointer thing?", that is a thread safe smart pointer that is used internally by ActiveMQ-CPP to manage all the pointers that make up the cms::Message objects among other things. We create an instance of a Pointer&lt;DestinationInfo&gt; type which will pointer to our DestinationInfo command if the dynamicCast method is able to make that conversion, if not a ClassCastException is thrown. Once we have the DestinationInfo pointer we can retrieve the Operation Type of the command and then compare it to the constants in ActiveMQConstants to see what is being done to the Destination. There are only two operation types, add and remove, but since the DestinationInfo object encodes the operation type value as an unsigned char we provide a fall-back case to alert us to that error. We are almost done now, all that remains is to output what happened, and also let the user know what the name of the Destination is, the <strong>getPhysicalName</strong> method in the Destination class tells us that. You could also find out whether the Destination is a Topic or a Queue using the Destination object, we leave that as an exercise to the reader.</p>
<h3 id="HandlingAdvisoryMessages-CompleteExample:ConsumerthatlistensforCreationandDestructionofTemporaryDestinations"><strong>Complete Example: Consumer that listens for Creation and Destruction of Temporary Destinations</strong></h3>
<p>The complete code of our client application is shown below, you can also find this code as well as a simple client that creates both a Temporary Topic and a Temporary Queue in the examples folder in the source distribution.</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>TempDestinationAdvisoryConsumer Header File</b></div><div class="codeContent panelContent pdl">
<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[
#ifndef _ACTIVEMQCPP_EXAMPLES_ADVISORIES_TEMPDESTINATIONADVISORYCONSUMER_H_
#define _ACTIVEMQCPP_EXAMPLES_ADVISORIES_TEMPDESTINATIONADVISORYCONSUMER_H_
#include &lt;string&gt;
#include &lt;memory&gt;
#include &lt;cms/Session.h&gt;
#include &lt;cms/MessageProducer.h&gt;
#include &lt;cms/MessageConsumer.h&gt;
#include &lt;cms/MessageListener.h&gt;
#include &lt;decaf/lang/Runnable.h&gt;
namespace activemqcpp {
namespace examples {
namespace advisories {
/**
* Monitors a Broker for Temporary Topic creation and destruction.
*
* @since 3.0
*/
class TempDestinationAdvisoryConsumer : public cms::MessageListener {
private:
cms::Session* session;
std::auto_ptr&lt;cms::MessageConsumer&gt; consumer;
public:
TempDestinationAdvisoryConsumer( cms::Session* session );
virtual ~TempDestinationAdvisoryConsumer();
/**
* Async Message callback.
*/
virtual void onMessage( const cms::Message* message );
};
}}}
#endif /* _ACTIVEMQCPP_EXAMPLES_ADVISORIES_TEMPDESTINATIONADVISORYCONSUMER_H_ */
]]></script>
</div></div>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>TempDestinationAdvisoryConsumer Source File</b></div><div class="codeContent panelContent pdl">
<script class="brush: java; gutter: false; theme: Default" type="syntaxhighlighter"><![CDATA[
#include &quot;TempDestinationAdvisoryConsumer.h&quot;
#include &lt;cms/Topic.h&gt;
#include &lt;cms/Message.h&gt;
#include &lt;cms/TextMessage.h&gt;
#include &lt;activemq/core/ActiveMQConstants.h&gt;
#include &lt;activemq/commands/ActiveMQMessage.h&gt;
#include &lt;activemq/commands/DestinationInfo.h&gt;
#include &lt;decaf/lang/exceptions/NullPointerException.h&gt;
#include &lt;decaf/lang/exceptions/ClassCastException.h&gt;
#include &lt;decaf/lang/Integer.h&gt;
using namespace std;
using namespace activemqcpp;
using namespace activemqcpp::examples;
using namespace activemqcpp::examples::advisories;
using namespace activemq;
using namespace activemq::commands;
using namespace activemq::core;
using namespace decaf;
using namespace decaf::lang;
using namespace decaf::lang::exceptions;
////////////////////////////////////////////////////////////////////////////////
TempDestinationAdvisoryConsumer::TempDestinationAdvisoryConsumer( cms::Session* session ) {
if( session == NULL ) {
throw NullPointerException(
__FILE__, __LINE__, &quot;Session Object passed was Null.&quot; );
}
std::auto_ptr&lt;cms::Topic&gt; advisories( session-&gt;createTopic(
&quot;ActiveMQ.Advisory.TempTopic,ActiveMQ.Advisory.TempQueue&quot; ) );
this-&gt;session = session;
this-&gt;consumer.reset( session-&gt;createConsumer( advisories.get() ) );
this-&gt;consumer-&gt;setMessageListener( this );
}
////////////////////////////////////////////////////////////////////////////////
TempDestinationAdvisoryConsumer::~TempDestinationAdvisoryConsumer() {
}
////////////////////////////////////////////////////////////////////////////////
void TempDestinationAdvisoryConsumer::onMessage( const cms::Message* message ) {
if( message-&gt;getCMSType() == &quot;Advisory&quot; ) {
std::cout &lt;&lt; &quot;Received an Advisory Message!&quot; &lt;&lt; std::endl;
const ActiveMQMessage* amqMessage =
dynamic_cast&lt;const ActiveMQMessage*&gt;( message );
if( amqMessage != NULL &amp;&amp; amqMessage-&gt;getDataStructure() != NULL ) {
std::cout &lt;&lt; &quot;Advisory Message contains a Command Object!&quot; &lt;&lt; std::endl;
try {
Pointer&lt;DestinationInfo&gt; info =
amqMessage-&gt;getDataStructure().dynamicCast&lt;DestinationInfo&gt;();
unsigned char operationType = info-&gt;getOperationType();
if( operationType == ActiveMQConstants::DESTINATION_REMOVE_OPERATION ) {
std::cout &lt;&lt; &quot;Temporary Destination {&quot;
&lt;&lt; info-&gt;getDestination()-&gt;getPhysicalName()
&lt;&lt; &quot;} Removed.&quot;
&lt;&lt; std::endl;
} else if( operationType == ActiveMQConstants::DESTINATION_ADD_OPERATION ) {
std::cout &lt;&lt; &quot;Temporary Destination {&quot;
&lt;&lt; info-&gt;getDestination()-&gt;getPhysicalName()
&lt;&lt; &quot;} Added.&quot;
&lt;&lt; std::endl;
} else {
std::cout &lt;&lt; &quot;ERROR: I have no Idea what just happened!&quot;
&lt;&lt; std::endl;
}
} catch( ClassCastException&amp; ex ) {
std::cout &lt;&lt; &quot;ERROR: Expected the Command to be a DestinationInfo, &quot;
&lt;&lt; &quot;it wasn&#39;t so PANIC!!&quot;
&lt;&lt; std::endl;
}
}
} else {
std::cout &lt;&lt; &quot;Received a Non-Advisory Message!&quot; &lt;&lt; std::endl;
}
}
]]></script>
</div></div></div>