/*
 * 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.JMSException;
import javax.jms.JMSSecurityException;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.naming.InitialContext;

import org.hornetq.common.example.HornetQExample;

/**
 * A simple JMS example that shows how to access messaging with security configured.
 *
 * @author <a href="hgao@redhat.com">Howard Gao</a>
 */
public class SecurityExample extends HornetQExample
{
   private boolean result = true;

   public static void main(final String[] args)
   {
      new SecurityExample().run(args);
   }

   @Override
   public boolean runExample() throws Exception
   {
      Connection billConnection = null;
      Connection andrewConnection = null;
      Connection frankConnection = null;
      Connection samConnection = null;

      InitialContext initialContext = null;
      try
      {
         // /Step 1. Create an initial context to perform the JNDI lookup.
         initialContext = getContext(0);

         // Step 2. perform lookup on the topics
         Topic genericTopic = (Topic)initialContext.lookup("/topic/genericTopic");
         Topic europeTopic = (Topic)initialContext.lookup("/topic/europeTopic");
         Topic usTopic = (Topic)initialContext.lookup("/topic/usTopic");

         // Step 3. perform a lookup on the Connection Factory
         ConnectionFactory cf = (ConnectionFactory)initialContext.lookup("/ConnectionFactory");

         // Step 4. Try to create a JMS Connection without user/password. It will fail.
         try
         {
            cf.createConnection();
            result = false;
         }
         catch (JMSSecurityException e)
         {
            System.out.println("Default user cannot get a connection. Details: " + e.getMessage());
         }

         // Step 5. bill tries to make a connection using wrong password
         billConnection = null;
         try
         {
            billConnection = createConnection("bill", "hornetq1", cf);
            result = false;
         }
         catch (JMSException e)
         {
            System.out.println("User bill failed to connect. Details: " + e.getMessage());
         }

         // Step 6. bill makes a good connection.
         billConnection = createConnection("bill", "hornetq", cf);
         billConnection.start();

         // Step 7. andrew makes a good connection.
         andrewConnection = createConnection("andrew", "hornetq1", cf);
         andrewConnection.start();

         // Step 8. frank makes a good connection.
         frankConnection = createConnection("frank", "hornetq2", cf);
         frankConnection.start();

         // Step 9. sam makes a good connection.
         samConnection = createConnection("sam", "hornetq3", cf);
         samConnection.start();

         // Step 10. Check every user can publish/subscribe genericTopics.
         System.out.println("------------------------Checking permissions on " + genericTopic + "----------------");
         checkUserSendAndReceive(genericTopic, billConnection, "bill");
         checkUserSendAndReceive(genericTopic, andrewConnection, "andrew");
         checkUserSendAndReceive(genericTopic, frankConnection, "frank");
         checkUserSendAndReceive(genericTopic, samConnection, "sam");
         System.out.println("-------------------------------------------------------------------------------------");

         System.out.println("------------------------Checking permissions on " + europeTopic + "----------------");

         // Step 11. Check permissions on news.europe.europeTopic for bill: can't send and can't receive
         checkUserNoSendNoReceive(europeTopic, billConnection, "bill");

         // Step 12. Check permissions on news.europe.europeTopic for andrew: can send but can't receive
         checkUserSendNoReceive(europeTopic, andrewConnection, "andrew", frankConnection);

         // Step 13. Check permissions on news.europe.europeTopic for frank: can't send but can receive
         checkUserReceiveNoSend(europeTopic, frankConnection, "frank", andrewConnection);

         // Step 14. Check permissions on news.europe.europeTopic for sam: can't send but can receive
         checkUserReceiveNoSend(europeTopic, samConnection, "sam", andrewConnection);
         System.out.println("-------------------------------------------------------------------------------------");

         System.out.println("------------------------Checking permissions on " + usTopic + "----------------");

         // Step 15. Check permissions on news.us.usTopic for bill: can't send and can't receive
         checkUserNoSendNoReceive(usTopic, billConnection, "bill");

         // Step 16. Check permissions on news.us.usTopic for andrew: can't send and can't receive
         checkUserNoSendNoReceive(usTopic, andrewConnection, "andrew");

         // Step 17. Check permissions on news.us.usTopic for frank: can both send and receive
         checkUserSendAndReceive(usTopic, frankConnection, "frank");

         // Step 18. Check permissions on news.us.usTopic for sam: can't send but can receive
         checkUserReceiveNoSend(usTopic, samConnection, "sam", frankConnection);
         System.out.println("-------------------------------------------------------------------------------------");

         return result;
      }
      finally
      {
         // Step 19. Be sure to close our JMS resources!
         if (billConnection != null)
         {
            billConnection.close();
         }
         if (andrewConnection != null)
         {
            andrewConnection.close();
         }
         if (frankConnection != null)
         {
            frankConnection.close();
         }
         if (samConnection != null)
         {
            samConnection.close();
         }

         // Also the initialContext
         if (initialContext != null)
         {
            initialContext.close();
         }
      }
   }

   // Check the user can receive message but cannot send message.
   private void checkUserReceiveNoSend(final Topic topic,
                                       final Connection connection,
                                       final String user,
                                       final Connection sendingConn) throws JMSException
   {
      Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
      MessageProducer producer = session.createProducer(topic);
      MessageConsumer consumer = session.createConsumer(topic);
      TextMessage msg = session.createTextMessage("hello-world-1");

      try
      {
         producer.send(msg);
         System.out.println("Security setting is broken! User " + user +
                            " can send message [" +
                            msg.getText() +
                            "] to topic " +
                            topic);
         result = false;
      }
      catch (JMSException e)
      {
         System.out.println("User " + user + " cannot send message [" + msg.getText() + "] to topic: " + topic);
      }

      // Now send a good message
      Session session1 = sendingConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
      producer = session1.createProducer(topic);
      producer.send(msg);

      TextMessage receivedMsg = (TextMessage)consumer.receive(2000);

      if (receivedMsg != null)
      {
         System.out.println("User " + user + " can receive message [" + receivedMsg.getText() + "] from topic " + topic);
      }
      else
      {
         System.out.println("Security setting is broken! User " + user + " cannot receive message from topic " + topic);
         result = false;
      }

      session1.close();
      session.close();
   }

   // Check the user can send message but cannot receive message
   private void checkUserSendNoReceive(final Topic topic,
                                       final Connection connection,
                                       final String user,
                                       final Connection receivingConn) throws JMSException
   {
      Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
      MessageProducer producer = session.createProducer(topic);
      try
      {
         session.createConsumer(topic);
      }
      catch (JMSException e)
      {
         System.out.println("User " + user + " cannot receive any message from topic " + topic);
      }

      Session session1 = receivingConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
      MessageConsumer goodConsumer = session1.createConsumer(topic);

      TextMessage msg = session.createTextMessage("hello-world-2");
      producer.send(msg);

      TextMessage receivedMsg = (TextMessage)goodConsumer.receive(2000);
      if (receivedMsg != null)
      {
         System.out.println("User " + user + " can send message [" + receivedMsg.getText() + "] to topic " + topic);
      }
      else
      {
         System.out.println("Security setting is broken! User " + user +
                            " cannot send message [" +
                            msg.getText() +
                            "] to topic " +
                            topic);
         result = false;
      }

      session.close();
      session1.close();
   }

   // Check the user has neither send nor receive permission on topic
   private void checkUserNoSendNoReceive(final Topic topic, final Connection connection, final String user) throws JMSException
   {
      Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
      MessageProducer producer = session.createProducer(topic);

      try
      {
         session.createConsumer(topic);
      }
      catch (JMSException e)
      {
         System.out.println("User " + user + " cannot create consumer on topic " + topic);
      }

      TextMessage msg = session.createTextMessage("hello-world-3");
      try
      {
         producer.send(msg);
         System.out.println("Security setting is broken! User " + user +
                            " can send message [" +
                            msg.getText() +
                            "] to topic " +
                            topic);
         result = false;
      }
      catch (JMSException e)
      {
         System.out.println("User " + user + " cannot send message [" + msg.getText() + "] to topic: " + topic);
      }

      session.close();
   }

   // Check the user connection has both send and receive permissions on the topic
   private void checkUserSendAndReceive(final Topic topic, final Connection connection, final String user) throws JMSException
   {
      Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
      TextMessage msg = session.createTextMessage("hello-world-4");
      MessageProducer producer = session.createProducer(topic);
      MessageConsumer consumer = session.createConsumer(topic);
      producer.send(msg);
      TextMessage receivedMsg = (TextMessage)consumer.receive(5000);
      if (receivedMsg != null)
      {
         System.out.println("User " + user + " can send message: [" + msg.getText() + "] to topic: " + topic);
         System.out.println("User " + user + " can receive message: [" + msg.getText() + "] from topic: " + topic);
      }
      else
      {
         System.out.println("Error! User " + user + " cannot receive the message! ");
         result = false;
      }
      session.close();
   }

   private Connection createConnection(final String username, final String password, final ConnectionFactory cf) throws JMSException
   {
      return cf.createConnection(username, password);
   }
}
