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.commons.dbcp;
019:
020: import java.util.Vector;
021:
022: import junit.framework.Test;
023: import junit.framework.TestCase;
024: import junit.framework.TestSuite;
025:
026: import org.apache.commons.pool.PoolableObjectFactory;
027:
028: /**
029: * TestCase for AbandonedObjectPool
030: *
031: * @author Wayne Woodfield
032: * @version $Revision: 479137 $ $Date: 2006-11-25 08:51:48 -0700 (Sat, 25 Nov 2006) $
033: */
034: public class TestAbandonedObjectPool extends TestCase {
035: private AbandonedObjectPool pool = null;
036: private AbandonedConfig config = null;
037:
038: public TestAbandonedObjectPool(String testName) {
039: super (testName);
040: }
041:
042: public static Test suite() {
043: return new TestSuite(TestAbandonedObjectPool.class);
044: }
045:
046: public void setUp() throws Exception {
047: super .setUp();
048: config = new AbandonedConfig();
049:
050: // -- Uncomment the following line to enable logging --
051: // config.setLogAbandoned(true);
052:
053: config.setRemoveAbandoned(true);
054: config.setRemoveAbandonedTimeout(1);
055: pool = new AbandonedObjectPool(new SimpleFactory(), config);
056: }
057:
058: public void tearDown() throws Exception {
059: super .tearDown();
060: pool.close();
061: pool = null;
062: }
063:
064: /**
065: * Tests fix for Bug 28579, a bug in AbandonedObjectPool that causes numActive to go negative
066: * in GenericObjectPool
067: */
068: public void testConcurrentInvalidation() throws Exception {
069: final int POOL_SIZE = 30;
070: pool.setMaxActive(POOL_SIZE);
071: pool.setMaxIdle(POOL_SIZE);
072: pool
073: .setWhenExhaustedAction(AbandonedObjectPool.WHEN_EXHAUSTED_FAIL);
074:
075: // Exhaust the connection pool
076: Vector vec = new Vector();
077: for (int i = 0; i < POOL_SIZE; i++) {
078: vec.add(pool.borrowObject());
079: }
080:
081: // Abandon all borrowed objects
082: for (int i = 0; i < vec.size(); i++) {
083: ((PooledTestObject) vec.elementAt(i)).setAbandoned(true);
084: }
085:
086: // Try launching a bunch of borrows concurrently. Abandoned sweep will be triggered for each.
087: final int CONCURRENT_BORROWS = 5;
088: Thread[] threads = new Thread[CONCURRENT_BORROWS];
089: for (int i = 0; i < CONCURRENT_BORROWS; i++) {
090: threads[i] = new ConcurrentBorrower(vec);
091: threads[i].start();
092: }
093:
094: // Wait for all the threads to finish
095: for (int i = 0; i < CONCURRENT_BORROWS; i++) {
096: threads[i].join();
097: }
098:
099: // Return all objects that have not been destroyed
100: for (int i = 0; i < vec.size(); i++) {
101: PooledTestObject pto = (PooledTestObject) vec.elementAt(i);
102: if (pto.isActive()) {
103: pool.returnObject(pto);
104: }
105: }
106:
107: // Now, the number of open connections should be 0
108: assertTrue("numActive should have been 0, was "
109: + pool.getNumActive(), pool.getNumActive() == 0);
110: }
111:
112: class ConcurrentBorrower extends Thread {
113: private Vector _borrowed;
114:
115: public ConcurrentBorrower(Vector borrowed) {
116: _borrowed = borrowed;
117: }
118:
119: public void run() {
120: try {
121: _borrowed.add(pool.borrowObject());
122: } catch (Exception e) {
123: // expected in most cases
124: }
125: }
126: }
127:
128: class SimpleFactory implements PoolableObjectFactory {
129:
130: public Object makeObject() {
131: return new PooledTestObject(config);
132: }
133:
134: public boolean validateObject(Object obj) {
135: return true;
136: }
137:
138: public void activateObject(Object obj) {
139: ((PooledTestObject) obj).setActive(true);
140: }
141:
142: public void passivateObject(Object obj) {
143: ((PooledTestObject) obj).setActive(false);
144: }
145:
146: public void destroyObject(Object obj) {
147: ((PooledTestObject) obj).setActive(false);
148: // while destroying connections, yield control to other threads
149: // helps simulate threading errors
150: Thread.yield();
151: }
152: }
153: }
154:
155: class PooledTestObject extends AbandonedTrace {
156: private boolean active = false;
157: private int _hash = 0;
158: private boolean _abandoned = false;
159:
160: private static int hash = 1;
161:
162: public PooledTestObject(AbandonedConfig config) {
163: super (config);
164: _hash = hash++;
165: }
166:
167: public synchronized void setActive(boolean b) {
168: active = b;
169: }
170:
171: public synchronized boolean isActive() {
172: return active;
173: }
174:
175: public int hashCode() {
176: return _hash;
177: }
178:
179: public void setAbandoned(boolean b) {
180: _abandoned = b;
181: }
182:
183: protected long getLastUsed() {
184: if (_abandoned) {
185: // Abandoned object sweep will occur no matter what the value of removeAbandonedTimeout,
186: // because this indicates that this object was last used decades ago
187: return 1;
188: } else {
189: // Abandoned object sweep won't clean up this object
190: return 0;
191: }
192: }
193:
194: public boolean equals(Object obj) {
195: if (!(obj instanceof PooledTestObject))
196: return false;
197: return obj.hashCode() == hashCode();
198: }
199: }
|