1 17 package org.apache.geronimo.connector.outbound; 18 19 import java.util.TimerTask ; 20 import java.util.ArrayList ; 21 import java.util.Iterator ; 22 import java.util.Timer ; 23 24 import javax.resource.spi.ManagedConnectionFactory ; 25 import javax.resource.spi.ConnectionRequestInfo ; 26 import javax.resource.ResourceException ; 27 import javax.security.auth.Subject ; 28 29 import org.apache.commons.logging.Log; 30 import org.apache.commons.logging.LogFactory; 31 import edu.emory.mathcs.backport.java.util.concurrent.locks.ReadWriteLock; 32 import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantReadWriteLock; 33 import edu.emory.mathcs.backport.java.util.concurrent.Semaphore; 34 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; 35 36 39 public abstract class AbstractSinglePoolConnectionInterceptor implements ConnectionInterceptor, PoolingAttributes { 40 protected static Log log = LogFactory.getLog(AbstractSinglePoolConnectionInterceptor.class.getName()); 41 protected final ConnectionInterceptor next; 42 private final ReadWriteLock resizeLock = new ReentrantReadWriteLock(); 43 protected Semaphore permits; 44 protected int blockingTimeoutMilliseconds; 45 protected int connectionCount = 0; 46 private long idleTimeoutMilliseconds; 47 private IdleReleaser idleReleaser; 48 protected Timer timer = PoolIdleReleaserTimer.getTimer(); 49 protected int maxSize = 0; 50 protected int minSize = 0; 51 protected int shrinkLater = 0; 52 protected volatile boolean destroyed = false; 53 54 public AbstractSinglePoolConnectionInterceptor(final ConnectionInterceptor next, 55 int maxSize, 56 int minSize, 57 int blockingTimeoutMilliseconds, 58 int idleTimeoutMinutes) { 59 this.next = next; 60 this.maxSize = maxSize; 61 this.minSize = minSize; 62 this.blockingTimeoutMilliseconds = blockingTimeoutMilliseconds; 63 setIdleTimeoutMinutes(idleTimeoutMinutes); 64 permits = new Semaphore(maxSize, true); 65 } 66 67 public void getConnection(ConnectionInfo connectionInfo) throws ResourceException { 68 if (connectionInfo.getManagedConnectionInfo().getManagedConnection() != null) { 69 if (log.isTraceEnabled()) { 70 log.trace("using already assigned connection " + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to pool " + this); 71 } 72 return; 73 } 74 try { 75 resizeLock.readLock().lock(); 76 try { 77 if (permits.tryAcquire(blockingTimeoutMilliseconds, TimeUnit.MILLISECONDS)) { 78 internalGetConnection(connectionInfo); 79 } else { 80 throw new ResourceException ("No ManagedConnections available " 81 + "within configured blocking timeout ( " 82 + blockingTimeoutMilliseconds 83 + " [ms] ) for pool " + this); 84 85 } 86 } finally { 87 resizeLock.readLock().unlock(); 88 } 89 90 } catch (InterruptedException ie) { 91 throw new ResourceException ("Interrupted while requesting permit!"); 92 } } 94 95 protected abstract void internalGetConnection(ConnectionInfo connectionInfo) throws ResourceException ; 96 97 public void returnConnection(ConnectionInfo connectionInfo, 98 ConnectionReturnAction connectionReturnAction) { 99 if (log.isTraceEnabled()) { 100 log.trace("returning connection " + connectionInfo.getConnectionHandle() + " for MCI " + connectionInfo.getManagedConnectionInfo() + " and MC " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to pool " + this); 101 } 102 103 if (destroyed) { 105 try { 106 connectionInfo.getManagedConnectionInfo().getManagedConnection().destroy(); 107 } catch (ResourceException re) { 108 } 110 return; 111 } 112 113 resizeLock.readLock().lock(); 114 try { 115 ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo(); 116 if (connectionReturnAction == ConnectionReturnAction.RETURN_HANDLE && mci.hasConnectionHandles()) { 117 if (log.isTraceEnabled()) { 118 log.trace("Return request at pool with connection handles! " + connectionInfo.getConnectionHandle() + " for MCI " + connectionInfo.getManagedConnectionInfo() + " and MC " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to pool " + this, new Exception ("Stack trace")); 119 } 120 return; 121 } 122 123 boolean wasInPool = internalReturn(connectionInfo, connectionReturnAction); 124 125 if (!wasInPool) { 126 permits.release(); 127 } 128 } finally { 129 resizeLock.readLock().unlock(); 130 } 131 } 132 133 protected abstract boolean internalReturn(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction); 134 135 protected abstract void internalDestroy(); 136 137 public void destroy() { 139 destroyed = true; 140 if (idleReleaser != null) 141 idleReleaser.cancel(); 142 internalDestroy(); 143 next.destroy(); 144 } 145 146 public int getPartitionCount() { 147 return 1; 148 } 149 150 public abstract int getPartitionMaxSize(); 151 152 public void setPartitionMaxSize(int newMaxSize) throws InterruptedException { 153 if (newMaxSize <= 0) { 154 throw new IllegalArgumentException ("Max size must be positive, not " + newMaxSize); 155 } 156 if (newMaxSize != getPartitionMaxSize()) { 157 resizeLock.writeLock().lock(); 158 try { 159 ResizeInfo resizeInfo = new ResizeInfo(this.minSize, permits.availablePermits(), connectionCount, newMaxSize); 160 this.shrinkLater = resizeInfo.getShrinkLater(); 161 162 permits = new Semaphore(newMaxSize, true); 163 for (int i = 0; i < resizeInfo.getTransferCheckedOut(); i++) { 165 permits.acquire(); 166 } 167 transferConnections(newMaxSize, resizeInfo.getShrinkNow()); 169 this.minSize = resizeInfo.getNewMinSize(); 170 } finally { 171 resizeLock.writeLock().unlock(); 172 } 173 } 174 } 175 176 177 static final class ResizeInfo { 178 179 private final int newMinSize; 180 private final int shrinkNow; 181 private final int shrinkLater; 182 private final int transferCheckedOut; 183 184 ResizeInfo(final int oldMinSize, final int oldPermitsAvailable, final int oldConnectionCount, final int newMaxSize) { 185 final int checkedOut = oldConnectionCount - oldPermitsAvailable; 186 int shrinkLater = checkedOut - newMaxSize; 187 if (shrinkLater < 0) { 188 shrinkLater = 0; 189 } 190 this.shrinkLater = shrinkLater; 191 int shrinkNow = oldConnectionCount - newMaxSize - shrinkLater; 192 if (shrinkNow < 0) { 193 shrinkNow = 0; 194 } 195 this.shrinkNow = shrinkNow; 196 if (newMaxSize >= oldMinSize) { 197 newMinSize = oldMinSize; 198 } else { 199 newMinSize = newMaxSize; 200 } 201 this.transferCheckedOut = checkedOut - shrinkLater; 202 } 203 204 public int getNewMinSize() { 205 return newMinSize; 206 } 207 208 public int getShrinkNow() { 209 return shrinkNow; 210 } 211 212 public int getShrinkLater() { 213 return shrinkLater; 214 } 215 216 public int getTransferCheckedOut() { 217 return transferCheckedOut; 218 } 219 220 221 } 222 223 protected abstract void transferConnections(int maxSize, int shrinkNow); 224 225 public abstract int getIdleConnectionCount(); 226 227 public int getConnectionCount() { 228 return connectionCount; 229 } 230 231 public int getPartitionMinSize() { 232 return minSize; 233 } 234 235 public void setPartitionMinSize(int minSize) { 236 this.minSize = minSize; 237 } 238 239 public int getBlockingTimeoutMilliseconds() { 240 return blockingTimeoutMilliseconds; 241 } 242 243 public void setBlockingTimeoutMilliseconds(int blockingTimeoutMilliseconds) { 244 if (blockingTimeoutMilliseconds < 0) { 245 throw new IllegalArgumentException ("blockingTimeoutMilliseconds must be positive or 0, not " + blockingTimeoutMilliseconds); 246 } 247 if (blockingTimeoutMilliseconds == 0) { 248 this.blockingTimeoutMilliseconds = Integer.MAX_VALUE; 249 } else { 250 this.blockingTimeoutMilliseconds = blockingTimeoutMilliseconds; 251 } 252 } 253 254 public int getIdleTimeoutMinutes() { 255 return (int) idleTimeoutMilliseconds / (1000 * 60); 256 } 257 258 public void setIdleTimeoutMinutes(int idleTimeoutMinutes) { 259 if (idleTimeoutMinutes < 0) { 260 throw new IllegalArgumentException ("idleTimeoutMinutes must be positive or 0, not " + idleTimeoutMinutes); 261 } 262 if (idleReleaser != null) { 263 idleReleaser.cancel(); 264 } 265 if (idleTimeoutMinutes > 0) { 266 this.idleTimeoutMilliseconds = idleTimeoutMinutes * 60 * 1000; 267 idleReleaser = new IdleReleaser(this); 268 timer.schedule(idleReleaser, this.idleTimeoutMilliseconds, this.idleTimeoutMilliseconds); 269 } 270 } 271 272 protected abstract void getExpiredManagedConnectionInfos(long threshold, ArrayList killList); 273 274 protected abstract boolean addToPool(ManagedConnectionInfo mci); 275 276 private static class IdleReleaser extends TimerTask { 279 private AbstractSinglePoolConnectionInterceptor parent; 280 281 private IdleReleaser(AbstractSinglePoolConnectionInterceptor parent) { 282 this.parent = parent; 283 } 284 285 public boolean cancel() { 286 this.parent = null; 287 return super.cancel(); 288 } 289 290 public void run() { 291 AbstractSinglePoolConnectionInterceptor interceptor = parent; 293 if (interceptor == null) 294 return; 295 296 interceptor.resizeLock.readLock().lock(); 297 try { 298 long threshold = System.currentTimeMillis() - interceptor.idleTimeoutMilliseconds; 299 ArrayList killList = new ArrayList (interceptor.getPartitionMaxSize()); 300 interceptor.getExpiredManagedConnectionInfos(threshold, killList); 301 for (Iterator i = killList.iterator(); i.hasNext();) { 302 ManagedConnectionInfo managedConnectionInfo = (ManagedConnectionInfo) i.next(); 303 ConnectionInfo killInfo = new ConnectionInfo(managedConnectionInfo); 304 interceptor.internalReturn(killInfo, ConnectionReturnAction.DESTROY); 305 } 306 interceptor.permits.release(killList.size()); 307 } finally { 308 interceptor.resizeLock.readLock().unlock(); 309 } 310 } 311 312 } 313 314 protected class FillTask extends TimerTask { 317 private final ManagedConnectionFactory managedConnectionFactory; 318 private final Subject subject; 319 private final ConnectionRequestInfo cri; 320 321 public FillTask(ConnectionInfo connectionInfo) { 322 managedConnectionFactory = connectionInfo.getManagedConnectionInfo().getManagedConnectionFactory(); 323 subject = connectionInfo.getManagedConnectionInfo().getSubject(); 324 cri = connectionInfo.getManagedConnectionInfo().getConnectionRequestInfo(); 325 } 326 327 public void run() { 328 resizeLock.readLock().lock(); 329 try { 330 while (connectionCount < minSize) { 331 ManagedConnectionInfo mci = new ManagedConnectionInfo(managedConnectionFactory, cri); 332 mci.setSubject(subject); 333 ConnectionInfo ci = new ConnectionInfo(mci); 334 try { 335 next.getConnection(ci); 336 } catch (ResourceException e) { 337 return; 338 } 339 boolean added = addToPool(mci); 340 if (!added) { 341 internalReturn(ci, ConnectionReturnAction.DESTROY); 342 return; 343 } 344 } 345 } finally { 346 resizeLock.readLock().unlock(); 347 } 348 } 349 350 } 351 } 352 | Popular Tags |