/*
 * JORAM: Java(TM) Open Reliable Asynchronous Messaging
 * Copyright (C) 2004 - 2025 ScalAgent Distributed Technologies
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA.
 */
package org.ow2.joram.jakarta.connector;

import jakarta.jms.ConnectionFactory;
import jakarta.jms.IllegalStateException;
import jakarta.jms.JMSException;
import jakarta.jms.JMSSecurityException;
import jakarta.jms.QueueConnectionFactory;
import jakarta.jms.TopicConnectionFactory;
import jakarta.resource.ResourceException;
import jakarta.resource.spi.CommException;
import jakarta.resource.spi.ConnectionManager;
import jakarta.resource.spi.ConnectionRequestInfo;
import jakarta.resource.spi.ManagedConnectionFactory;
import jakarta.resource.spi.SecurityException;

import java.io.Serializable;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.ow2.joram.jakarta.jms.admin.AbstractConnectionFactory;
import org.ow2.joram.jakarta.jms.tcp.TcpConnectionFactory;

import fr.dyade.aaa.common.Debug;

/** 
 * The <code>DefaultConnectionManager</code> class is the default connection
 * manager provided with JORAM resource adapter, which intercepts connections
 * requests coming from non managed client applications.
 * 
 * ConnectionManager interface provides a hook for the resource adapter to pass a connection request to the application server. 
 * 
 * An application server provides implementation of the ConnectionManagerinterface. This implementation is not specific to any
 * particular type of the resource adapter or connection factory interface. 
 * The ConnectionManager implementation delegates to the application server to enable latter to provide quality of services (QoS)
 * - security,connection pool management, transaction management and error logging/tracing. 
 * An application server implements these services in a generic manner,independent of any resource adapter and EIS specific mechanisms.
 * The connector architecture does not specify how an application server implements these services; the implementation is specific to
 * an application server. 
 * After an application server hooks-in its services, the connection request gets delegated to a ManagedConnectionFactory instance either
 * for the creation of a new physical connection or for the matching of an already existing physical connection. 
 * An implementation class for ConnectionManager interface is required to implement the java.io.Serializable interface. 
 * In the non-managed application scenario, the ConnectionManagerimplementation class can be provided either by a resource adapter (as a
 * default ConnectionManager implementation) or by application developers. In both cases, QOS can be provided as components by third party
 * vendors.
 */
public class DefaultConnectionManager implements ConnectionManager, Serializable {
  /** Define serialVersionUID for interoperability. */
  private static final long serialVersionUID = 1L;
  
  private static final Logger logger = Debug.getLogger(DefaultConnectionManager.class.getName());
  private static final boolean DEBUG = logger.isLoggable(BasicLevel.DEBUG);
  
  /**
   * Static reference to the local <code>DefaultConnectionManager</code>
   * instance.
   */
  private static DefaultConnectionManager ref = null;


  /**
   * Creates a <code>DefaultConnectionManager</code> instance.
   */
  public DefaultConnectionManager() {}

  private void setFactoryParameters(AbstractConnectionFactory factory , ManagedConnectionFactoryImpl mcf) {
    if (DEBUG)
      logger.log(BasicLevel.DEBUG, this + " setFactoryParameters(" + factory + "," + mcf + ")"); 
    
    mcf.setParameters(factory);
  }

  /**
   * Returns the reference to the <code>DefaultConnectionManager</code>
   * instance, creates it if needed.
   */
  static DefaultConnectionManager getRef() {
    if (ref == null)
      ref = new DefaultConnectionManager();

    return ref;
  }
  
  // ************************************************************
  // Implementation of ConnectionManager
  // ************************************************************

  /**
   * Returns a <code>Connection</code> connection instance for
   * a non managed application.
   *
   * @exception CommException      If connecting fails.
   * @exception SecurityException  If connecting is not authorized.
   * @exception ResourceException  Generic exception.
   */
  public Object allocateConnection(ManagedConnectionFactory mcf, ConnectionRequestInfo cxRequest) throws ResourceException {
    if (DEBUG)
      logger.log(BasicLevel.DEBUG, this + " allocateConnection(" + mcf + "," + cxRequest + ")");
    
    String userName;
    String password;
    String identityClass;
    
    if (cxRequest == null) {
      userName = ((ManagedConnectionFactoryImpl) mcf).getUserName();
      password = ((ManagedConnectionFactoryImpl) mcf).getPassword();
      identityClass = ((ManagedConnectionFactoryImpl) mcf).getIdentityClass();
    } else {
      userName = ((ConnectionRequest) cxRequest).getUserName();
      password = ((ConnectionRequest) cxRequest).getPassword();
      identityClass = ((ConnectionRequest) cxRequest).getIdentityClass();
    }
    
    String hostName = ((ManagedConnectionFactoryImpl) mcf).getHostName();
    int serverPort = ((ManagedConnectionFactoryImpl) mcf).getServerPort().intValue();
    
    if (DEBUG)
      logger.log(BasicLevel.DEBUG, this + " allocateConnection: hostName = " + hostName + ", serverPort = " + serverPort);
    
    try {
      if (cxRequest instanceof QueueConnectionRequest) {
        QueueConnectionFactory factory = TcpConnectionFactory.create(hostName, serverPort);
        setFactoryParameters((AbstractConnectionFactory) factory, (ManagedConnectionFactoryImpl) mcf);
        ((AbstractConnectionFactory) factory).setIdentityClassName(identityClass);
        return factory.createQueueConnection(userName, password);
      } else if (cxRequest instanceof TopicConnectionRequest) {
        TopicConnectionFactory factory = TcpConnectionFactory.create(hostName, serverPort);
        setFactoryParameters((AbstractConnectionFactory) factory, (ManagedConnectionFactoryImpl) mcf);
        ((AbstractConnectionFactory) factory).setIdentityClassName(identityClass);
        return factory.createTopicConnection(userName, password);
      } else {
        ConnectionFactory factory = TcpConnectionFactory.create(hostName, serverPort);
        setFactoryParameters((AbstractConnectionFactory) factory, (ManagedConnectionFactoryImpl) mcf);
        ((AbstractConnectionFactory) factory).setIdentityClassName(identityClass);
        return factory.createConnection(userName, password);
      }
    } catch (IllegalStateException exc) {
      throw new CommException("Could not access the JORAM server: " + exc);
    } catch (JMSSecurityException exc) {
      throw new SecurityException("Invalid user identification: " + exc);
    } catch (JMSException exc) {
      throw new ResourceException("Failed connecting process: " + exc);
    }
  }
}
