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.Collections;
022 import java.util.List;
023
024 import javax.resource.ResourceException;
025 import javax.resource.spi.ManagedConnection;
026
027 import org.apache.commons.logging.Log;
028 import org.apache.commons.logging.LogFactory;
029
030 /**
031 * SinglePoolConnectionInterceptor chooses a single connection from the pool. If selectOneAssumeMatch
032 * is true, it simply returns the selected connection.
033 * THIS SHOULD BE USED ONLY IF MAXIMUM SPEED IS ESSENTIAL AND YOU HAVE THOROUGLY CHECKED THAT
034 * MATCHING WOULD SUCCEED ON THE SELECTED CONNECTION. (i.e., read the docs on your connector
035 * to find out how matching works)
036 * If selectOneAssumeMatch is false, it checks with the ManagedConnectionFactory that the
037 * selected connection does match before returning it: if not it throws an exception.
038 *
039 * @version $Rev: 620213 $ $Date: 2008-02-10 00:13:09 +0100 (Sun, 10 Feb 2008) $
040 */
041 public class SinglePoolConnectionInterceptor extends AbstractSinglePoolConnectionInterceptor {
042 private static final Log log = LogFactory.getLog(SinglePoolConnectionInterceptor.class.getName());
043
044 private boolean selectOneAssumeMatch;
045
046 //pool is mutable but only changed when protected by write lock on resizelock in superclass
047 // private PoolDeque pool;
048 private final List<ManagedConnectionInfo> pool;
049
050 public SinglePoolConnectionInterceptor(final ConnectionInterceptor next,
051 int maxSize,
052 int minSize,
053 int blockingTimeoutMilliseconds,
054 int idleTimeoutMinutes,
055 boolean selectOneAssumeMatch) {
056 super(next, maxSize, minSize, blockingTimeoutMilliseconds, idleTimeoutMinutes);
057 // pool = new PoolDeque(maxSize);
058 pool = new ArrayList<ManagedConnectionInfo>(maxSize);
059 this.selectOneAssumeMatch = selectOneAssumeMatch;
060 }
061
062 protected void internalGetConnection(ConnectionInfo connectionInfo) throws ResourceException {
063 synchronized (pool) {
064 if (destroyed) {
065 throw new ResourceException("ManagedConnection pool has been destroyed");
066 }
067
068 ManagedConnectionInfo newMCI;
069 if (pool.isEmpty()) {
070 next.getConnection(connectionInfo);
071 connectionCount++;
072 if (log.isTraceEnabled()) {
073 log.trace("Supplying new connection MCI: " + connectionInfo.getManagedConnectionInfo() + " MC: " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " from pool: " + this);
074 }
075 return;
076 } else {
077 newMCI = pool.remove(pool.size() - 1);
078 }
079 if (connectionCount < minSize) {
080 timer.schedule(new FillTask(connectionInfo), 10);
081 }
082 if (selectOneAssumeMatch) {
083 connectionInfo.setManagedConnectionInfo(newMCI);
084 if (log.isTraceEnabled()) {
085 log.trace("Supplying pooled connection without checking matching MCI: " + connectionInfo.getManagedConnectionInfo() + " MC: " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " from pool: " + this);
086 }
087 return;
088 }
089 try {
090 ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
091 ManagedConnection matchedMC = newMCI.getManagedConnectionFactory().matchManagedConnections(Collections.singleton(newMCI.getManagedConnection()),
092 mci.getSubject(),
093 mci.getConnectionRequestInfo());
094 if (matchedMC != null) {
095 connectionInfo.setManagedConnectionInfo(newMCI);
096 if (log.isTraceEnabled()) {
097 log.trace("Supplying pooled connection MCI: " + connectionInfo.getManagedConnectionInfo() + " MC: " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " from pool: " + this);
098 }
099 } else {
100 //matching failed.
101 ConnectionInfo returnCI = new ConnectionInfo();
102 returnCI.setManagedConnectionInfo(newMCI);
103 returnConnection(returnCI, ConnectionReturnAction.RETURN_HANDLE);
104 throw new ResourceException("The pooling strategy does not match the MatchManagedConnections implementation. Please investigate and reconfigure this pool");
105 }
106 } catch (ResourceException e) {
107 //something is wrong: destroy connection, rethrow, release permit
108 ConnectionInfo returnCI = new ConnectionInfo();
109 returnCI.setManagedConnectionInfo(newMCI);
110 returnConnection(returnCI, ConnectionReturnAction.DESTROY);
111 throw e;
112 }
113 }
114 }
115
116 protected void internalDestroy() {
117 synchronized (pool) {
118 while (!pool.isEmpty()) {
119 ManagedConnection mc = pool.remove(pool.size() - 1).getManagedConnection();
120 if (mc != null) {
121 try {
122 mc.destroy();
123 }
124 catch (ResourceException re) {
125 //ignore
126 }
127 }
128 }
129 }
130 }
131
132 protected Object getPool() {
133 return pool;
134 }
135
136 protected void doAdd(ManagedConnectionInfo mci) {
137 pool.add(mci);
138 }
139
140 protected boolean doRemove(ManagedConnectionInfo mci) {
141 return !pool.remove(mci);
142 }
143
144 protected void transferConnections(int maxSize, int shrinkNow) {
145 for (int i = 0; i < shrinkNow; i++) {
146 ConnectionInfo killInfo = new ConnectionInfo(pool.get(0));
147 internalReturn(killInfo, ConnectionReturnAction.DESTROY);
148 }
149 }
150
151 public int getIdleConnectionCount() {
152 return pool.size();
153 }
154
155
156 protected void getExpiredManagedConnectionInfos(long threshold, List<ManagedConnectionInfo> killList) {
157 synchronized (pool) {
158 for (ManagedConnectionInfo mci : pool) {
159 if (mci.getLastUsed() < threshold) {
160 killList.add(mci);
161 }
162 }
163 }
164 }
165
166 }