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    
029    import org.apache.activemq.ActiveMQConnection;
030    import org.apache.activemq.transport.TransportListener;
031    import org.apache.commons.pool.ObjectPoolFactory;
032    
033    /**
034     * Holds a real JMS connection along with the session pools associated with it.
035     * 
036     * @version $Revision: 956541 $
037     */
038    public class ConnectionPool {
039    
040        private ActiveMQConnection connection;
041        private Map<SessionKey, SessionPool> cache;
042        private AtomicBoolean started = new AtomicBoolean(false);
043        private int referenceCount;
044        private ObjectPoolFactory poolFactory;
045        private long lastUsed = System.currentTimeMillis();
046        private long firstUsed = lastUsed;
047        private boolean hasFailed;
048        private boolean hasExpired;
049        private int idleTimeout = 30 * 1000;
050        private long expiryTimeout = 0l;
051    
052        public ConnectionPool(ActiveMQConnection connection, ObjectPoolFactory poolFactory) {
053            this(connection, new HashMap<SessionKey, SessionPool>(), poolFactory);
054            // Add a transport Listener so that we can notice if this connection
055            // should be expired due to
056            // a connection failure.
057            connection.addTransportListener(new TransportListener() {
058                public void onCommand(Object command) {
059                }
060    
061                public void onException(IOException error) {
062                    synchronized (ConnectionPool.this) {
063                        System.err.println("HasFaile=true on :" + error);
064                        Thread.dumpStack();
065                        hasFailed = true;
066                    }
067                }
068    
069                public void transportInterupted() {
070                }
071    
072                public void transportResumed() {
073                }
074            });       
075            //
076            // make sure that we set the hasFailed flag, in case the transport already failed
077            // prior to the addition of our new TransportListener
078            //
079            if(connection.isTransportFailed()) {
080                hasFailed = true;
081            }
082        }
083    
084        public ConnectionPool(ActiveMQConnection connection, Map<SessionKey, SessionPool> cache, ObjectPoolFactory poolFactory) {
085            this.connection = connection;
086            this.cache = cache;
087            this.poolFactory = poolFactory;
088        }
089    
090        public void start() throws JMSException {
091            if (started.compareAndSet(false, true)) {
092                    try {
093                            connection.start();
094                    } catch (JMSException e) {
095                            started.set(false);
096                            throw(e);
097                    }
098            }
099        }
100    
101        public synchronized ActiveMQConnection getConnection() {
102            return connection;
103        }
104    
105        public Session createSession(boolean transacted, int ackMode) throws JMSException {
106            SessionKey key = new SessionKey(transacted, ackMode);
107            SessionPool pool = cache.get(key);
108            if (pool == null) {
109                pool = createSessionPool(key);
110                cache.put(key, pool);
111            }
112            PooledSession session = pool.borrowSession();
113            return session;
114        }
115    
116        public synchronized void close() {
117            if (connection != null) {
118                try {
119                    Iterator<SessionPool> i = cache.values().iterator();
120                    while (i.hasNext()) {
121                        SessionPool pool = i.next();
122                        i.remove();
123                        try {
124                            pool.close();
125                        } catch (Exception e) {
126                        }
127                    }
128                } finally {
129                    try {
130                        connection.close();
131                    } catch (Exception e) {
132                    } finally {
133                        connection = null;
134                    }
135                }
136            }
137        }
138    
139        public synchronized void incrementReferenceCount() {
140            referenceCount++;
141            lastUsed = System.currentTimeMillis();
142        }
143    
144        public synchronized void decrementReferenceCount() {
145            referenceCount--;
146            lastUsed = System.currentTimeMillis();
147            if (referenceCount == 0) {
148                expiredCheck();
149            }
150        }
151    
152        /**
153         * @return true if this connection has expired.
154         */
155        public synchronized boolean expiredCheck() {
156            if (connection == null) {
157                return true;
158            }
159            if (hasExpired) {
160                if (referenceCount == 0) {
161                    close();
162                }
163                return true;
164            }
165            if (hasFailed 
166                    || (idleTimeout > 0 && System.currentTimeMillis() > lastUsed + idleTimeout)
167                    || expiryTimeout > 0 && System.currentTimeMillis() > firstUsed + expiryTimeout) {
168                hasExpired = true;
169                if (referenceCount == 0) {
170                    close();
171                }
172                return true;
173            }
174            return false;
175        }
176    
177        public int getIdleTimeout() {
178            return idleTimeout;
179        }
180    
181        public void setIdleTimeout(int idleTimeout) {
182            this.idleTimeout = idleTimeout;
183        }
184    
185        protected SessionPool createSessionPool(SessionKey key) {
186            return new SessionPool(this, key, poolFactory.createPool());
187        }
188    
189        public void setExpiryTimeout(long expiryTimeout) {
190            this.expiryTimeout  = expiryTimeout;
191        }
192        
193        public long getExpiryTimeout() {
194            return expiryTimeout;
195        }
196    
197    }