| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.geronimo.connector.outbound; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import javax.resource.ResourceException; |
| import javax.resource.spi.ManagedConnection; |
| import javax.resource.spi.ManagedConnectionFactory; |
| |
| /** |
| * This pool is the most spec-compliant pool. It can be used by itself with no partitioning. |
| * It is apt to be the slowest pool. |
| * For each connection request, it synchronizes access to the pool and asks the |
| * ManagedConnectionFactory for a match from among all managed connections. If none is found, |
| * it may discard a random existing connection, and creates a new connection. |
| * |
| * @version $Rev$ $Date$ |
| */ |
| public class SinglePoolMatchAllConnectionInterceptor extends AbstractSinglePoolConnectionInterceptor { |
| |
| private HashMap pool; |
| |
| private int maxSize; |
| |
| public SinglePoolMatchAllConnectionInterceptor(final ConnectionInterceptor next, |
| int maxSize, |
| int minSize, |
| int blockingTimeoutMilliseconds, |
| int idleTimeoutMinutes) { |
| |
| super(next, maxSize, minSize, blockingTimeoutMilliseconds, idleTimeoutMinutes); |
| this.maxSize = maxSize; |
| pool = new HashMap(maxSize); |
| } |
| |
| protected void internalGetConnection(ConnectionInfo connectionInfo) throws ResourceException { |
| synchronized (pool) { |
| if (destroyed) { |
| throw new ResourceException("ManagedConnection pool has been destroyed"); |
| } |
| try { |
| if (!pool.isEmpty()) { |
| ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo(); |
| ManagedConnectionFactory managedConnectionFactory = mci.getManagedConnectionFactory(); |
| ManagedConnection matchedMC = |
| managedConnectionFactory |
| .matchManagedConnections(pool.keySet(), |
| mci.getSubject(), |
| mci.getConnectionRequestInfo()); |
| if (matchedMC != null) { |
| connectionInfo.setManagedConnectionInfo((ManagedConnectionInfo) pool.get(matchedMC)); |
| pool.remove(matchedMC); |
| if (log.isTraceEnabled()) { |
| log.trace("Returning pooled connection " + connectionInfo.getManagedConnectionInfo()); |
| } |
| if (connectionCount < minSize) { |
| timer.schedule(new FillTask(connectionInfo), 10); |
| } |
| return; |
| } |
| } |
| //matching failed or pool is empty |
| //if pool is at maximum size, pick a cx to kill |
| if (connectionCount == maxSize) { |
| Iterator iterator = pool.entrySet().iterator(); |
| ManagedConnectionInfo kill = (ManagedConnectionInfo) ((Map.Entry) iterator.next()).getValue(); |
| iterator.remove(); |
| ConnectionInfo killInfo = new ConnectionInfo(kill); |
| internalReturn(killInfo, ConnectionReturnAction.DESTROY); |
| } |
| next.getConnection(connectionInfo); |
| connectionCount++; |
| if (log.isTraceEnabled()) { |
| log.trace("Returning new connection " + connectionInfo.getManagedConnectionInfo()); |
| } |
| if (connectionCount < minSize) { |
| timer.schedule(new FillTask(connectionInfo), 10); |
| } |
| |
| } catch (ResourceException e) { |
| //something is wrong: rethrow, release permit |
| permits.release(); |
| throw e; |
| } |
| } |
| } |
| |
| protected boolean internalReturn(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction) { |
| ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo(); |
| ManagedConnection mc = mci.getManagedConnection(); |
| try { |
| mc.cleanup(); |
| } catch (ResourceException e) { |
| connectionReturnAction = ConnectionReturnAction.DESTROY; |
| } |
| |
| boolean wasInPool = false; |
| synchronized (pool) { |
| // a bit redundant, but this closes a small timing hole... |
| if (destroyed) { |
| try { |
| mc.destroy(); |
| } |
| catch (ResourceException re) { } // ignore |
| return pool.remove(mci.getManagedConnection()) != null; |
| } |
| if (shrinkLater > 0) { |
| //nothing can get in the pool while shrinkLater > 0, so wasInPool is false here. |
| connectionReturnAction = ConnectionReturnAction.DESTROY; |
| shrinkLater--; |
| } else if (connectionReturnAction == ConnectionReturnAction.RETURN_HANDLE) { |
| mci.setLastUsed(System.currentTimeMillis()); |
| pool.put(mci.getManagedConnection(), mci); |
| return wasInPool; |
| } else { |
| wasInPool = pool.remove(mci.getManagedConnection()) != null; |
| } |
| } |
| //we must destroy connection. |
| next.returnConnection(connectionInfo, connectionReturnAction); |
| connectionCount--; |
| return wasInPool; |
| } |
| |
| protected void internalDestroy() { |
| synchronized (pool) { |
| Iterator it = pool.keySet().iterator(); |
| for (; it.hasNext(); ) { |
| try { |
| ((ManagedConnection)it.next()).destroy(); |
| } |
| catch (ResourceException re) { } // ignore |
| it.remove(); |
| } |
| } |
| } |
| |
| public int getPartitionMaxSize() { |
| return maxSize; |
| } |
| |
| public int getIdleConnectionCount() { |
| return pool.size(); |
| } |
| |
| protected void transferConnections(int maxSize, int shrinkNow) { |
| //1st example: copy 0 (none) |
| //2nd example: copy 10 (all) |
| HashMap oldPool = pool; |
| pool = new HashMap(maxSize); |
| //since we have replaced pool already, pool.remove will be very fast:-) |
| assert oldPool.size() == connectionCount; |
| Iterator it = oldPool.entrySet().iterator(); |
| for (int i = 0; i < shrinkNow; i++) { |
| ConnectionInfo killInfo = new ConnectionInfo((ManagedConnectionInfo) ((Map.Entry)it.next()).getValue()); |
| internalReturn(killInfo, ConnectionReturnAction.DESTROY); |
| } |
| for (; it.hasNext(); ) { |
| Map.Entry entry = (Map.Entry) it.next(); |
| pool.put(entry.getKey(), entry.getValue()); |
| } |
| |
| } |
| |
| protected void getExpiredManagedConnectionInfos(long threshold, ArrayList killList) { |
| synchronized (pool) { |
| for (Iterator iterator = pool.entrySet().iterator(); iterator.hasNext();) { |
| Map.Entry entry = (Map.Entry) iterator.next(); |
| ManagedConnectionInfo mci = (ManagedConnectionInfo) entry.getValue(); |
| if (mci.getLastUsed() < threshold) { |
| killList.add(mci); |
| } |
| } |
| } |
| |
| } |
| |
| protected boolean addToPool(ManagedConnectionInfo mci) { |
| boolean added; |
| synchronized (pool) { |
| connectionCount++; |
| added = getPartitionMaxSize() > getIdleConnectionCount(); |
| if (added) { |
| pool.put(mci.getManagedConnection(), mci); |
| } |
| } |
| return added; |
| } |
| |
| } |