/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2001, 2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Axis" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.jboss.axis.transport.jms;

import org.jboss.axis.AxisEngine;
import org.jboss.axis.AxisFault;
import org.jboss.axis.MessageContext;
import org.jboss.axis.client.Call;
import org.jboss.axis.client.Transport;
import org.jboss.axis.utils.Messages;
import org.jboss.logging.Logger;

import javax.jms.Destination;
import java.util.HashMap;
import java.util.Iterator;

/**
 * JMSTransport is the JMS-specific implemenation of org.jboss.axis.client.Transport.
 *   It implements the setupMessageContextImpl() function to set JMS-specific message
 *   context fields and transport chains. Connector and connection factory
 *   properties are passed in during instantiation and are in turn passed through
 *   when creating a connector.
 *
 * @author Jaime Meritt  (jmeritt@sonicsoftware.com)
 * @author Richard Chung (rchung@sonicsoftware.com)
 * @author Dave Chappell (chappell@sonicsoftware.com)
 */
public class JMSTransport extends Transport
{
   protected static Logger log = Logger.getLogger(JMSTransport.class);

   private HashMap connectors;
   private HashMap connectorProps;
   private HashMap connectionFactoryProps;
   private JMSConnector defaultConnector;
   private HashMap passwords;
   private Object connectorLock;

   public JMSTransport(HashMap connectorProps,
                       HashMap connectionFactoryProps)
   {
      transportName = "JMSTransport";
      connectors = new HashMap();
      passwords = new HashMap();
      this.connectorProps = connectorProps;
      this.connectionFactoryProps = connectionFactoryProps;
      connectorLock = new Object();
   }

   /**
    * Set up any transport-specific derived properties in the message context.
    * @param context the context to set up
    * @param message the client service instance
    * @param engine the engine containing the registries
    * @throws AxisFault if service cannot be found
    */
   public void setupMessageContextImpl(MessageContext context,
                                       Call message,
                                       AxisEngine engine)
           throws AxisFault
   {
      String username = message.getUsername();
      JMSConnector connector = null;
      try
      {
         if (username == null)
         {
            initConnectorIfNecessary();
            connector = defaultConnector;
         }
         else
         {
            String password = message.getPassword();
            synchronized (connectorLock)
            {
               if (connectors.containsKey(username))
               {
                  String oldPassword = (String)passwords.get(username);
                  if (password.equals(oldPassword))
                     connector = (JMSConnector)connectors.get(username);
                  else
                     throw new AxisFault("badUserPass");
               }
               else
               {
                  connector = createConnector(username, password);
                  connectors.put(username, connector);
                  // I should really md5 hash these
                  passwords.put(username, password);
               }
            }
         }
      }
      catch (Exception e)
      {
         log.error(Messages.getMessage("cannotConnectError"), e);

         if (e instanceof AxisFault)
            throw (AxisFault)e;
         throw new AxisFault("cannotConnect", e);
      }

      context.setProperty(JMSConstants.CONNECTOR, connector);

      //I would like to use the following, but that requires JMS-URL syntax
      //which I don't have so I will rely on message properties for now
      //String destination = message.getTargetEndpointAddress();

      Object tmp = message.getProperty(JMSConstants.DESTINATION);
      if (tmp != null && (tmp instanceof String || tmp instanceof Destination))
         context.setProperty(JMSConstants.DESTINATION, tmp);
      else
         context.removeProperty(JMSConstants.DESTINATION);

      tmp = message.getProperty(JMSConstants.WAIT_FOR_RESPONSE);
      if (tmp != null && tmp instanceof Boolean)
         context.setProperty(JMSConstants.WAIT_FOR_RESPONSE, tmp);
      else
         context.removeProperty(JMSConstants.WAIT_FOR_RESPONSE);

      tmp = message.getProperty(JMSConstants.DELIVERY_MODE);
      if (tmp != null && tmp instanceof Integer)
         context.setProperty(JMSConstants.DELIVERY_MODE, tmp);
      else
         context.removeProperty(JMSConstants.DELIVERY_MODE);

      tmp = message.getProperty(JMSConstants.PRIORITY);
      if (tmp != null && tmp instanceof Integer)
         context.setProperty(JMSConstants.PRIORITY, tmp);
      else
         context.removeProperty(JMSConstants.PRIORITY);

      tmp = message.getProperty(JMSConstants.TIME_TO_LIVE);
      if (tmp != null && tmp instanceof Long)
         context.setProperty(JMSConstants.TIME_TO_LIVE, tmp);
      else
         context.removeProperty(JMSConstants.TIME_TO_LIVE);

   }

   private void initConnectorIfNecessary()
           throws Exception
   {
      if (defaultConnector != null)
         return;
      synchronized (connectorLock)
      {
         //this is to catch a race issue when n threads do the null check
         //before one of them actually creates the default connector
         if (defaultConnector != null)
            return;
         defaultConnector = createConnector(null, null);
      }
   }

   private JMSConnector createConnector(String username, String password)
           throws Exception
   {
      JMSConnector connector = JMSConnectorFactory.
              createClientConnector(connectorProps, connectionFactoryProps,
                      username, password);
      connector.start();
      return connector;
   }

   /**
    * Shuts down the connectors managed by this JMSTransport.
    */
   public void shutdown()
   {
      synchronized (connectorLock)
      {
         if (defaultConnector != null)
            defaultConnector.shutdown();

         Iterator connectorIter = connectors.values().iterator();
         while (connectorIter.hasNext())
         {
            ((JMSConnector)connectorIter.next()).shutdown();
         }
      }
   }

}