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 }