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    }