KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > geronimo > connector > outbound > AbstractSinglePoolConnectionInterceptor


1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.geronimo.connector.outbound;
18
19 import java.util.TimerTask JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.Timer JavaDoc;
23
24 import javax.resource.spi.ManagedConnectionFactory JavaDoc;
25 import javax.resource.spi.ConnectionRequestInfo JavaDoc;
26 import javax.resource.ResourceException JavaDoc;
27 import javax.security.auth.Subject JavaDoc;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import edu.emory.mathcs.backport.java.util.concurrent.locks.ReadWriteLock;
32 import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantReadWriteLock;
33 import edu.emory.mathcs.backport.java.util.concurrent.Semaphore;
34 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
35
36 /**
37  * @version $Rev: 476049 $ $Date: 2006-11-16 23:35:17 -0500 (Thu, 16 Nov 2006) $
38  */

39 public abstract class AbstractSinglePoolConnectionInterceptor implements ConnectionInterceptor, PoolingAttributes {
40     protected static Log log = LogFactory.getLog(AbstractSinglePoolConnectionInterceptor.class.getName());
41     protected final ConnectionInterceptor next;
42     private final ReadWriteLock resizeLock = new ReentrantReadWriteLock();
43     protected Semaphore permits;
44     protected int blockingTimeoutMilliseconds;
45     protected int connectionCount = 0;
46     private long idleTimeoutMilliseconds;
47     private IdleReleaser idleReleaser;
48     protected Timer JavaDoc timer = PoolIdleReleaserTimer.getTimer();
49     protected int maxSize = 0;
50     protected int minSize = 0;
51     protected int shrinkLater = 0;
52     protected volatile boolean destroyed = false;
53
54     public AbstractSinglePoolConnectionInterceptor(final ConnectionInterceptor next,
55                                                    int maxSize,
56                                                    int minSize,
57                                                    int blockingTimeoutMilliseconds,
58                                                    int idleTimeoutMinutes) {
59         this.next = next;
60         this.maxSize = maxSize;
61         this.minSize = minSize;
62         this.blockingTimeoutMilliseconds = blockingTimeoutMilliseconds;
63         setIdleTimeoutMinutes(idleTimeoutMinutes);
64         permits = new Semaphore(maxSize, true);
65     }
66
67     public void getConnection(ConnectionInfo connectionInfo) throws ResourceException JavaDoc {
68         if (connectionInfo.getManagedConnectionInfo().getManagedConnection() != null) {
69             if (log.isTraceEnabled()) {
70                 log.trace("using already assigned connection " + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to pool " + this);
71             }
72             return;
73         }
74         try {
75             resizeLock.readLock().lock();
76             try {
77                 if (permits.tryAcquire(blockingTimeoutMilliseconds, TimeUnit.MILLISECONDS)) {
78                     internalGetConnection(connectionInfo);
79                 } else {
80                     throw new ResourceException JavaDoc("No ManagedConnections available "
81                             + "within configured blocking timeout ( "
82                             + blockingTimeoutMilliseconds
83                             + " [ms] ) for pool " + this);
84
85                 }
86             } finally {
87                 resizeLock.readLock().unlock();
88             }
89
90         } catch (InterruptedException JavaDoc ie) {
91             throw new ResourceException JavaDoc("Interrupted while requesting permit!");
92         } // end of try-catch
93
}
94
95     protected abstract void internalGetConnection(ConnectionInfo connectionInfo) throws ResourceException JavaDoc;
96
97     public void returnConnection(ConnectionInfo connectionInfo,
98                                  ConnectionReturnAction connectionReturnAction) {
99         if (log.isTraceEnabled()) {
100             log.trace("returning connection " + connectionInfo.getConnectionHandle() + " for MCI " + connectionInfo.getManagedConnectionInfo() + " and MC " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to pool " + this);
101         }
102
103         // not strictly synchronized with destroy(), but pooled operations in internalReturn() are...
104
if (destroyed) {
105             try {
106                 connectionInfo.getManagedConnectionInfo().getManagedConnection().destroy();
107             } catch (ResourceException JavaDoc re) {
108                 // empty
109
}
110             return;
111         }
112
113         resizeLock.readLock().lock();
114         try {
115             ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
116             if (connectionReturnAction == ConnectionReturnAction.RETURN_HANDLE && mci.hasConnectionHandles()) {
117                 if (log.isTraceEnabled()) {
118                     log.trace("Return request at pool with connection handles! " + connectionInfo.getConnectionHandle() + " for MCI " + connectionInfo.getManagedConnectionInfo() + " and MC " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to pool " + this, new Exception JavaDoc("Stack trace"));
119                 }
120                 return;
121             }
122
123             boolean wasInPool = internalReturn(connectionInfo, connectionReturnAction);
124
125             if (!wasInPool) {
126                 permits.release();
127             }
128         } finally {
129             resizeLock.readLock().unlock();
130         }
131     }
132
133     protected abstract boolean internalReturn(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction);
134
135     protected abstract void internalDestroy();
136
137     // Cancel the IdleReleaser TimerTask (fixes memory leak) and clean up the pool
138
public void destroy() {
139         destroyed = true;
140         if (idleReleaser != null)
141             idleReleaser.cancel();
142         internalDestroy();
143         next.destroy();
144     }
145
146     public int getPartitionCount() {
147         return 1;
148     }
149
150     public abstract int getPartitionMaxSize();
151
152     public void setPartitionMaxSize(int newMaxSize) throws InterruptedException JavaDoc {
153         if (newMaxSize <= 0) {
154             throw new IllegalArgumentException JavaDoc("Max size must be positive, not " + newMaxSize);
155         }
156         if (newMaxSize != getPartitionMaxSize()) {
157             resizeLock.writeLock().lock();
158             try {
159                 ResizeInfo resizeInfo = new ResizeInfo(this.minSize, permits.availablePermits(), connectionCount, newMaxSize);
160                 this.shrinkLater = resizeInfo.getShrinkLater();
161
162                 permits = new Semaphore(newMaxSize, true);
163                 //pre-acquire permits for the existing checked out connections that will not be closed when they are returned.
164
for (int i = 0; i < resizeInfo.getTransferCheckedOut(); i++) {
165                     permits.acquire();
166                 }
167                 //transfer connections we are going to keep
168
transferConnections(newMaxSize, resizeInfo.getShrinkNow());
169                 this.minSize = resizeInfo.getNewMinSize();
170             } finally {
171                 resizeLock.writeLock().unlock();
172             }
173         }
174     }
175
176
177     static final class ResizeInfo {
178
179         private final int newMinSize;
180         private final int shrinkNow;
181         private final int shrinkLater;
182         private final int transferCheckedOut;
183
184         ResizeInfo(final int oldMinSize, final int oldPermitsAvailable, final int oldConnectionCount, final int newMaxSize) {
185             final int checkedOut = oldConnectionCount - oldPermitsAvailable;
186             int shrinkLater = checkedOut - newMaxSize;
187             if (shrinkLater < 0) {
188                 shrinkLater = 0;
189             }
190             this.shrinkLater = shrinkLater;
191             int shrinkNow = oldConnectionCount - newMaxSize - shrinkLater;
192             if (shrinkNow < 0) {
193                 shrinkNow = 0;
194             }
195             this.shrinkNow = shrinkNow;
196             if (newMaxSize >= oldMinSize) {
197                 newMinSize = oldMinSize;
198             } else {
199                 newMinSize = newMaxSize;
200             }
201             this.transferCheckedOut = checkedOut - shrinkLater;
202         }
203
204         public int getNewMinSize() {
205             return newMinSize;
206         }
207
208         public int getShrinkNow() {
209             return shrinkNow;
210         }
211
212         public int getShrinkLater() {
213             return shrinkLater;
214         }
215
216         public int getTransferCheckedOut() {
217             return transferCheckedOut;
218         }
219
220
221     }
222
223     protected abstract void transferConnections(int maxSize, int shrinkNow);
224
225     public abstract int getIdleConnectionCount();
226
227     public int getConnectionCount() {
228         return connectionCount;
229     }
230
231     public int getPartitionMinSize() {
232         return minSize;
233     }
234
235     public void setPartitionMinSize(int minSize) {
236         this.minSize = minSize;
237     }
238
239     public int getBlockingTimeoutMilliseconds() {
240         return blockingTimeoutMilliseconds;
241     }
242
243     public void setBlockingTimeoutMilliseconds(int blockingTimeoutMilliseconds) {
244         if (blockingTimeoutMilliseconds < 0) {
245             throw new IllegalArgumentException JavaDoc("blockingTimeoutMilliseconds must be positive or 0, not " + blockingTimeoutMilliseconds);
246         }
247         if (blockingTimeoutMilliseconds == 0) {
248             this.blockingTimeoutMilliseconds = Integer.MAX_VALUE;
249         } else {
250             this.blockingTimeoutMilliseconds = blockingTimeoutMilliseconds;
251         }
252     }
253
254     public int getIdleTimeoutMinutes() {
255         return (int) idleTimeoutMilliseconds / (1000 * 60);
256     }
257
258     public void setIdleTimeoutMinutes(int idleTimeoutMinutes) {
259         if (idleTimeoutMinutes < 0) {
260             throw new IllegalArgumentException JavaDoc("idleTimeoutMinutes must be positive or 0, not " + idleTimeoutMinutes);
261         }
262         if (idleReleaser != null) {
263             idleReleaser.cancel();
264         }
265         if (idleTimeoutMinutes > 0) {
266             this.idleTimeoutMilliseconds = idleTimeoutMinutes * 60 * 1000;
267             idleReleaser = new IdleReleaser(this);
268             timer.schedule(idleReleaser, this.idleTimeoutMilliseconds, this.idleTimeoutMilliseconds);
269         }
270     }
271
272     protected abstract void getExpiredManagedConnectionInfos(long threshold, ArrayList JavaDoc killList);
273
274     protected abstract boolean addToPool(ManagedConnectionInfo mci);
275
276     // static class to permit chain of strong references from preventing ClassLoaders
277
// from being GC'ed.
278
private static class IdleReleaser extends TimerTask JavaDoc {
279         private AbstractSinglePoolConnectionInterceptor parent;
280
281         private IdleReleaser(AbstractSinglePoolConnectionInterceptor parent) {
282             this.parent = parent;
283         }
284
285         public boolean cancel() {
286             this.parent = null;
287             return super.cancel();
288         }
289
290         public void run() {
291             // protect against interceptor being set to null mid-execution
292
AbstractSinglePoolConnectionInterceptor interceptor = parent;
293             if (interceptor == null)
294                 return;
295
296             interceptor.resizeLock.readLock().lock();
297             try {
298                 long threshold = System.currentTimeMillis() - interceptor.idleTimeoutMilliseconds;
299                 ArrayList JavaDoc killList = new ArrayList JavaDoc(interceptor.getPartitionMaxSize());
300                 interceptor.getExpiredManagedConnectionInfos(threshold, killList);
301                 for (Iterator JavaDoc i = killList.iterator(); i.hasNext();) {
302                     ManagedConnectionInfo managedConnectionInfo = (ManagedConnectionInfo) i.next();
303                     ConnectionInfo killInfo = new ConnectionInfo(managedConnectionInfo);
304                     interceptor.internalReturn(killInfo, ConnectionReturnAction.DESTROY);
305                 }
306                 interceptor.permits.release(killList.size());
307             } finally {
308                 interceptor.resizeLock.readLock().unlock();
309             }
310         }
311
312     }
313
314     // Currently only a short-lived (10 millisecond) task.
315
// So, FillTask, unlike IdleReleaser, shouldn't cause GC problems.
316
protected class FillTask extends TimerTask JavaDoc {
317         private final ManagedConnectionFactory JavaDoc managedConnectionFactory;
318         private final Subject JavaDoc subject;
319         private final ConnectionRequestInfo JavaDoc cri;
320
321         public FillTask(ConnectionInfo connectionInfo) {
322             managedConnectionFactory = connectionInfo.getManagedConnectionInfo().getManagedConnectionFactory();
323             subject = connectionInfo.getManagedConnectionInfo().getSubject();
324             cri = connectionInfo.getManagedConnectionInfo().getConnectionRequestInfo();
325         }
326
327         public void run() {
328             resizeLock.readLock().lock();
329             try {
330                 while (connectionCount < minSize) {
331                     ManagedConnectionInfo mci = new ManagedConnectionInfo(managedConnectionFactory, cri);
332                     mci.setSubject(subject);
333                     ConnectionInfo ci = new ConnectionInfo(mci);
334                     try {
335                         next.getConnection(ci);
336                     } catch (ResourceException JavaDoc e) {
337                         return;
338                     }
339                     boolean added = addToPool(mci);
340                     if (!added) {
341                         internalReturn(ci, ConnectionReturnAction.DESTROY);
342                         return;
343                     }
344                 }
345             } finally {
346                 resizeLock.readLock().unlock();
347             }
348         }
349
350     }
351 }
352
Popular Tags