001: package net.sourceforge.squirrel_sql.fw.sql;
002:
003: /*
004: * Copyright (C) 2001-2004 Colin Bell
005: * colbell@users.sourceforge.net
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: import java.beans.PropertyChangeListener;
022: import java.sql.Connection;
023: import java.sql.PreparedStatement;
024: import java.sql.SQLException;
025: import java.sql.SQLWarning;
026: import java.sql.Statement;
027: import java.util.Calendar;
028: import java.util.Date;
029:
030: import net.sourceforge.squirrel_sql.fw.dialects.DialectFactory;
031: import net.sourceforge.squirrel_sql.fw.util.PropertyChangeReporter;
032: import net.sourceforge.squirrel_sql.fw.util.StringManager;
033: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
034: import net.sourceforge.squirrel_sql.fw.util.StringUtilities;
035: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
036: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
037:
038: /**
039: * This represents a connection to an SQL server. it is basically a wrapper
040: * around <TT>java.sql.Connection</TT>.
041: *
042: * @author <A HREF="mailto:colbell@users.sourceforge.net">Colin Bell</A>
043: */
044: public class SQLConnection implements ISQLConnection {
045: private ISQLDriver _sqlDriver;
046:
047: /** Internationalized strings for this class. */
048: private static final StringManager s_stringMgr = StringManagerFactory
049: .getStringManager(SQLConnection.class);
050:
051: /** Logger for this class. */
052: private final static ILogger s_log = LoggerController
053: .createLogger(SQLConnection.class);
054:
055: /** The <TT>java.sql.Connection</TT> this object is wrapped around. */
056: private Connection _conn;
057:
058: /** Connectiopn properties specified when connection was opened. */
059: private final SQLDriverPropertyCollection _connProps;
060:
061: private boolean _autoCommitOnClose = false;
062:
063: private Date _timeOpened;
064: private Date _timeClosed;
065:
066: /** Object to handle property change events. */
067: private transient PropertyChangeReporter _propChgReporter;
068:
069: private SQLDatabaseMetaData metaData = null;
070:
071: public SQLConnection(Connection conn,
072: SQLDriverPropertyCollection connProps, ISQLDriver sqlDriver) {
073: super ();
074: _sqlDriver = sqlDriver;
075: if (conn == null) {
076: throw new IllegalArgumentException("SQLConnection == null");
077: }
078: _conn = conn;
079: _connProps = connProps;
080: _timeOpened = Calendar.getInstance().getTime();
081: metaData = new SQLDatabaseMetaData(this );
082: }
083:
084: /* (non-Javadoc)
085: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#close()
086: */
087: public void close() throws SQLException {
088: SQLException savedEx = null;
089: if (_conn != null) {
090: s_log.debug("Closing connection");
091: try {
092: if (!_conn.getAutoCommit()) {
093: if (_autoCommitOnClose) {
094: _conn.commit();
095: } else {
096: _conn.rollback();
097: }
098: }
099: } catch (SQLException ex) {
100: savedEx = ex;
101: }
102: _conn.close();
103: _conn = null;
104: _timeClosed = Calendar.getInstance().getTime();
105: if (savedEx != null) {
106: s_log.debug("Connection close failed", savedEx);
107: throw savedEx;
108: }
109: s_log.debug("Connection closed successfully");
110: }
111: }
112:
113: /* (non-Javadoc)
114: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#commit()
115: */
116: public void commit() throws SQLException {
117: validateConnection();
118: _conn.commit();
119: }
120:
121: /* (non-Javadoc)
122: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#rollback()
123: */
124: public void rollback() throws SQLException {
125: validateConnection();
126: _conn.rollback();
127: }
128:
129: /**
130: * Retrieve the properties specified when connection was opened. This can
131: * be <TT>null</TT>.
132: *
133: * @return Connection properties.
134: */
135: public SQLDriverPropertyCollection getConnectionProperties() {
136: return _connProps;
137: }
138:
139: /* (non-Javadoc)
140: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#getAutoCommit()
141: */
142: public boolean getAutoCommit() throws SQLException {
143: validateConnection();
144: return _conn.getAutoCommit();
145: }
146:
147: /* (non-Javadoc)
148: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#setAutoCommit(boolean)
149: */
150: public void setAutoCommit(boolean value) throws SQLException {
151: validateConnection();
152: final Connection conn = getConnection();
153: final boolean oldValue = conn.getAutoCommit();
154: if (oldValue != value) {
155: _conn.setAutoCommit(value);
156: getPropertyChangeReporter().firePropertyChange(
157: IPropertyNames.AUTO_COMMIT, oldValue, value);
158: }
159: }
160:
161: /* (non-Javadoc)
162: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#getCommitOnClose()
163: */
164: public boolean getCommitOnClose() {
165: return _autoCommitOnClose;
166: }
167:
168: /* (non-Javadoc)
169: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#getTransactionIsolation()
170: */
171: public int getTransactionIsolation() throws SQLException {
172: validateConnection();
173: return _conn.getTransactionIsolation();
174: }
175:
176: /* (non-Javadoc)
177: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#setTransactionIsolation(int)
178: */
179: public void setTransactionIsolation(int value) throws SQLException {
180: validateConnection();
181: _conn.setTransactionIsolation(value);
182: }
183:
184: /* (non-Javadoc)
185: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#setCommitOnClose(boolean)
186: */
187: public void setCommitOnClose(boolean value) {
188: _autoCommitOnClose = value;
189: }
190:
191: /* (non-Javadoc)
192: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#createStatement()
193: */
194: public Statement createStatement() throws SQLException {
195: validateConnection();
196: return _conn.createStatement();
197: }
198:
199: /* (non-Javadoc)
200: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#prepareStatement(java.lang.String)
201: */
202: public PreparedStatement prepareStatement(String sql)
203: throws SQLException {
204: validateConnection();
205: return _conn.prepareStatement(sql);
206: }
207:
208: /**
209: * Retrieve the time that this connection was opened. Note that this time
210: * is the time that this <TT>SQLConnection</TT> was created, not the time
211: * that the <TT>java.sql.Connection</TT> object that it is wrapped around
212: * was opened.
213: *
214: * @return Time connection opened.
215: */
216: public Date getTimeOpened() {
217: return _timeOpened;
218: }
219:
220: /**
221: * Retrieve the time that this connection was closed. If this connection
222: * is still opened then <TT>null</TT> will be returned..
223: *
224: * @return Time connection closed.
225: */
226: public Date getTimeClosed() {
227: return _timeClosed;
228: }
229:
230: /**
231: * Retrieve the metadata for this connection.
232: *
233: * @return The <TT>SQLMetaData</TT> object.
234: */
235: public SQLDatabaseMetaData getSQLMetaData() {
236: return metaData;
237: }
238:
239: /* (non-Javadoc)
240: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#getConnection()
241: */
242: public Connection getConnection() {
243: /* This is extremely useful when trying to track down Swing UI freezing.
244: * However, it currently fills the log which obscures other debug
245: * messages even though UI performance is acceptable, so it is commented
246: * out until it is needed later.
247: if (s_log.isDebugEnabled()) {
248: try {
249: if (SwingUtilities.isEventDispatchThread() ) {
250: throw new Exception();
251: }
252: } catch (Exception e) {
253: s_log.debug("GUI thread doing database work", e);
254: }
255: }
256: */
257: return _conn;
258: }
259:
260: /* (non-Javadoc)
261: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#getCatalog()
262: */
263: public String getCatalog() throws SQLException {
264: validateConnection();
265: return getConnection().getCatalog();
266: }
267:
268: /* (non-Javadoc)
269: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#setCatalog(java.lang.String)
270: */
271: public void setCatalog(String catalogName) throws SQLException {
272: validateConnection();
273: final Connection conn = getConnection();
274: final String oldValue = conn.getCatalog();
275: if (!StringUtilities.areStringsEqual(oldValue, catalogName)) {
276: setDbSpecificCatalog(catalogName);
277: getPropertyChangeReporter().firePropertyChange(
278: IPropertyNames.CATALOG, oldValue, catalogName);
279: }
280: }
281:
282: /**
283: * Decides which setCatalog method to call. Different databases have special requirements for this method
284: * so this just determines the database type and redirects to the appropriate db-specific or generic
285: * method.
286: *
287: * @param dialectType
288: * the type of database
289: * @param catalogName
290: * the catalog name to use
291: * @throws SQLException
292: * if an error occurs
293: */
294: private void setDbSpecificCatalog(String catalogName)
295: throws SQLException {
296: SQLDatabaseMetaData md = getSQLMetaData();
297:
298: if (DialectFactory.isMSSQLServer(md)) {
299: setMSSQLServerCatalog(catalogName);
300: } else if (DialectFactory.isInformix(md)) {
301: setInformixCatalog(catalogName);
302: } else {
303: setGenericDbCatalog(catalogName);
304: }
305: }
306:
307: private void setGenericDbCatalog(String catalogName)
308: throws SQLException {
309: final Connection conn = getConnection();
310: conn.setCatalog(catalogName);
311: }
312:
313: /**
314: * MS SQL Server throws an exception if the catalog name contains a period without it being quoted.
315: *
316: * @param catalogName
317: * the catalog name to use
318: * @throws SQLException
319: * if an error occurs
320: */
321: private void setMSSQLServerCatalog(String catalogName)
322: throws SQLException {
323: final Connection conn = getConnection();
324: conn.setCatalog(quote(catalogName));
325: }
326:
327: /**
328: * Work-around for Informix catalog switching bugs.
329: *
330: * @param catalogName
331: * the catalog name to use
332: * @throws SQLException
333: * if an error occurs
334: */
335: private void setInformixCatalog(String catalogName)
336: throws SQLException {
337: final Connection conn = getConnection();
338: Statement stmt = null;
339: String sql = "DATABASE " + catalogName;
340: try {
341: stmt = conn.createStatement();
342: stmt.execute(sql);
343: } catch (SQLException e) {
344: s_log
345: .error("setInformixCatalog: failed to change database with the database SQL directive: "
346: + sql);
347: } finally {
348: SQLUtilities.closeStatement(stmt);
349: }
350: // finally, try to set the catalog, which appears to be a NO-OP in the Informix driver.
351: conn.setCatalog(catalogName);
352: }
353:
354: /* (non-Javadoc)
355: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#getWarnings()
356: */
357: public SQLWarning getWarnings() throws SQLException {
358: validateConnection();
359: return _conn.getWarnings();
360: }
361:
362: /**
363: * Add a listener for property change events.
364: *
365: * @param lis The new listener.
366: */
367: public void addPropertyChangeListener(
368: PropertyChangeListener listener) {
369: if (listener != null) {
370: getPropertyChangeReporter().addPropertyChangeListener(
371: listener);
372: } else {
373: s_log
374: .debug("Attempted to add a null PropertyChangeListener");
375: }
376: }
377:
378: /**
379: * Remove a property change listener.
380: *
381: * @param lis The listener to be removed.
382: */
383: public void removePropertyChangeListener(
384: PropertyChangeListener listener) {
385: if (listener != null) {
386: getPropertyChangeReporter().removePropertyChangeListener(
387: listener);
388: } else {
389: s_log
390: .debug("Attempted to remove a null PropertyChangeListener");
391: }
392: }
393:
394: protected void validateConnection() throws SQLException {
395: if (_conn == null) {
396: throw new SQLException(s_stringMgr
397: .getString("SQLConnection.noConn"));
398: }
399: }
400:
401: /**
402: * Retrieve the object that reports on property change events. If it
403: * doesn't exist then create it.
404: *
405: * @return PropertyChangeReporter object.
406: */
407: private synchronized PropertyChangeReporter getPropertyChangeReporter() {
408: if (_propChgReporter == null) {
409: _propChgReporter = new PropertyChangeReporter(this );
410: }
411: return _propChgReporter;
412: }
413:
414: private String quote(String str) {
415: String identifierQuoteString = "";
416: try {
417: identifierQuoteString = getSQLMetaData()
418: .getIdentifierQuoteString();
419: } catch (SQLException ex) {
420: s_log
421: .debug(
422: "DBMS doesn't supportDatabasemetaData.getIdentifierQuoteString",
423: ex);
424: }
425: if (identifierQuoteString != null
426: && !identifierQuoteString.equals(" ")) {
427: return identifierQuoteString + str + identifierQuoteString;
428: }
429: return str;
430: }
431:
432: /* (non-Javadoc)
433: * @see net.sourceforge.squirrel_sql.fw.sql.ISQLConnection#getSQLDriver()
434: */
435: public ISQLDriver getSQLDriver() {
436: return _sqlDriver;
437: }
438:
439: }
|