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.datasources;
019:
020: import java.io.IOException;
021: import java.io.ObjectInputStream;
022: import java.sql.Connection;
023: import java.sql.SQLException;
024: import java.util.Map;
025:
026: import javax.naming.NamingException;
027: import javax.naming.Reference;
028: import javax.naming.StringRefAddr;
029: import javax.sql.ConnectionPoolDataSource;
030:
031: import org.apache.commons.pool.KeyedObjectPool;
032: import org.apache.commons.pool.impl.GenericKeyedObjectPool;
033: import org.apache.commons.pool.impl.GenericObjectPool;
034: import org.apache.commons.dbcp.SQLNestedException;
035:
036: /**
037: * A pooling <code>DataSource</code> appropriate for deployment within
038: * J2EE environment. There are many configuration options, most of which are
039: * defined in the parent class. All users (based on username) share a single
040: * maximum number of Connections in this datasource.
041: *
042: * @author John D. McNally
043: * @version $Revision: 500687 $ $Date: 2007-01-27 16:33:47 -0700 (Sat, 27 Jan 2007) $
044: */
045: public class SharedPoolDataSource extends InstanceKeyDataSource {
046:
047: private final Map userKeys = new LRUMap(10);
048:
049: private int maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;
050: private int maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
051: private int maxWait = (int) Math.min((long) Integer.MAX_VALUE,
052: GenericObjectPool.DEFAULT_MAX_WAIT);
053: private KeyedObjectPool pool = null;
054:
055: /**
056: * Default no-arg constructor for Serialization
057: */
058: public SharedPoolDataSource() {
059: }
060:
061: /**
062: * Close pool being maintained by this datasource.
063: */
064: public void close() throws Exception {
065: if (pool != null) {
066: pool.close();
067: }
068: InstanceKeyObjectFactory.removeInstance(instanceKey);
069: }
070:
071: // -------------------------------------------------------------------
072: // Properties
073:
074: /**
075: * The maximum number of active connections that can be allocated from
076: * this pool at the same time, or non-positive for no limit.
077: */
078: public int getMaxActive() {
079: return (this .maxActive);
080: }
081:
082: /**
083: * The maximum number of active connections that can be allocated from
084: * this pool at the same time, or non-positive for no limit.
085: * The default is 8.
086: */
087: public void setMaxActive(int maxActive) {
088: assertInitializationAllowed();
089: this .maxActive = maxActive;
090: }
091:
092: /**
093: * The maximum number of active connections that can remain idle in the
094: * pool, without extra ones being released, or negative for no limit.
095: */
096: public int getMaxIdle() {
097: return (this .maxIdle);
098: }
099:
100: /**
101: * The maximum number of active connections that can remain idle in the
102: * pool, without extra ones being released, or negative for no limit.
103: * The default is 8.
104: */
105: public void setMaxIdle(int maxIdle) {
106: assertInitializationAllowed();
107: this .maxIdle = maxIdle;
108: }
109:
110: /**
111: * The maximum number of milliseconds that the pool will wait (when there
112: * are no available connections) for a connection to be returned before
113: * throwing an exception, or -1 to wait indefinitely. Will fail
114: * immediately if value is 0.
115: * The default is -1.
116: */
117: public int getMaxWait() {
118: return (this .maxWait);
119: }
120:
121: /**
122: * The maximum number of milliseconds that the pool will wait (when there
123: * are no available connections) for a connection to be returned before
124: * throwing an exception, or -1 to wait indefinitely. Will fail
125: * immediately if value is 0.
126: * The default is -1.
127: */
128: public void setMaxWait(int maxWait) {
129: assertInitializationAllowed();
130: this .maxWait = maxWait;
131: }
132:
133: // ----------------------------------------------------------------------
134: // Instrumentation Methods
135:
136: /**
137: * Get the number of active connections in the pool.
138: */
139: public int getNumActive() {
140: return (pool == null) ? 0 : pool.getNumActive();
141: }
142:
143: /**
144: * Get the number of idle connections in the pool.
145: */
146: public int getNumIdle() {
147: return (pool == null) ? 0 : pool.getNumIdle();
148: }
149:
150: // ----------------------------------------------------------------------
151: // Inherited abstract methods
152:
153: protected synchronized PooledConnectionAndInfo getPooledConnectionAndInfo(
154: String username, String password) throws SQLException {
155: if (pool == null) {
156: try {
157: registerPool(username, password);
158: } catch (NamingException e) {
159: throw new SQLNestedException("RegisterPool failed", e);
160: }
161: }
162:
163: PooledConnectionAndInfo info = null;
164: try {
165: info = (PooledConnectionAndInfo) pool
166: .borrowObject(getUserPassKey(username, password));
167: } catch (Exception e) {
168: throw new SQLNestedException(
169: "Could not retrieve connection info from pool", e);
170: }
171: return info;
172: }
173:
174: /**
175: * Returns a <code>SharedPoolDataSource</code> {@link Reference}.
176: *
177: * @since 1.2.2
178: */
179: public Reference getReference() throws NamingException {
180: Reference ref = new Reference(getClass().getName(),
181: SharedPoolDataSourceFactory.class.getName(), null);
182: ref.add(new StringRefAddr("instanceKey", instanceKey));
183: return ref;
184: }
185:
186: private UserPassKey getUserPassKey(String username, String password) {
187: UserPassKey key = (UserPassKey) userKeys.get(username);
188: if (key == null) {
189: key = new UserPassKey(username, password);
190: userKeys.put(username, key);
191: }
192: return key;
193: }
194:
195: private void registerPool(String username, String password)
196: throws javax.naming.NamingException, SQLException {
197:
198: ConnectionPoolDataSource cpds = testCPDS(username, password);
199:
200: // Create an object pool to contain our PooledConnections
201: GenericKeyedObjectPool tmpPool = new GenericKeyedObjectPool(
202: null);
203: tmpPool.setMaxActive(getMaxActive());
204: tmpPool.setMaxIdle(getMaxIdle());
205: tmpPool.setMaxWait(getMaxWait());
206: tmpPool.setWhenExhaustedAction(whenExhaustedAction(maxActive,
207: maxWait));
208: tmpPool.setTestOnBorrow(getTestOnBorrow());
209: tmpPool.setTestOnReturn(getTestOnReturn());
210: tmpPool
211: .setTimeBetweenEvictionRunsMillis(getTimeBetweenEvictionRunsMillis());
212: tmpPool.setNumTestsPerEvictionRun(getNumTestsPerEvictionRun());
213: tmpPool
214: .setMinEvictableIdleTimeMillis(getMinEvictableIdleTimeMillis());
215: tmpPool.setTestWhileIdle(getTestWhileIdle());
216: pool = tmpPool;
217: // Set up the factory we will use (passing the pool associates
218: // the factory with the pool, so we do not have to do so
219: // explicitly)
220: new KeyedCPDSConnectionFactory(cpds, pool,
221: getValidationQuery(), isRollbackAfterValidation());
222: }
223:
224: protected void setupDefaults(Connection con, String username)
225: throws SQLException {
226: con.setAutoCommit(isDefaultAutoCommit());
227: int defaultTransactionIsolation = getDefaultTransactionIsolation();
228: if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) {
229: con.setTransactionIsolation(defaultTransactionIsolation);
230: }
231: con.setReadOnly(isDefaultReadOnly());
232: }
233:
234: /**
235: * Supports Serialization interface.
236: *
237: * @param in a <code>java.io.ObjectInputStream</code> value
238: * @exception IOException if an error occurs
239: * @exception ClassNotFoundException if an error occurs
240: */
241: private void readObject(ObjectInputStream in) throws IOException,
242: ClassNotFoundException {
243: try {
244: in.defaultReadObject();
245: SharedPoolDataSource oldDS = (SharedPoolDataSource) new SharedPoolDataSourceFactory()
246: .getObjectInstance(getReference(), null, null, null);
247: this .pool = oldDS.pool;
248: } catch (NamingException e) {
249: throw new IOException("NamingException: " + e);
250: }
251: }
252: }
|