/*
 *  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 *  Copyright 2000-2009 Sun Microsystems, Inc. All rights reserved. 
 *
 *  The contents of this file are subject to the terms of either the GNU
 *  General Public License Version 2 only ("GPL") or the Common Development
 *  and Distribution License ("CDDL") (collectively, the "License").  You may
 *  not use this file except in compliance with the License.  You can obtain
 *  a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 *  or mq/legal/LICENSE.txt.  See the License for the specific language
 *  governing permissions and limitations under the License.
 * 
 *  When distributing the software, include this License Header Notice in each
 *  file and include the License file at mq/legal/LICENSE.txt.  Sun designates
 *  this particular file as subject to the "Classpath" exception as provided by
 *  Sun in the GPL Version 2 section of the License file that accompanied this
 *  code.  If applicable, add the following below the License Header, with the
 *  fields enclosed by brackets [] replaced by your own identifying information:
 *  "Portions Copyrighted [year] [name of copyright owner]"
 * 
 *  Contributor(s):
 * 
 *  If you wish your version of this file to be governed by only the CDDL or
 *  only the GPL Version 2, indicate your decision by adding "[Contributor]
 *  elects to include this software in this distribution under the [CDDL or GPL
 *  Version 2] license."  If you don't indicate a single choice of license, a
 *  recipient has the option to distribute your version of this file under
 *  either the CDDL, the GPL Version 2 or  to extend the choice of license to
 *  its licensees as provided above.  However, if you add GPL Version 2 code
 *  and therefore, elected the GPL Version 2 license, then the option applies
 *  only if the new code is made subject to such option by the copyright holder. 
 *
 *  ConnectionAdapter
 *
 *  @(#)ConnectionAdapter.java	1.28 07/12/07
 */

package com.sun.messaging.jms.ra;

import javax.jms.*; 
import javax.resource.ResourceException;
import javax.resource.spi.ConnectionEvent;
//import javax.resource.spi.*;

import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.logging.Logger;

import com.sun.messaging.jmq.jmsclient.XASessionImpl;
import com.sun.messaging.jmq.jmsclient.XAQueueSessionImpl;
import com.sun.messaging.jmq.jmsclient.XATopicSessionImpl;
 
import com.sun.messaging.jms.IllegalStateException;
 
/**
 * Implements the JMS Connection interface for the S1 MQ RA.
 */
 
public class ConnectionAdapter
implements javax.jms.Connection,
           javax.jms.QueueConnection,
           javax.jms.TopicConnection
{
    /** The ResourceAdapter instance associated with this instance */
    private com.sun.messaging.jms.ra.ResourceAdapter ra = null;

    /** The ManagedConnection instance that created this instance */
    private com.sun.messaging.jms.ra.ManagedConnection mc = null;

    /** The XAConection instance wrapped by this ConnectionAdapter */
    public com.sun.messaging.jmq.jmsclient.XAConnectionImpl xac = null;

    /** The XASession instance created by this ConnectionAdapter */
    private com.sun.messaging.jms.ra.SessionAdapter sa = null;

    /** Flags whether further creation of session is allowed per J2EE1.4 */
    private boolean sessions_allowed = true;

    /** The set of sessions created by this connection */
    private Set<SessionAdapter> sessions = null;

    /** Flags whether the connection has been destroyed */
    private boolean destroyed = false;

    /** Flags whether the adapter has been closed */
    private boolean closed = false;

    /** Flags whether the connection is in an Application Client Container */
    private boolean inACC = true;

    /* Loggers */
    private static transient final String _className =
            "com.sun.messaging.jms.ra.ConnectionAdapter";
    protected static transient final String _lgrNameJMS = "javax.jms";
    protected static transient final String _lgrNameJMSConnection =
            "javax.jms.Connection.mqjmsra";
    protected static transient final String _lgrNameOutboundConnection =
            "javax.resourceadapter.mqjmsra.outbound.connection";
    protected static transient final Logger _loggerOC =
            Logger.getLogger(_lgrNameOutboundConnection);
    protected static transient final Logger _loggerJMS =
            Logger.getLogger(_lgrNameJMS);
    protected static transient final Logger _loggerJC =
            Logger.getLogger(_lgrNameJMSConnection);
    protected static transient final String _lgrMIDPrefix = "MQJMSRA_CA";
    protected static transient final String _lgrMID_EET = _lgrMIDPrefix + "1001: ";
    protected static transient final String _lgrMID_INF = _lgrMIDPrefix + "1101: ";
    protected static transient final String _lgrMID_WRN = _lgrMIDPrefix + "2001: ";
    protected static transient final String _lgrMID_ERR = _lgrMIDPrefix + "3001: ";
    protected static transient final String _lgrMID_EXC = _lgrMIDPrefix + "4001: ";
 
 
    /** Constructor */
    public ConnectionAdapter(com.sun.messaging.jms.ra.ManagedConnection mc,	
        com.sun.messaging.jmq.jmsclient.XAConnectionImpl xac,
        com.sun.messaging.jms.ra.ResourceAdapter ra)
    {
        Object params[] = new Object[3];
        params[0] = mc;
        params[1] = xac;
        params[2] = ra;

        _loggerOC.entering(_className, "constructor()", params);

        this.mc = mc;
        this.xac = xac;
        this.ra = ra;
        sessions = new HashSet<SessionAdapter>();

        if (ra != null) {
            inACC = ra.getInAppClientContainer();
            if (ra.getInClusteredContainer() && this.getManagedConnection().getManagedConnectionFactory().isUseSharedSubscriptionInClusteredContainer() ) {
                xac.setRANamespaceUID(ra._getRAUID());
            }
        }
        //System.out.println("MQRA:CA:Constructor-inACC="+inACC);
        //xac.dump(System.out);
    } 

    // Methods that implement javax.jms.Connection //
    // Connection control, metadata //

    public void
    setClientID(String clientId)
    throws JMSException
    {
        _loggerJC.entering(_className, "setClientID()", clientId);
        //System.out.println("MQRA:CA:setClientID-to-"+clientId);
        if (!inACC) {
            throw new com.sun.messaging.jms.JMSException("MQRA:CA:Unsupported-setClientID()");
        }
        checkClosed();
        xac.setClientID(clientId);
    }

    public String
    getClientID()
    throws JMSException
    {
        _loggerJC.entering(_className, "getClientID()");
        checkClosed();
        return xac.getClientID();
    }

    public void
    setExceptionListener(ExceptionListener listener)
    throws JMSException
    {
        _loggerJC.entering(_className, "setExceptionListener()", listener);
        if (!inACC) {
            throw new com.sun.messaging.jms.JMSException("MQRA:CA:Unsupported-setClientID()");
        }
        checkClosed();
        xac.setExceptionListener(listener);
    }

    public ExceptionListener
    getExceptionListener()
    throws JMSException
    {
        checkClosed();
        return xac.getExceptionListener();
    }

    public javax.jms.ConnectionMetaData
    getMetaData()
    throws JMSException
    {
        checkClosed();
        return xac.getMetaData();
    }

    public void
    start()
    throws JMSException
    {
        checkClosed();
        xac.start();
    }

    public void
    stop()
    throws JMSException
    {
        if (!inACC) {
            throw new com.sun.messaging.jms.JMSException("MQRA:CA:Unsupported-stop()");
        }
        checkClosed();
        xac.stop();
    }

    public void
    close()
    throws JMSException
    {
        //_loggerOC.fine(_lgrMID_INF+"close():mcId="+mcId+":xacId="+xac._getConnectionID()+":closing CA");
        _loggerOC.fine(_lgrMID_INF+"close():xacId="+xac._getConnectionID()+":clientId="+xac.getClientID());
        //System.out.println("MQRA:CA:close()");
        synchronized (this) {

        if (closed) {
            return;
        }
        closed = true;
        //System.out.println("MQRA:CA:close:xac has clientID="+xac.getClientID());
        if (!xac._isClosed()) {
            xac.stop();
        }
        //Close Sessions
        synchronized (sessions) {
            Iterator it = sessions.iterator();

            while (it.hasNext()) {
                SessionAdapter sa = (SessionAdapter)it.next();
                if (sa != null) {
                    sa.closeAdapter();
                }
            }
            //Clear HashSet
            sessions.clear();
            sessions_allowed = true;
            this.sa = null;
        }
        //System.out.println("MQRA:CA:close:call xac._closeForPooling()");
        try {
            _loggerOC.fine(_lgrMID_INF+"close():xacId="+xac._getConnectionID()+":unsetting clientId");
            xac._closeForPooling();
        } catch (JMSException jmse) {
            System.err.println("MQRA:CA:close:Got JMSExc during _closeForPooling:ignoring:"+jmse.getMessage());
            jmse.printStackTrace();
        }
        //System.out.println("MQRA:CA:close:Sending ConClosedEvent");
        mc.sendEvent(ConnectionEvent.CONNECTION_CLOSED, null, this);
        //closed = true;
        if (false) {
        if (inACC) {
            //System.out.println("MQRA:CA:close:inACC:Sending ConErrEvent");
            mc.sendEvent(ConnectionEvent.CONNECTION_ERROR_OCCURRED, null, this);
        }
        }
        if (false) { // !(inACC) 
            //Force the connection to close if it has a clientID so that the cientID can be re-used
            //try {
                //System.out.println("MQRA:CA:close:xac is closed="+xac._isClosed());
                if (!xac._isClosed() && (xac._getClientID() != null)) {
                    //System.out.println("MQRA:CA:close:Sending ConErrEvent");
                    mc.sendEvent(ConnectionEvent.CONNECTION_ERROR_OCCURRED, null, this);
                }
            //} catch (JMSException jmse) {
                //System.out.println("MQRA:CA:close:Got JMSExc while sending ERROR EVT");
            //}
        }
        }
    }


    // Methods that implement javax.jms.Connection //
    // All ConnectionConsumer methods return UnsupportedOperationException //

    public ConnectionConsumer
    createConnectionConsumer(
        Destination dest,
        String messageSelector,
        ServerSessionPool sessionPool,
        int maxMessages)
    throws JMSException
    {
        UnsupportedOperationException uoex =
            new UnsupportedOperationException("createConnectionConsumer");
        String code = "2";
        checkClosed();
        throw new com.sun.messaging.jms.JMSException("MQRA:CA:Unsupported-createConnectionConsumer", code, uoex);
        //return xac.createConnectionConsumer(dest, messageSelector, sessionPool, maxMessages);
    }
 
    public ConnectionConsumer
    createConnectionConsumer(
        Queue queue,
        String messageSelector,
        ServerSessionPool sessionPool,
        int maxMessages)
    throws JMSException
    {
        UnsupportedOperationException uoex =
            new UnsupportedOperationException("createConnectionConsumer");
        String code = "2";
        checkClosed();
        throw new com.sun.messaging.jms.JMSException("MQRA:CA:Unsupported-createConnectionConsumer", code, uoex);
        //return xac.createConnectionConsumer(queue, messageSelector, sessionPool, maxMessages);
    }
 
    public ConnectionConsumer
    createConnectionConsumer(
        Topic topic,
        String messageSelector,
        ServerSessionPool sessionPool,
        int maxMessages)
    throws JMSException
    {
        UnsupportedOperationException uoex =
            new UnsupportedOperationException("createConnectionConsumer");
        String code = "2";
        checkClosed();
        throw new com.sun.messaging.jms.JMSException("MQRA:CA:Unsupported-createConnectionConsumer", code, uoex);
        //return xac.createConnectionConsumer(topic, messageSelector, sessionPool, maxMessages);
    }
 
    public ConnectionConsumer
    createDurableConnectionConsumer(
        Topic topic,
        String subscriptionName,
        String messageSelector,
        ServerSessionPool sessionPool,   
        int maxMessages)
    throws JMSException
    {
        UnsupportedOperationException uoex =
            new UnsupportedOperationException("createConnectionConsumer");
        String code = "2";
        checkClosed();
        throw new com.sun.messaging.jms.JMSException("MQRA:CA:Unsupported-createDurableConnectionConsumer", code, uoex);
        //return xac.createDurableConnectionConsumer(topic, subscriptionName,
                    //messageSelector, sessionPool, maxMessages);
    }


    // Methods that implement javax.jms.Connection //
    // Session creation  //

    /**
     * Creates a Session object.
     *   
     * @param transacted indicates whether the session is transacted
     * @param acknowledgeMode indicates the acknowledgement mode of the session.
     *        Ignored if the session is transacted. Valid values are
     *        Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE, and
     *        Session.DUPS_OK_ACKNOWLEDGE
     *   
     * @return a newly created Session.
     *   
     * @exception JMSException if a JMS error occurs.
     */  
    public Session
    createSession(boolean transacted, int acknowledgeMode)
    throws JMSException
    {
        //System.out.println("MQRA:CA:createSession()-"+transacted+":"+acknowledgeMode);
        checkClosed();
        if (sessions_allowed == false) {
            throw new com.sun.messaging.jms.JMSException(
                "MQRA:CA:createSession failed-Only one JMS Session allowed when managed connection is involved in a transaction");
        }
        try {
            if (!inACC && mc.xaTransactionActive()) {
                //no more sessions if we are not in the application client container & XA txn is active
                sessions_allowed = true; //false;
            }
        } catch (Exception ee) {
            sessions_allowed = true; //false;
        }
        XASessionImpl xas = (XASessionImpl)xac.createSession(
            (mc.xaTransactionStarted() ? true : transacted), acknowledgeMode, (inACC ? null : mc));
        SessionAdapter sess_adapter = new SessionAdapter(this, xac, xas);
        addSessionAdapter(sess_adapter);
        return sess_adapter;
    }

    /**
     * Creates a QueueSession object.
     *   
     * @param transacted indicates whether the session is transacted
     * @param acknowledgeMode indicates the acknowledgement mode of the session.
     *        Ignored if the session is transacted. Valid values are
     *        Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE, and
     *        Session.DUPS_OK_ACKNOWLEDGE
     *   
     * @return a newly created QueueSession.
     *   
     * @exception JMSException if a JMS error occurs.
     */  
    public QueueSession
    createQueueSession(boolean transacted, int acknowledgeMode)
    throws JMSException
    {
        //System.out.println("MQRA:CA:createQueueSession()-"+transacted+":"+acknowledgeMode);
        checkClosed();
        if (sessions_allowed == false) {
            throw new com.sun.messaging.jms.JMSException(
                "MQRA:CA:createQueueSession failed-Only one JMS Session allowed when managed connection is involved in a transaction");
        }
        try {
            if (!inACC && mc.xaTransactionActive()) {
                //no more sessions if we are not in the application client container & XA txn is active
                sessions_allowed = true; //false;
            }
        } catch (Exception ee) {
            sessions_allowed = true; //false;
        }
        XAQueueSessionImpl xas = (XAQueueSessionImpl)xac.createQueueSession(
            (mc.xaTransactionStarted() ? true : transacted), acknowledgeMode, (inACC ? null : mc));
        SessionAdapter sess_adapter = new SessionAdapter(this, xac, xas);
        sess_adapter.setQueueSession();
        addSessionAdapter(sess_adapter);
        return sess_adapter;
    }

    /**
     * Creates a TopicSession object.
     *   
     * @param transacted indicates whether the session is transacted
     * @param acknowledgeMode indicates the acknowledgement mode of the session.
     *        Ignored if the session is transacted. Valid values are
     *        Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE, and
     *        Session.DUPS_OK_ACKNOWLEDGE
     *   
     * @return a newly created TopicSession.
     *   
     * @exception JMSException if a JMS error occurs.
     */  
    public TopicSession
    createTopicSession(boolean transacted, int acknowledgeMode)
    throws JMSException
    {
        //System.out.println("MQRA:CA:createTopicSession()-"+transacted+":"+acknowledgeMode);
        checkClosed();
        if (sessions_allowed == false) {
            throw new com.sun.messaging.jms.JMSException(
                "MQRA:CA:createTopicSession failed-Only one JMS Session allowed when managed connection is involved in a transaction");
        }
        try {
            if (!inACC && mc.xaTransactionActive()) {
                //no more sessions if we are not in the application client container & XA txn is active
                sessions_allowed = true; //false;
            }
        } catch (Exception ee) {
            sessions_allowed = true; //false;
        }
        XATopicSessionImpl xas = (XATopicSessionImpl)xac.createTopicSession(
            (mc.xaTransactionStarted() ? true : transacted), acknowledgeMode, (inACC ? null : mc));
        SessionAdapter sess_adapter = new SessionAdapter(this, xac, xas);
        sess_adapter.setTopicSession();
        addSessionAdapter(sess_adapter);
        return sess_adapter;
    }

    // check closed and throw IllegalStateException
    void checkClosed()
    throws JMSException
    {
        if (closed) {
            throw new com.sun.messaging.jms.IllegalStateException("MQRA:CA:Illegal:Connection is closed");
        }
    }

    // add / remove SessionAdapter from the set of sessions //

    void addSessionAdapter(SessionAdapter sa)
    {
        synchronized (sessions) {
            sessions.add(sa);
            if (this.sa == null) {
                this.sa = sa;
            }
            if (!inACC) {
                //no more sessions if we are not in the application client container
                sessions_allowed = false;
            }
        } 
    }

    void removeSessionAdapter(SessionAdapter sa)
    {
        synchronized (sessions) {
            sessions.remove(sa);
            //Allow sessions if the only one got closed
            if (sessions_allowed == false && sessions.isEmpty()) {
                sessions_allowed = true;
                this.sa = null;
            }
        }
    }

    public void
    associateManagedConnection(com.sun.messaging.jms.ra.ManagedConnection newmc)
    throws ResourceException
    {
        if (destroyed) {
            System.err.println("MQRA:CA:associateMC:cnxn is destroyed. DebugState="+xac.getDebugState(false).toString());
            throw new javax.resource.spi.IllegalStateException(
                "MQRA:CA:unable to associate ManagedConnection - Connection is destoryed");
        }
        //Switch association from current mc to newmc
        if (newmc != null) {
            this.mc = newmc;
        }
    }

    public void destroy()
    {
        //System.out.println("MQRA:CA:destroy()");
        try {
            xac.close();
        } catch (JMSException jmse) {
            System.err.println("MQRA:CA:destroy:Exception on phys cnxn close-ignoring:"+jmse.getMessage());
        }
    }

    public void
    cleanup()
    {
        //System.out.println("MQRA:CA:cleanup()");
        //Close Sessions
        synchronized (sessions) {
        Iterator it = sessions.iterator();

        while (it.hasNext()) {
            SessionAdapter sa = (SessionAdapter)it.next();
            if (sa != null) {
                //try {
                    sa.closeAdapter();
                //} catch (JMSException jmse) {
                    //System.err.println("MQRA:CA:cleanup():Exception on SessionAdapter close-ignoring"+jmse.getMessage());
                    //jmse.printStackTrace();
                //}
            }
        }
        //Clear HashSet
        sessions.clear();
        sessions_allowed = true;
        this.sa = null;
        }
     }

     protected com.sun.messaging.jms.ra.ResourceAdapter
     getResourceAdapter()
     {
          return ra;
     }

     public SessionAdapter
     getSessionAdapter()
     {
          return sa;
     }

     public com.sun.messaging.jms.ra.ManagedConnection
     getManagedConnection()
     {
         return mc;
     }

     public void
     open(String clientId)
     throws JMSException
     {
         synchronized (this) {
         //System.out.println("MQRA:CA:open():inACC="+inACC+":cid="+clientId);
         if (false) {
         if ("cts".equals(clientId)) {
             if (inACC) {
                 //System.out.println("MQRA:CA:open():inACC:setting-cts1");
                 xac._setClientID("cts1");
             } else {
                 //System.out.println("MQRA:CA:open():NOTinACC:setting-cts2");
                 xac._setClientID("cts2");
             }
         } else {

         xac._setClientID(clientId);

         }
         }
         xac._setClientID(clientId);
         closed = false;
         }
     }

     public void
     open()
     {
         closed = false;
     }
}
