001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.activemq.pool;
019    
020    import java.io.IOException;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    import java.util.Map;
024    import java.util.concurrent.atomic.AtomicBoolean;
025    
026    import javax.jms.JMSException;
027    import javax.jms.Session;
028    import javax.transaction.RollbackException;
029    import javax.transaction.Status;
030    import javax.transaction.SystemException;
031    import javax.transaction.TransactionManager;
032    import javax.transaction.xa.XAResource;
033    
034    import org.apache.activemq.ActiveMQConnection;
035    import org.apache.activemq.transport.TransportListener;
036    import org.apache.commons.pool.ObjectPoolFactory;
037    
038    /**
039     * Holds a real JMS connection along with the session pools associated with it.
040     * 
041     * @version $Revision$
042     */
043    public class ConnectionPool {
044    
045        private ActiveMQConnection connection;
046        private Map<SessionKey, SessionPool> cache;
047        private AtomicBoolean started = new AtomicBoolean(false);
048        private int referenceCount;
049        private ObjectPoolFactory poolFactory;
050        private long lastUsed = System.currentTimeMillis();
051        private boolean hasFailed;
052        private boolean hasExpired;
053        private int idleTimeout = 30 * 1000;
054    
055        public ConnectionPool(ActiveMQConnection connection, ObjectPoolFactory poolFactory) {
056            this(connection, new HashMap<SessionKey, SessionPool>(), poolFactory);
057            // Add a transport Listener so that we can notice if this connection
058            // should be expired due to
059            // a connection failure.
060            connection.addTransportListener(new TransportListener() {
061                public void onCommand(Object command) {
062                }
063    
064                public void onException(IOException error) {
065                    synchronized (ConnectionPool.this) {
066                        hasFailed = true;
067                    }
068                }
069    
070                public void transportInterupted() {
071                }
072    
073                public void transportResumed() {
074                }
075            });       
076            //
077            // make sure that we set the hasFailed flag, in case the transport already failed
078            // prior to the addition of our new TransportListener
079            //
080            if(connection.isTransportFailed()) {
081                hasFailed = true;
082            }
083        }
084    
085        public ConnectionPool(ActiveMQConnection connection, Map<SessionKey, SessionPool> cache, ObjectPoolFactory poolFactory) {
086            this.connection = connection;
087            this.cache = cache;
088            this.poolFactory = poolFactory;
089        }
090    
091        public void start() throws JMSException {
092            if (started.compareAndSet(false, true)) {
093                    try {
094                            connection.start();
095                    } catch (JMSException e) {
096                            started.set(false);
097                            throw(e);
098                    }
099            }
100        }
101    
102        public synchronized ActiveMQConnection getConnection() {
103            return connection;
104        }
105    
106        public Session createSession(boolean transacted, int ackMode) throws JMSException {
107            SessionKey key = new SessionKey(transacted, ackMode);
108            SessionPool pool = cache.get(key);
109            if (pool == null) {
110                pool = createSessionPool(key);
111                cache.put(key, pool);
112            }
113            PooledSession session = pool.borrowSession();
114            return session;
115        }
116    
117        public synchronized void close() {
118            if (connection != null) {
119                try {
120                    Iterator<SessionPool> i = cache.values().iterator();
121                    while (i.hasNext()) {
122                        SessionPool pool = i.next();
123                        i.remove();
124                        try {
125                            pool.close();
126                        } catch (Exception e) {
127                        }
128                    }
129                } finally {
130                    try {
131                        connection.close();
132                    } catch (Exception e) {
133                    } finally {
134                        connection = null;
135                    }
136                }
137            }
138        }
139    
140        public synchronized void incrementReferenceCount() {
141            referenceCount++;
142            lastUsed = System.currentTimeMillis();
143        }
144    
145        public synchronized void decrementReferenceCount() {
146            referenceCount--;
147            lastUsed = System.currentTimeMillis();
148            if (referenceCount == 0) {
149                expiredCheck();
150            }
151        }
152    
153        /**
154         * @return true if this connection has expired.
155         */
156        public synchronized boolean expiredCheck() {
157            if (connection == null) {
158                return true;
159            }
160            if (hasExpired) {
161                if (referenceCount == 0) {
162                    close();
163                }
164                return true;
165            }
166            if (hasFailed || (idleTimeout > 0 && System.currentTimeMillis() > lastUsed + idleTimeout)) {
167                hasExpired = true;
168                if (referenceCount == 0) {
169                    close();
170                }
171                return true;
172            }
173            return false;
174        }
175    
176        public int getIdleTimeout() {
177            return idleTimeout;
178        }
179    
180        public void setIdleTimeout(int idleTimeout) {
181            this.idleTimeout = idleTimeout;
182        }
183    
184        protected SessionPool createSessionPool(SessionKey key) {
185            return new SessionPool(this, key, poolFactory.createPool());
186        }
187    
188    }