001: /*
002: * ====================================================================
003: * JAFFA - Java Application Framework For All
004: *
005: * Copyright (C) 2002 JAFFA Development Group
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * Redistribution and use of this software and associated documentation ("Software"),
022: * with or without modification, are permitted provided that the following conditions are met:
023: * 1. Redistributions of source code must retain copyright statements and notices.
024: * Redistributions must also contain a copy of this document.
025: * 2. Redistributions in binary form must reproduce the above copyright notice,
026: * this list of conditions and the following disclaimer in the documentation
027: * and/or other materials provided with the distribution.
028: * 3. The name "JAFFA" must not be used to endorse or promote products derived from
029: * this Software without prior written permission. For written permission,
030: * please contact mail to: jaffagroup@yahoo.com.
031: * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"
032: * appear in their names without prior written permission.
033: * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).
034: *
035: * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: */
049:
050: package org.jaffa.persistence.engines.jdbcengine.datasource;
051:
052: import org.apache.log4j.Logger;
053: import java.util.*;
054: import java.sql.Connection;
055:
056: import org.jaffa.persistence.engines.jdbcengine.datasource.exceptions.DataSourceCreationException;
057: import org.jaffa.persistence.engines.jdbcengine.security.IJdbcSecurityPlugin;
058:
059: import java.io.IOException;
060: import java.sql.SQLException;
061: import org.apache.commons.dbcp.DelegatingConnection;
062: import org.jaffa.persistence.engines.jdbcengine.configservice.initdomain.Database;
063: import org.jaffa.persistence.engines.jdbcengine.configservice.initdomain.Param;
064: import org.jaffa.util.BeanHelper;
065:
066: /**
067: * Factory implementation for making us DataSource objects to execute DDL against.
068: * This uses a Connection pool to create DataSource objects.
069: */
070: public class DataSourceFactory {
071:
072: private static final Logger log = Logger
073: .getLogger(DataSourceFactory.class);
074:
075: // a cache to hold statements per connection
076: private static Map c_connectionCache = new WeakHashMap();
077:
078: // the ConnectionFactory, used for pooling connections
079: private static IConnectionFactory c_connectionFactory = null;
080:
081: // The JDBC Security Plugin
082: private static IJdbcSecurityPlugin c_jdbcSecurityPlugin = null;
083:
084: /** Make private to ensure this never gets instantiated.*/
085: private DataSourceFactory() {
086: }
087:
088: /** Returns a new DataSource instance.
089: * @param database the configuration information for the data source.
090: * @throws DataSourceCreationException if the connection could not be acquired.
091: * @return the DataSource.
092: */
093: public static DataSource getDataSource(Database database)
094: throws DataSourceCreationException {
095: if (log.isDebugEnabled())
096: log.debug("Acquiring a Connection");
097: Connection connection = getConnection(database);
098: try {
099: connection.setAutoCommit(false);
100: } catch (SQLException e) {
101: String str = "Error in re-setting the AutoCommit on the Connection";
102: log.error(str, e);
103: throw new DataSourceCreationException(
104: DataSourceCreationException.SET_AUTO_COMMIT_FAILED,
105: null, e);
106: }
107:
108: Map statementCache = null;
109: if (c_connectionFactory.usePreparedStatementCaching()) {
110: statementCache = (Map) c_connectionCache.get(connection);
111: if (statementCache == null) {
112: if (log.isDebugEnabled())
113: log
114: .debug("Creating a cache for all the PreparedStatement and CallableStatement objects for the connection "
115: + connection);
116: statementCache = new WeakHashMap();
117: synchronized (c_connectionCache) {
118: c_connectionCache.put(connection, statementCache);
119: }
120: }
121: }
122:
123: if (log.isDebugEnabled())
124: log.debug("Creating a new DataSource");
125: return new DataSource(
126: connection,
127: (database.getHitlistSize() != null ? new Integer(
128: database.getHitlistSize().getValue()) : null),
129: database.getEngine(),
130: (database.getUsePreparedStatement() != null ? new Boolean(
131: database.getUsePreparedStatement().isValue())
132: : null), statementCache);
133: }
134:
135: /** Free up the DataSource. This will free up the underlying pooled connection.
136: * @param ds the DataSource.
137: */
138: public static void freeDataSource(DataSource ds) {
139: Connection connection = ds.getConnection();
140: freeConnection(connection);
141: }
142:
143: private static Connection getConnection(Database database)
144: throws DataSourceCreationException {
145: // acquire a Connection
146: if (c_connectionFactory == null)
147: initialize(database);
148: Connection connection = null;
149: try {
150: connection = c_connectionFactory.createConnection();
151: } catch (Exception e) {
152: String str = "Error in acquiring a Connection from the ConnectionFactory";
153: log.error(str, e);
154: throw new DataSourceCreationException(
155: DataSourceCreationException.CONNECTION_FAILED,
156: null, e);
157: }
158:
159: // now invoke the newConnection trigger of the security plugin
160: try {
161: if (c_jdbcSecurityPlugin != null) {
162: if (log.isDebugEnabled())
163: log
164: .debug("Invoking the newConnection trigger on the JdbcSecurityPlugin");
165: c_jdbcSecurityPlugin.newConnection(connection);
166: }
167: } catch (Exception e) {
168: String str = "Error in executing the newConnection trigger of the JdbcSecurityPlugin. Freeing up the connection and then throwing the exception.";
169: log.error(str, e);
170:
171: // do cleanup
172: try {
173: // close the connection forcibly.. as it might have been altered.. don't want to re-use it !!!
174: forciblyCloseConnection(connection);
175: } catch (Exception e1) {
176: String str1 = "Error in closing the connection. Continuing with the process.";
177: log.warn(str1, e1);
178: }
179:
180: try {
181: c_connectionFactory.freeConnection(connection);
182: } catch (Exception e1) {
183: // do nothing
184: log
185: .warn(
186: "Error in returning a Connection back to the ConnectionFactory",
187: e1);
188: }
189:
190: throw new DataSourceCreationException(
191: DataSourceCreationException.NEW_CONNECTION_OF_JDBC_PLUGIN_FAILED,
192: null, e);
193: }
194: return connection;
195: }
196:
197: private static void freeConnection(Connection connection) {
198: // invoke the freeConnection trigger of the security plugin
199: try {
200: if (c_jdbcSecurityPlugin != null) {
201: if (log.isDebugEnabled())
202: log
203: .debug("Invoking the freeConnection trigger on the JdbcSecurityPlugin");
204: c_jdbcSecurityPlugin.freeConnection(connection);
205: }
206: } catch (Exception e) {
207: String str = "Error in executing the freeConnection trigger of the JdbcSecurityPlugin. Forcibly closing the connection, to avoid re-use by the connection pool.";
208: log.warn(str, e);
209: try {
210: forciblyCloseConnection(connection);
211: } catch (Exception e1) {
212: String str1 = "Error in closing the connection. Continuing with the process.";
213: log.warn(str1, e1);
214: }
215: }
216:
217: // now free the connection
218: try {
219: c_connectionFactory.freeConnection(connection);
220: } catch (Exception e) {
221: // do nothing
222: log
223: .warn(
224: "Error in returning a Connection back to the ConnectionFactory",
225: e);
226: }
227: }
228:
229: private synchronized static void initialize(Database database)
230: throws DataSourceCreationException {
231: if (c_connectionFactory == null) {
232: try {
233: // create an instance of the JDBC Security Plugin, while instantiating the ConnectionFactory
234: if (database.getJdbcSecurityPlugin() != null
235: && database.getJdbcSecurityPlugin().getValue() != null
236: && database.getJdbcSecurityPlugin().getValue()
237: .length() > 0)
238: c_jdbcSecurityPlugin = (IJdbcSecurityPlugin) Class
239: .forName(
240: database.getJdbcSecurityPlugin()
241: .getValue()).newInstance();
242: } catch (Exception e) {
243: String str = "Error in creating an instance of the JDBC Security Plugin for the class: "
244: + database.getJdbcSecurityPlugin();
245: log.error(str, e);
246: throw new DataSourceCreationException(
247: DataSourceCreationException.JDBC_PLUGIN_CREATION_FAILED,
248: new Object[] { database.getJdbcSecurityPlugin() },
249: e);
250: }
251:
252: try {
253: // now instantiate the ConnectionFactory and set its properties
254: IConnectionFactory connectionFactory = (IConnectionFactory) Class
255: .forName(
256: database.getConnectionFactory()
257: .getClassName()).newInstance();
258: if (database.getConnectionFactory().getParam() != null) {
259: for (Iterator i = database.getConnectionFactory()
260: .getParam().iterator(); i.hasNext();) {
261: Param param = (Param) i.next();
262: BeanHelper.setField(connectionFactory, param
263: .getName(), param.getValue());
264: }
265: }
266: c_connectionFactory = connectionFactory;
267: } catch (Exception e) {
268: String str = "Error in creating an instance of the ConnectionFactory for the class: "
269: + database.getConnectionFactory()
270: .getClassName();
271: log.error(str, e);
272: throw new DataSourceCreationException(
273: DataSourceCreationException.CONNECTION_FACTORY_CREATION_FAILED,
274: new Object[] { database.getConnectionFactory()
275: .getClassName() }, e);
276: }
277: }
278: }
279:
280: private static void forciblyCloseConnection(Connection connection)
281: throws SQLException {
282: // When using the dbcp DataSource, a wrapper connection is created. Extract the actual connection object from the wrapper.
283: if (connection instanceof DelegatingConnection)
284: connection = ((DelegatingConnection) connection)
285: .getInnermostDelegate();
286: connection.close();
287: }
288:
289: }
|