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