/*
 * Copyright 2009 Red Hat, Inc.
 * Red Hat licenses this file to you under the Apache License, version
 * 2.0 (the "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *    http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package org.hornetq.jms.example;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.naming.InitialContext;

import org.hornetq.common.example.HornetQExample;

/**
 * This examples demonstrates the use of HornetQ "Diverts" to transparently divert or copy messages
 * from one address to another.
 *
 * Please see the readme.html for more information.
 *
 * @author <a href="tim.fox@jboss.com">Tim Fox</a>
 */
public class DivertExample extends HornetQExample
{
   public static void main(final String[] args)
   {
      new DivertExample().run(args);
   }

   @Override
   public boolean runExample() throws Exception
   {
      Connection connectionLondon = null;

      Connection connectionNewYork = null;

      InitialContext initialContextLondon = null;

      InitialContext initialContextNewYork = null;
      try
      {
         // Step 1. Create an initial context to perform the JNDI lookup on the London server
         initialContextLondon = getContext(0);

         // Step 2. Look-up the queue orderQueue on the London server - this is the queue any orders are sent to
         Queue orderQueue = (Queue)initialContextLondon.lookup("/queue/orders");

         // Step 3. Look-up the topic priceUpdates on the London server- this is the topic that any price updates are
         // sent to
         Topic priceUpdates = (Topic)initialContextLondon.lookup("/topic/priceUpdates");

         // Step 4. Look-up the spy topic on the London server- this is what we will use to snoop on any orders
         Topic spyTopic = (Topic)initialContextLondon.lookup("/topic/spyTopic");

         // Step 6. Create an initial context to perform the JNDI lookup on the New York server
         initialContextNewYork = getContext(1);

         // Step 7. Look-up the topic newYorkPriceUpdates on the New York server - any price updates sent to
         // priceUpdates on the London server will
         // be diverted to the queue priceForward on the London server, and a bridge will consume from that queue and
         // forward
         // them to the address newYorkPriceUpdates on the New York server where they will be distributed to the topic
         // subscribers on
         // the New York server
         Topic newYorkPriceUpdates = (Topic)initialContextNewYork.lookup("/topic/newYorkPriceUpdates");

         // Step 8. Perform a lookup on the Connection Factory on the London server
         ConnectionFactory cfLondon = (ConnectionFactory)initialContextLondon.lookup("/ConnectionFactory");

         // Step 9. Perform a lookup on the Connection Factory on the New York server
         ConnectionFactory cfNewYork = (ConnectionFactory)initialContextNewYork.lookup("/ConnectionFactory");

         // Step 10. Create a JMS Connection on the London server
         connectionLondon = cfLondon.createConnection();

         // Step 11. Create a JMS Connection on the New York server
         connectionNewYork = cfNewYork.createConnection();

         // Step 12. Create a JMS Session on the London server
         Session sessionLondon = connectionLondon.createSession(false, Session.AUTO_ACKNOWLEDGE);

         // Step 13. Create a JMS Session on the New York server
         Session sessionNewYork = connectionNewYork.createSession(false, Session.AUTO_ACKNOWLEDGE);

         // Step 14. Create a JMS MessageProducer orderProducer that sends to the queue orderQueue on the London server
         MessageProducer orderProducer = sessionLondon.createProducer(orderQueue);

         // Step 15. Create a JMS MessageProducer priceProducer that sends to the topic priceUpdates on the London
         // server
         MessageProducer priceProducer = sessionLondon.createProducer(priceUpdates);

         // Step 15. Create a JMS subscriber which subscribes to the spyTopic on the London server
         MessageConsumer spySubscriberA = sessionLondon.createConsumer(spyTopic);

         // Step 16. Create another JMS subscriber which also subscribes to the spyTopic on the London server
         MessageConsumer spySubscriberB = sessionLondon.createConsumer(spyTopic);

         // Step 17. Create a JMS MessageConsumer which consumes orders from the order queue on the London server
         MessageConsumer orderConsumer = sessionLondon.createConsumer(orderQueue);

         // Step 18. Create a JMS subscriber which subscribes to the priceUpdates topic on the London server
         MessageConsumer priceUpdatesSubscriberLondon = sessionLondon.createConsumer(priceUpdates);

         // Step 19. Create a JMS subscriber which subscribes to the newYorkPriceUpdates topic on the New York server
         MessageConsumer newYorkPriceUpdatesSubscriberA = sessionNewYork.createConsumer(newYorkPriceUpdates);

         // Step 20. Create another JMS subscriber which also subscribes to the newYorkPriceUpdates topic on the New
         // York server
         MessageConsumer newYorkPriceUpdatesSubscriberB = sessionNewYork.createConsumer(newYorkPriceUpdates);

         // Step 21. Start the connections

         connectionLondon.start();

         connectionNewYork.start();

         // Step 22. Create an order message
         TextMessage orderMessage = sessionLondon.createTextMessage("This is an order");

         // Step 23. Send the order message to the order queue on the London server
         orderProducer.send(orderMessage);

         System.out.println("Sent message: " + orderMessage.getText());

         // Step 24. The order message is consumed by the orderConsumer on the London server
         TextMessage receivedOrder = (TextMessage)orderConsumer.receive(5000);

         System.out.println("Received order: " + receivedOrder.getText());

         // Step 25. A copy of the order is also received by the spyTopic subscribers on the London server
         TextMessage spiedOrder1 = (TextMessage)spySubscriberA.receive(5000);

         System.out.println("Snooped on order: " + spiedOrder1.getText());

         TextMessage spiedOrder2 = (TextMessage)spySubscriberB.receive(5000);

         System.out.println("Snooped on order: " + spiedOrder2.getText());

         // Step 26. Create and send a price update message, destined for London
         TextMessage priceUpdateMessageLondon = sessionLondon.createTextMessage("This is a price update for London");

         priceUpdateMessageLondon.setStringProperty("office", "London");

         priceProducer.send(priceUpdateMessageLondon);

         // Step 27. The price update *should* be received by the local subscriber since we only divert messages
         // where office = New York
         TextMessage receivedUpdate = (TextMessage)priceUpdatesSubscriberLondon.receive(2000);

         System.out.println("Received price update locally: " + receivedUpdate.getText());

         // Step 28. The price update *should not* be received in New York

         TextMessage priceUpdate1 = (TextMessage)newYorkPriceUpdatesSubscriberA.receive(1000);

         if (priceUpdate1 != null)
         {
            return false;
         }

         System.out.println("Did not received price update in New York, look it's: " + priceUpdate1);

         TextMessage priceUpdate2 = (TextMessage)newYorkPriceUpdatesSubscriberB.receive(1000);

         if (priceUpdate2 != null)
         {
            return false;
         }

         System.out.println("Did not received price update in New York, look it's: " + priceUpdate2);

         // Step 29. Create a price update message, destined for New York

         TextMessage priceUpdateMessageNewYork = sessionLondon.createTextMessage("This is a price update for New York");

         priceUpdateMessageNewYork.setStringProperty("office", "New York");

         // Step 30. Send the price update message to the priceUpdates topic on the London server
         priceProducer.send(priceUpdateMessageNewYork);

         // Step 31. The price update *should not* be received by the local subscriber to the priceUpdates topic
         // since it has been *exclusively* diverted to the priceForward queue, because it has a header saying
         // it is destined for the New York office
         Message message = priceUpdatesSubscriberLondon.receive(1000);

         if (message != null)
         {
            return false;
         }

         System.out.println("Didn't receive local price update, look, it's: " + message);

         // Step 32. The remote subscribers on server 1 *should* receive a copy of the price update since
         // it has been diverted to a local priceForward queue which has a bridge consuming from it and which
         // forwards it to the same address on server 1.
         // We notice how the forwarded messages have had a special header added by our custom transformer that
         // we told the divert to use

         priceUpdate1 = (TextMessage)newYorkPriceUpdatesSubscriberA.receive(5000);

         System.out.println("Received forwarded price update on server 1: " + priceUpdate1.getText());
         System.out.println("Time of forward: " + priceUpdate1.getLongProperty("time_of_forward"));

         priceUpdate2 = (TextMessage)newYorkPriceUpdatesSubscriberB.receive(5000);

         System.out.println("Received forwarded price update on server 2: " + priceUpdate2.getText());
         System.out.println("Time of forward: " + priceUpdate2.getLongProperty("time_of_forward"));

         return true;
      }
      finally
      {
         // Step 12. Be sure to close our resources!
         if (initialContextLondon != null)
         {
            initialContextLondon.close();
         }
         if (initialContextNewYork != null)
         {
            initialContextNewYork.close();
         }
         if (connectionLondon != null)
         {
            connectionLondon.close();
         }
         if (connectionNewYork != null)
         {
            connectionNewYork.close();
         }
      }
   }

}
