/**
 *
 * Copyright 2004 Hiram Chirino
 *
 * Licensed 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.codehaus.activemq.ra;

import org.codehaus.activemq.ActiveMQSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.jms.ServerSessionPool;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.XASession;
import javax.jms.ServerSession;
import javax.transaction.xa.XAResource;
import javax.resource.spi.endpoint.MessageEndpoint;
import javax.resource.spi.UnavailableException;
import java.util.ArrayList;
import java.util.LinkedList;

/**
 * @version $Revision: 1.1.2.1 $ $Date: 2004/12/14 16:30:53 $
 */
public class ServerSessionPoolImpl implements ServerSessionPool {
    
    private static final Log log = LogFactory.getLog(ServerSessionPoolImpl.class);

    private static final int MAX_SESSION = 10;

    private ServerSessionImpl ss;

    private ArrayList idleSessions = new ArrayList();

    private LinkedList activeSessions = new LinkedList();

    private boolean closing = false;
    private ActiveMQAsfEndpointWorker activeMQAsfEndpointWorker;

    public ServerSessionPoolImpl(ActiveMQAsfEndpointWorker activeMQAsfEndpointWorker) {
        this.activeMQAsfEndpointWorker = activeMQAsfEndpointWorker;
    }

    private ServerSessionImpl createServerSessionImpl() throws JMSException {
        int acknowledge = (activeMQAsfEndpointWorker.transacted) ? Session.SESSION_TRANSACTED : activeMQAsfEndpointWorker.endpointActivationKey.getActivationSpec().getAcknowledgeModeForSession();
        final Session session = activeMQAsfEndpointWorker.connection.createSession(activeMQAsfEndpointWorker.transacted,acknowledge);            
        final XAResource xaResource = (session instanceof XASession) ? ((XASession) session).getXAResource() : null;
        MessageEndpoint endpoint;
        try {
                
            // Give the endpoint a LocalTransaction and XAResource interface to manage the tx.
            TxResourceProxy txResourceProxy = new TxResourceProxy(){
                Session getSession() {
                    return session;
                }

                XAResource getXAResource() {
                    return xaResource;
                }
            };
            endpoint = createEndpoint(txResourceProxy);
        } catch (UnavailableException e) {
            // The container could be limiting us on the number of endpoints
            // that are being created.
            session.close();
            return null;
        }
        return new ServerSessionImpl(this, (ActiveMQSession)session, activeMQAsfEndpointWorker.workManager, endpoint);
    }

    private MessageEndpoint createEndpoint(TxResourceProxy txResourceProxy) throws UnavailableException {
        MessageEndpoint endpoint;
        endpoint = activeMQAsfEndpointWorker.endpointFactory.createEndpoint(txResourceProxy);
        MessageEndpointProxy endpointProxy = new MessageEndpointProxy(endpoint);
        return endpointProxy;
    }

    /**
     */
    synchronized public ServerSession getServerSession() throws JMSException {
        log.debug("ServerSession requested.");
        if (closing) {
            throw new JMSException("Session Pool Shutting Down.");
        }

        if (idleSessions.size() > 0) {
            ServerSessionImpl ss = (ServerSessionImpl) idleSessions.remove(idleSessions.size() - 1);
            activeSessions.addLast(ss);
            log.debug("Using idle session: " + ss);
            return ss;
        } else {
            // Are we at the upper limit?
            if (activeSessions.size() >= MAX_SESSION) {
                // then reuse the allready created sessions..
                // This is going to queue up messages into a session for
                // processing.
                return getExistingServerSession();
            }
            ServerSessionImpl ss = createServerSessionImpl();
            // We may not be able to create a session due to the conatiner
            // restricting us.
            if (ss == null) {
                return getExistingServerSession();
            }
            activeSessions.addLast(ss);
            log.debug("Created a new session: " + ss);
            return ss;
        }
    }

    /**
     * @return
     */
    private ServerSession getExistingServerSession() {
        ServerSessionImpl ss = (ServerSessionImpl) activeSessions.removeFirst();
        activeSessions.addLast(ss);
        log.debug("Reusing an active session: " + ss);
        return ss;
    }

    synchronized public void returnToPool(ServerSessionImpl ss) {
        log.debug("Session returned to pool: " + ss);
        idleSessions.add(ss);
    }

    public void close() {
        synchronized (this) {
            closing = true;
        }
    }
}
