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 }