001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.data.jdbc;
017:
018: import java.sql.CallableStatement;
019: import java.sql.Connection;
020: import java.sql.DatabaseMetaData;
021: import java.sql.PreparedStatement;
022: import java.sql.SQLException;
023: import java.sql.SQLWarning;
024: import java.sql.Savepoint;
025: import java.sql.Statement;
026: import java.util.Collections;
027: import java.util.Iterator;
028: import java.util.LinkedList;
029: import java.util.List;
030: import java.util.Map;
031: import java.util.logging.Level;
032: import java.util.logging.Logger;
033:
034: import javax.naming.NamingException;
035: import javax.sql.ConnectionEvent;
036: import javax.sql.ConnectionEventListener;
037: import javax.sql.ConnectionPoolDataSource;
038: import javax.sql.DataSource;
039: import javax.sql.PooledConnection;
040:
041: import org.geotools.factory.GeoTools;
042:
043: /**
044: * A copy of our origional ConnectionPool class in order to back in onto
045: * a java.sql.DataSource.
046: * <p>
047: * This class is method compatible - if you like it we can do the following:
048: * <ol>
049: * <li>Introduce an interface ConnectionManager
050: * <li>Mark the origional ConnectionPool with the ConnectionManager interface
051: * <li>Make this DataSourceManager implement ConnectionManager
052: * <li>Change the constructor of JDBCDataStore to take a ConnectionManager
053: * <li>Update the factories to use this implementation (with an off the shelf DataSource implementation)
054: * <li>Create new factories that will accept an application supplied DataSource (or String for JNDI lookup)
055: * </ul>
056: * This implementation is "naked" - it depends on the dataSource provided to handle the whole pooling idea.
057: * It *does* do something - it will close all outstanding connections (leased through this interface) when
058: * the close method is called.'
059: *
060: * @author Jody Garnett
061: * @version 2.4
062: */
063: public final class DataSourceManager implements ConnectionManager {
064: /** A logger */
065: private static final Logger LOGGER = org.geotools.util.logging.Logging
066: .getLogger("org.geotools.data.jdbc");
067:
068: /** This is our data source - we are expecting it to handle pooling. */
069: private DataSource dataSource;
070:
071: /** This of connections that are in use */
072: private List usedConnections = Collections
073: .synchronizedList(new LinkedList());
074:
075: /**
076: * Uses JNDI InitialContext to lookup a DataSource with the provided name.
077: * <p>
078: * This solution is mostly intended for use with Java EE Applications, or desktop
079: * applications running in an evironment support JNDI. This class makes use
080: * of the global GeoTools.getDefaultHints() in order to aquire the the corrent
081: * InitialContext. The out of the box implementation makes use of the JRE's
082: * JNDI configuration (which is probably already set up by your application server).
083: * <p>
084: * @param dataSourceName Name to lookup in InitialContext.
085: * @throws NamingException If there is nothing located in the indicated location
086: * @throws ClassCastException If the name does not refer to a DataSource
087: */
088: public DataSourceManager(String dataSourceName)
089: throws NamingException, ClassCastException {
090: this ((DataSource) GeoTools.getInitialContext(
091: GeoTools.getDefaultHints()).lookup(dataSourceName));
092: }
093:
094: /**
095: * Creates a new DataSourceManager using the provided DataSource.
096: * <p>
097: * Please note we are depending on the dataSource to provide conneciton pooling.
098: * @param dataSource Used to create connections as needed
099: */
100: public DataSourceManager(DataSource dataSource) {
101: this .dataSource = dataSource;
102: }
103:
104: /**
105: * Gets a Connection from the Connection Pool.
106: *
107: * <p>
108: * If no available connections exist a new connection will be created and added to the pool.
109: * When the returned connection is closed it will be added to the connection pool for other
110: * requests to this method.
111: * </p>
112: *
113: * @return A Connection from the ConnectionPool.
114: *
115: * @throws SQLException If an error occurs getting the connection or if the
116: * connection pool has been closed by a previous call to close().
117: */
118: public synchronized Connection getConnection() throws SQLException {
119: if (usedConnections == null) {
120: throw new SQLException("DataSourceManager is closed");
121: }
122: Connection connection = dataSource.getConnection();
123: this .usedConnections.add(connection);
124:
125: return new ConnectionDecorator(connection) {
126: public void close() throws SQLException {
127: usedConnections.remove(connection);
128: connection.close();
129: }
130: };
131: }
132:
133: /** Closes all the PooledConnections in the the ConnectionPool.
134: * The current behaviour is to first close all the used connections,
135: * then close all the available connections. This method will also set
136: * the state of the ConnectionPool to closed, caused any future calls
137: * to getConnection to throw an SQLException.
138: */
139: public synchronized void close() {
140: for (Iterator i = usedConnections.iterator(); i.hasNext();) {
141: Connection connection = (Connection) i.next();
142: try {
143: if (!connection.isClosed()) {
144: connection.close(); // return to dataSource
145: }
146: } catch (SQLException eek) {
147: LOGGER.log(Level.WARNING, "Closing Connection: " + eek,
148: eek);
149: }
150: usedConnections.clear();
151: usedConnections = null;
152: dataSource = null;
153: }
154: }
155:
156: public synchronized boolean isClosed() {
157: return this.usedConnections == null;
158: }
159: }
|