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.geronimo.connector.outbound;
019
020 import java.util.ArrayList;
021 import java.util.HashMap;
022 import java.util.Iterator;
023 import java.util.Map;
024
025 import javax.resource.ResourceException;
026 import javax.resource.spi.ManagedConnection;
027 import javax.resource.spi.ManagedConnectionFactory;
028
029 /**
030 * This pool is the most spec-compliant pool. It can be used by itself with no partitioning.
031 * It is apt to be the slowest pool.
032 * For each connection request, it synchronizes access to the pool and asks the
033 * ManagedConnectionFactory for a match from among all managed connections. If none is found,
034 * it may discard a random existing connection, and creates a new connection.
035 *
036 * @version $Rev: 550546 $ $Date: 2007-06-25 12:52:11 -0400 (Mon, 25 Jun 2007) $
037 */
038 public class SinglePoolMatchAllConnectionInterceptor extends AbstractSinglePoolConnectionInterceptor {
039
040 private HashMap pool;
041
042 private int maxSize;
043
044 public SinglePoolMatchAllConnectionInterceptor(final ConnectionInterceptor next,
045 int maxSize,
046 int minSize,
047 int blockingTimeoutMilliseconds,
048 int idleTimeoutMinutes) {
049
050 super(next, maxSize, minSize, blockingTimeoutMilliseconds, idleTimeoutMinutes);
051 this.maxSize = maxSize;
052 pool = new HashMap(maxSize);
053 }
054
055 protected void internalGetConnection(ConnectionInfo connectionInfo) throws ResourceException {
056 synchronized (pool) {
057 if (destroyed) {
058 throw new ResourceException("ManagedConnection pool has been destroyed");
059 }
060 try {
061 if (!pool.isEmpty()) {
062 ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
063 ManagedConnectionFactory managedConnectionFactory = mci.getManagedConnectionFactory();
064 ManagedConnection matchedMC =
065 managedConnectionFactory
066 .matchManagedConnections(pool.keySet(),
067 mci.getSubject(),
068 mci.getConnectionRequestInfo());
069 if (matchedMC != null) {
070 connectionInfo.setManagedConnectionInfo((ManagedConnectionInfo) pool.get(matchedMC));
071 pool.remove(matchedMC);
072 if (log.isTraceEnabled()) {
073 log.trace("Returning pooled connection " + connectionInfo.getManagedConnectionInfo());
074 }
075 if (connectionCount < minSize) {
076 timer.schedule(new FillTask(connectionInfo), 10);
077 }
078 return;
079 }
080 }
081 //matching failed or pool is empty
082 //if pool is at maximum size, pick a cx to kill
083 if (connectionCount == maxSize) {
084 Iterator iterator = pool.entrySet().iterator();
085 ManagedConnectionInfo kill = (ManagedConnectionInfo) ((Map.Entry) iterator.next()).getValue();
086 iterator.remove();
087 ConnectionInfo killInfo = new ConnectionInfo(kill);
088 internalReturn(killInfo, ConnectionReturnAction.DESTROY);
089 }
090 next.getConnection(connectionInfo);
091 connectionCount++;
092 if (log.isTraceEnabled()) {
093 log.trace("Returning new connection " + connectionInfo.getManagedConnectionInfo());
094 }
095 if (connectionCount < minSize) {
096 timer.schedule(new FillTask(connectionInfo), 10);
097 }
098
099 } catch (ResourceException e) {
100 //something is wrong: rethrow, release permit
101 permits.release();
102 throw e;
103 }
104 }
105 }
106
107 protected boolean internalReturn(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction) {
108 ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
109 ManagedConnection mc = mci.getManagedConnection();
110 try {
111 mc.cleanup();
112 } catch (ResourceException e) {
113 connectionReturnAction = ConnectionReturnAction.DESTROY;
114 }
115
116 boolean wasInPool = false;
117 synchronized (pool) {
118 // a bit redundant, but this closes a small timing hole...
119 if (destroyed) {
120 try {
121 mc.destroy();
122 }
123 catch (ResourceException re) { } // ignore
124 return pool.remove(mci.getManagedConnection()) != null;
125 }
126 if (shrinkLater > 0) {
127 //nothing can get in the pool while shrinkLater > 0, so wasInPool is false here.
128 connectionReturnAction = ConnectionReturnAction.DESTROY;
129 shrinkLater--;
130 } else if (connectionReturnAction == ConnectionReturnAction.RETURN_HANDLE) {
131 mci.setLastUsed(System.currentTimeMillis());
132 pool.put(mci.getManagedConnection(), mci);
133 return wasInPool;
134 } else {
135 wasInPool = pool.remove(mci.getManagedConnection()) != null;
136 }
137 }
138 //we must destroy connection.
139 next.returnConnection(connectionInfo, connectionReturnAction);
140 connectionCount--;
141 return wasInPool;
142 }
143
144 protected void internalDestroy() {
145 synchronized (pool) {
146 Iterator it = pool.keySet().iterator();
147 for (; it.hasNext(); ) {
148 try {
149 ((ManagedConnection)it.next()).destroy();
150 }
151 catch (ResourceException re) { } // ignore
152 it.remove();
153 }
154 }
155 }
156
157 public int getPartitionMaxSize() {
158 return maxSize;
159 }
160
161 public int getIdleConnectionCount() {
162 return pool.size();
163 }
164
165 protected void transferConnections(int maxSize, int shrinkNow) {
166 //1st example: copy 0 (none)
167 //2nd example: copy 10 (all)
168 HashMap oldPool = pool;
169 pool = new HashMap(maxSize);
170 //since we have replaced pool already, pool.remove will be very fast:-)
171 assert oldPool.size() == connectionCount;
172 Iterator it = oldPool.entrySet().iterator();
173 for (int i = 0; i < shrinkNow; i++) {
174 ConnectionInfo killInfo = new ConnectionInfo((ManagedConnectionInfo) ((Map.Entry)it.next()).getValue());
175 internalReturn(killInfo, ConnectionReturnAction.DESTROY);
176 }
177 for (; it.hasNext(); ) {
178 Map.Entry entry = (Map.Entry) it.next();
179 pool.put(entry.getKey(), entry.getValue());
180 }
181
182 }
183
184 protected void getExpiredManagedConnectionInfos(long threshold, ArrayList killList) {
185 synchronized (pool) {
186 for (Iterator iterator = pool.entrySet().iterator(); iterator.hasNext();) {
187 Map.Entry entry = (Map.Entry) iterator.next();
188 ManagedConnectionInfo mci = (ManagedConnectionInfo) entry.getValue();
189 if (mci.getLastUsed() < threshold) {
190 killList.add(mci);
191 }
192 }
193 }
194
195 }
196
197 protected boolean addToPool(ManagedConnectionInfo mci) {
198 boolean added;
199 synchronized (pool) {
200 connectionCount++;
201 added = getPartitionMaxSize() > getIdleConnectionCount();
202 if (added) {
203 pool.put(mci.getManagedConnection(), mci);
204 }
205 }
206 return added;
207 }
208
209 }