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    }