001: /*
002: * WbConnection.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.db;
013:
014: import java.beans.PropertyChangeEvent;
015: import java.beans.PropertyChangeListener;
016: import java.io.PrintWriter;
017: import java.lang.reflect.Field;
018: import java.lang.reflect.Method;
019: import java.sql.Connection;
020: import java.sql.DatabaseMetaData;
021: import java.sql.DriverManager;
022: import java.sql.ResultSet;
023: import java.sql.SQLException;
024: import java.sql.SQLWarning;
025: import java.sql.Savepoint;
026: import java.sql.Statement;
027: import java.util.ArrayList;
028: import java.util.Iterator;
029: import java.util.List;
030:
031: import workbench.db.report.TagWriter;
032: import workbench.interfaces.DbExecutionListener;
033: import workbench.interfaces.StatementRunner;
034: import workbench.resource.ResourceMgr;
035: import workbench.util.ExceptionUtil;
036: import workbench.log.LogMgr;
037: import workbench.resource.Settings;
038: import workbench.sql.ScriptParser;
039: import workbench.sql.preparedstatement.PreparedStatementPool;
040: import workbench.util.FileUtil;
041: import workbench.util.StrBuffer;
042: import workbench.util.StringUtil;
043:
044: /**
045: *
046: * @author support@sql-workbench.net
047: */
048: public class WbConnection implements DbExecutionListener {
049: public static final String PROP_CATALOG = "catalog";
050: public static final String PROP_SCHEMA = "schema";
051: public static final String PROP_AUTOCOMMIT = "autocommit";
052: public static final String PROP_CONNECTION_STATE = "state";
053: public static final String CONNECTION_CLOSED = "closed";
054: public static final String CONNECTION_OPEN = "open";
055:
056: private String id;
057: private StringBuilder scriptError = null;
058: private Connection sqlConnection;
059: private DbMetadata metaData;
060: private ConnectionProfile profile;
061: private PreparedStatementPool preparedStatementPool;
062: private List<PropertyChangeListener> listeners;
063: private DbObjectCache objectCache;
064:
065: private Method clearSettings = null;
066: private Object dbAccess = null;
067: private boolean doOracleClear = true;
068:
069: private boolean busy;
070: private KeepAliveDaemon keepAlive = null;
071:
072: /**
073: * Create a new wrapper connection around the original SQL connection.
074: * This will also initialize a {@link #DbMetadata} instance.
075: */
076: public WbConnection(String anId, Connection aConn,
077: ConnectionProfile aProfile) throws SQLException {
078: this .id = anId;
079: setSqlConnection(aConn);
080: setProfile(aProfile);
081: }
082:
083: /**
084: * Returns the internal ID of this connection.
085: *
086: * @return the internal id of this connection.
087: */
088: public String getId() {
089: return this .id;
090: }
091:
092: private void setProfile(ConnectionProfile aProfile) {
093: this .profile = aProfile;
094: initKeepAlive();
095: }
096:
097: public PreparedStatementPool getPreparedStatementPool() {
098: if (this .preparedStatementPool == null) {
099: this .preparedStatementPool = new PreparedStatementPool(this );
100: }
101: return this .preparedStatementPool;
102: }
103:
104: public DbObjectCache getObjectCache() {
105: if (this .objectCache == null) {
106: this .objectCache = new DbObjectCache(this );
107: }
108: return this .objectCache;
109: }
110:
111: public String getCurrentSchema() {
112: return this .metaData.getCurrentSchema();
113: }
114:
115: /**
116: * Return the name of the current user.
117: * Wrapper for DatabaseMetaData.getUserName() that throws no Exception
118: */
119: public String getCurrentUser() {
120: try {
121: return this .sqlConnection.getMetaData().getUserName();
122: } catch (Throwable e) {
123: return StringUtil.EMPTY_STRING;
124: }
125: }
126:
127: public boolean supportsQueryTimeout() {
128: if (this .metaData == null)
129: return true;
130: return this .metaData.getDbSettings().supportsQueryTimeout();
131: }
132:
133: /**
134: * @return The profile associated with this connection
135: */
136: public ConnectionProfile getProfile() {
137: return this .profile;
138: }
139:
140: void runPreDisconnectScript() {
141: if (this .profile == null)
142: return;
143: if (this .sqlConnection == null)
144: return;
145: if (this .keepAlive != null) {
146: this .keepAlive.shutdown();
147: }
148: String sql = profile.getPreDisconnectScript();
149: runConnectScript(sql, "disconnect");
150: }
151:
152: void runPostConnectScript() {
153: if (this .profile == null)
154: return;
155: if (this .sqlConnection == null)
156: return;
157: String sql = profile.getPostConnectScript();
158: runConnectScript(sql, "connect");
159: }
160:
161: private void runConnectScript(String sql, String type) {
162: if (StringUtil.isWhitespaceOrEmpty(sql))
163: return;
164: LogMgr.logInfo("WbConnection.runConnectScript()", "Executing "
165: + type + " script...");
166:
167: StatementRunner runner = StatementRunner.Factory.createRunner();
168: runner.setConnection(this );
169:
170: ScriptParser p = new ScriptParser(sql);
171: p
172: .setAlternateLineComment(this .getDbSettings()
173: .getLineComment());
174: Iterator itr = p.getIterator();
175: String command = null;
176:
177: // The statemenRunner will call clearMessages() when statementDone()
178: // is called which in turn will call clearWarnings() on this instances.
179: // This will also clear the scriptError and thus all messages
180: // that are collected here. So I have to store the messages locally
181: // and cannot use the scriptError variable directly
182: StringBuilder messages = new StringBuilder(150);
183:
184: try {
185: while (itr.hasNext()) {
186: command = p.getNextCommand();
187: if (p == null)
188: continue;
189:
190: try {
191: runner.runStatement(command, -1, 0);
192: } finally {
193: runner.statementDone();
194: }
195: messages.append(ResourceMgr
196: .getString("MsgBatchExecutingStatement"));
197: messages.append(": ");
198: messages.append(StringUtil
199: .getMaxSubstring(command, 250));
200: messages.append("\n\n");
201: }
202: } catch (Throwable e) {
203: LogMgr.logError("WbConnection.runConnectScript()",
204: "Error executing " + type + " script", e);
205: messages = new StringBuilder();
206: messages.append(ResourceMgr
207: .getString("MsgBatchStatementError"));
208: messages.append(": ");
209: messages.append(command + "\n");
210: messages.append(e.getMessage());
211: } finally {
212: if (runner != null)
213: runner.done();
214: }
215: this .scriptError = messages;
216: }
217:
218: void setSqlConnection(Connection aConn) throws SQLException {
219: this .sqlConnection = aConn;
220: this .metaData = new DbMetadata(this );
221: this .doOracleClear = this .metaData.isOracle();
222: }
223:
224: /**
225: * Return any warnings that are stored in the underlying SQL Connection.
226: * The warnings are then cleared from the connection object.
227: *
228: * @see #clearWarnings()
229: * @return any warnings reported from the server, null if no warnings are available.
230: */
231: public String getWarnings() {
232: try {
233:
234: SQLWarning warn = this .getSqlConnection().getWarnings();
235: if (warn == null) {
236: if (this .scriptError != null) {
237: String error = this .scriptError.toString();
238: this .scriptError = null;
239: return error;
240: }
241: return null;
242: }
243:
244: StringBuilder msg = new StringBuilder(200);
245: if (!StringUtil.isEmptyString(this .scriptError))
246: msg.append(this .scriptError);
247:
248: String s = null;
249: while (warn != null) {
250: s = warn.getMessage();
251: msg.append('\n');
252: msg.append(s);
253: warn = warn.getNextWarning();
254: }
255: this .clearWarnings();
256: return msg.toString();
257: } catch (SQLException e) {
258: LogMgr.logError("WbConnection.getWarnings()",
259: "Error when retrieving SQL Warnings", e);
260: return null;
261: }
262: }
263:
264: /**
265: * This will clear the warnings from the connection object.
266: * Some drivers will not replace existing warnings until clearWarnings()
267: * is called, thus SQL Workbench would show the same error message over and
268: * over again.
269: * This method also works around a bug in the Oracle JDBC driver, because
270: * that does not properly clear the warnings list.
271: */
272: public void clearWarnings() {
273: this .scriptError = null;
274: if (this .sqlConnection == null)
275: return;
276: try {
277: this .sqlConnection.clearWarnings();
278:
279: if (doOracleClear) {
280: // obviously the Oracle driver does NOT clear the warnings
281: // (as discovered when looking at the source code)
282: // luckily the instance variable on the driver which holds the
283: // warnings is defined as public and thus we can
284: // reset the warnings "manually"
285: // This is done via reflection so that the Oracle driver
286: // does not need to be present when compiling
287: // this is not true for newer drivers (10.x)
288:
289: if (this .clearSettings == null || dbAccess == null) {
290: Class ora = this .sqlConnection.getClass();
291:
292: if (ora.getName().equals(
293: "oracle.jdbc.driver.OracleConnection")) {
294: Field dbAccessField = ora.getField("db_access");
295: Class dbAccessClass = dbAccessField.getType();
296: dbAccess = dbAccessField
297: .get(this .sqlConnection);
298: try {
299: clearSettings = dbAccessClass.getMethod(
300: "setWarnings",
301: new Class[] { SQLWarning.class });
302: } catch (Throwable e) {
303: // newer drivers do not seem to support this any more,
304: // so after the first error, we'll skip this for the rest of the session
305: doOracleClear = false;
306: clearSettings = null;
307: }
308: }
309: }
310: // the following line is equivalent to:
311: // OracleConnection con = (OracleConnection)this.sqlConnection;
312: // con.db_access.setWarnings(null);
313: if (clearSettings != null)
314: clearSettings.invoke(dbAccess,
315: new Object[] { null });
316: }
317: } catch (Throwable th) {
318: LogMgr.logWarning("WbConnection.clearWarnings()",
319: "Error resetting warnings!", th);
320: }
321: }
322:
323: public Connection getSqlConnection() {
324: return this .sqlConnection;
325: }
326:
327: public void commit() throws SQLException {
328: this .sqlConnection.commit();
329: }
330:
331: public Savepoint setSavepoint() throws SQLException {
332: if (this .getAutoCommit())
333: return null;
334: return this .sqlConnection.setSavepoint();
335: }
336:
337: /**
338: * A non-exception throwing wrapper around Connection.rollback(Savepoint)
339: */
340: public void rollback(Savepoint sp) {
341: if (sp == null)
342: return;
343: try {
344: this .sqlConnection.rollback(sp);
345: } catch (Throwable e) {
346: LogMgr.logError("WbConnection.rollback(Savepoint)",
347: "Error releasing savepoint", e);
348: }
349: }
350:
351: /**
352: * A non-exception throwing wrapper around Connection.releaseSavepoint(Savepoint)
353: */
354: public void releaseSavepoint(Savepoint sp) {
355: if (sp == null)
356: return;
357: try {
358: if (!this .getAutoCommit()) {
359: this .sqlConnection.releaseSavepoint(sp);
360: }
361: } catch (Throwable e) {
362: LogMgr.logError("WbConnection.releaseSavepoint",
363: "Error releasing savepoint", e);
364: }
365: }
366:
367: /**
368: * Execute a rollback on the connection.
369: */
370: public void rollback() throws SQLException {
371: this .sqlConnection.rollback();
372: }
373:
374: public boolean getIgnoreDropErrors() {
375: if (this .profile != null) {
376: return this .profile.getIgnoreDropErrors();
377: } else {
378: return false;
379: }
380: }
381:
382: public void toggleAutoCommit() {
383: boolean flag = this .getAutoCommit();
384: try {
385: this .setAutoCommit(!flag);
386: } catch (Exception e) {
387: LogMgr.logWarning("WbConnection.toggleAutoCommit()",
388: "Error when switching autocommit to " + !flag, e);
389: }
390: }
391:
392: public void setAutoCommit(boolean flag) throws SQLException {
393: boolean old = this .getAutoCommit();
394: if (old != flag) {
395: this .sqlConnection.setAutoCommit(flag);
396: fireConnectionStateChanged(PROP_AUTOCOMMIT, Boolean
397: .toString(old), Boolean.toString(flag));
398: }
399: }
400:
401: /**
402: * Some DBMS (e.g. MySQL) seem to start a new transaction in default
403: * isolation mode. Which means that if the SELECT is not committed,
404: * no changes will be visible until a commit is issued.
405: * In the DbExplorer this is a problem, as the user has no way
406: * of sending a commit to end the transation if the DbExplorer
407: * uses a separate connection.
408: * The {@link workbench.gui.dbobjects.TableDataPanel} will issue
409: * a commit after retrieving the data if this method returns true.
410: *
411: * @see workbench.gui.dbobjects.TableDataPanel#doRetrieve(boolean)
412: * @see workbench.gui.dbobjects.TableDataPanel#showRowCount()
413: */
414: public boolean selectStartsTransaction() {
415: String key = "workbench.db." + this .metaData.getDbId()
416: + ".select.startstransaction";
417: boolean flag = Settings.getInstance().getBoolProperty(key,
418: false);
419: return flag;
420: }
421:
422: public boolean getAutoCommit() {
423: if (this .sqlConnection == null)
424: return false;
425: try {
426: return this .sqlConnection.getAutoCommit();
427: } catch (SQLException e) {
428: LogMgr.logWarning("WbConnection.getAutoCommit()",
429: "Error when retrieving autoCommit attribute", e);
430: return false;
431: }
432: }
433:
434: /**
435: * Disconnect this connection. This is delegated to the Connection Manager
436: * because for certain DBMS some cleanup works needs to be done.
437: * And the ConnectionMgr is the only one who knows if there are more connections
438: * around, which might influence what needs to be cleaned up
439: * (Currently this is only HSQLDB, but who knows...)
440: */
441: public void disconnect() {
442: ConnectionMgr.getInstance().disconnect(this );
443: if (this .preparedStatementPool != null) {
444: this .preparedStatementPool.done();
445: }
446: fireConnectionStateChanged(PROP_CONNECTION_STATE,
447: CONNECTION_OPEN, CONNECTION_CLOSED);
448: }
449:
450: /**
451: * This will actually close the connection to the DBMS.
452: * It will also free an resources from the DbMetadata object and
453: * shutdown the keep alive thread.
454: */
455: public void close() {
456: if (this .keepAlive != null) {
457: this .keepAlive.shutdown();
458: this .keepAlive = null;
459: }
460:
461: if (this .profile != null
462: && this .profile.getRollbackBeforeDisconnect()
463: && this .sqlConnection != null) {
464: try {
465: this .rollback();
466: } catch (Exception e) {
467: LogMgr
468: .logWarning(
469: "WbConnection.close()",
470: "Error when calling rollback before disconnect",
471: e);
472: }
473: }
474:
475: try {
476: if (this .metaData != null)
477: this .metaData.close();
478: if (this .sqlConnection != null)
479: this .sqlConnection.close();
480: } catch (Throwable th) {
481: LogMgr.logWarning("WbConnection.close()",
482: "Error when closing connection", th);
483: } finally {
484: this .metaData = null;
485: this .sqlConnection = null;
486: }
487:
488: LogMgr.logDebug("WbConnection.close()", "Connection "
489: + this .getId() + " closed.");
490:
491: if (Settings.getInstance().getProperty(
492: "workbench.db.driver.log", null) != null) {
493: PrintWriter pw = DriverManager.getLogWriter();
494: FileUtil.closeQuitely(pw);
495: }
496:
497: }
498:
499: public boolean isClosed() {
500: if (this .sqlConnection == null)
501: return true;
502:
503: try {
504: return this .sqlConnection.isClosed();
505: } catch (Exception e) {
506: return true;
507: }
508: }
509:
510: /**
511: * Create a statement that produces ResultSets that
512: * are read only and forward only (for performance reasons)
513: *
514: * If the profile defined a default fetch size, this
515: * will be set as well.
516: *
517: * @throws java.sql.SQLException
518: */
519: public Statement createStatementForQuery() throws SQLException {
520: Statement stmt = null;
521: if (getDbSettings().allowsExtendedCreateStatement()) {
522: stmt = this .sqlConnection.createStatement(
523: ResultSet.TYPE_FORWARD_ONLY,
524: ResultSet.CONCUR_READ_ONLY);
525: } else {
526: stmt = this .sqlConnection.createStatement();
527: }
528:
529: try {
530: if (this .getProfile() != null) {
531: int fetchSize = this .getProfile().getFetchSize();
532: if (fetchSize > -1)
533: stmt.setFetchSize(fetchSize);
534: }
535: } catch (Exception e) {
536: LogMgr.logWarning("WbConnection.createStatementForQuery()",
537: "Error when setting the fetchSize: "
538: + ExceptionUtil.getDisplay(e));
539: }
540: return stmt;
541: }
542:
543: public Statement createStatement() throws SQLException {
544: return this .sqlConnection.createStatement();
545: }
546:
547: public boolean supportsSavepoints() {
548: if (this .sqlConnection == null)
549: return false;
550:
551: try {
552: return sqlConnection.getMetaData().supportsSavepoints();
553: } catch (Throwable e) {
554: return false;
555: }
556: }
557:
558: public boolean useJdbcCommit() {
559: return this .metaData.getDbSettings().useJdbcCommit();
560: }
561:
562: public DbSettings getDbSettings() {
563: return this .metaData.getDbSettings();
564: }
565:
566: public DbMetadata getMetadata() {
567: return this .metaData;
568: }
569:
570: public String getUrl() {
571: try {
572: return this .sqlConnection.getMetaData().getURL();
573: } catch (Throwable e) {
574: return null;
575: }
576: }
577:
578: public String toString() {
579: return getId() + ", " + getCurrentUser() + "@" + getUrl();
580: }
581:
582: /**
583: * Return a readable display of a connection.
584: * This might actually send a SELECT to the database to
585: * retrieve the current user or schema.
586: * @see #getCurrentUser()
587: * @see DbMetadata#getSchemaToUse()
588: * @see DbMetadata#getCurrentCatalog()
589: */
590: public String getDisplayString() {
591: String displayString = null;
592: try {
593: DbMetadata meta = getMetadata();
594: StringBuilder buff = new StringBuilder(100);
595: String user = getCurrentUser();
596: buff.append(ResourceMgr.getString("TxtUser"));
597: buff.append('=');
598: buff.append(user);
599:
600: String catalog = meta.getCurrentCatalog();
601: if (catalog != null && catalog.length() > 0) {
602: String catName = meta.getCatalogTerm();
603: buff.append(", ");
604: buff.append(catName == null ? "Catalog" : StringUtil
605: .capitalize(catName));
606: buff.append('=');
607: buff.append(catalog);
608: }
609:
610: String schema = meta.getSchemaToUse();
611: if (schema != null && !schema.equalsIgnoreCase(user)) {
612: String schemaName = meta.getSchemaTerm();
613: buff.append(", ");
614: buff.append(schemaName == null ? "Schema" : StringUtil
615: .capitalize(schemaName));
616: buff.append('=');
617: buff.append(schema);
618: }
619:
620: buff.append(", URL=");
621: if (getProfile() != null) {
622: buff.append(getProfile().getUrl());
623: } else {
624: buff.append(getSqlConnection().getMetaData().getURL());
625: }
626: displayString = buff.toString();
627: } catch (Exception e) {
628: LogMgr.logError("ConnectionMgr.getDisplayString()",
629: "Could not retrieve connection information", e);
630: displayString = getCurrentUser() + "@" + getUrl();
631: }
632: return displayString;
633: }
634:
635: public String getDatabaseVersion() {
636: try {
637: DatabaseMetaData jdbcmeta = metaData.getJdbcMetadata();
638: int major = jdbcmeta.getDatabaseMajorVersion();
639: int minor = jdbcmeta.getDatabaseMinorVersion();
640: return major + "." + minor;
641: } catch (Throwable e) {
642: LogMgr.logError("WbConnection.getDatabaseVersion()",
643: "Error retrieving DB version", e);
644: return "n/a";
645: }
646: }
647:
648: public String getDatabaseProductName() {
649: return this .metaData.getProductName();
650: }
651:
652: public String getOutputMessages() {
653: return this .metaData.getOutputMessages();
654: }
655:
656: public int hashCode() {
657: return this .id.hashCode();
658: }
659:
660: public boolean equals(Object o) {
661: if (o instanceof WbConnection) {
662: return (this .id.equals(((WbConnection) o).id));
663: }
664: return false;
665: }
666:
667: public StrBuffer getDatabaseInfoAsXml(StrBuffer indent) {
668: return this .getDatabaseInfoAsXml(indent, null);
669: }
670:
671: public String getDriverVersion() {
672: DatabaseMetaData db = null;
673: try {
674: db = this .sqlConnection.getMetaData();
675: return db.getDriverVersion();
676: } catch (Throwable e) {
677: LogMgr.logError("WbConnection.getDriverVersion()",
678: "Error retrieving driver version", e);
679: return "n/a";
680: }
681: }
682:
683: /**
684: * Returns information about the DBMS and the JDBC driver
685: * in the XML format used for the XML export
686: */
687: public StrBuffer getDatabaseInfoAsXml(StrBuffer indent,
688: String namespace) {
689: StrBuffer dbInfo = new StrBuffer(200);
690: DatabaseMetaData db = null;
691: try {
692: db = this .sqlConnection.getMetaData();
693: } catch (Exception e) {
694: return new StrBuffer("");
695: }
696:
697: TagWriter tagWriter = new TagWriter(namespace);
698: String value = null;
699:
700: tagWriter.appendTag(dbInfo, indent, "created", StringUtil
701: .getCurrentTimestampWithTZString());
702:
703: try {
704: value = db.getDriverName();
705: } catch (Throwable th) {
706: value = "n/a";
707: }
708: tagWriter.appendTag(dbInfo, indent, "jdbc-driver",
709: cleanValue(value));
710:
711: try {
712: value = db.getDriverVersion();
713: } catch (Throwable th) {
714: value = "n/a";
715: }
716: tagWriter.appendTag(dbInfo, indent, "jdbc-driver-version",
717: cleanValue(value));
718:
719: tagWriter.appendTag(dbInfo, indent, "connection", this
720: .getDisplayString());
721:
722: try {
723: value = db.getDatabaseProductName();
724: } catch (Throwable th) {
725: value = "n/a";
726: }
727: tagWriter.appendTag(dbInfo, indent, "database-product-name",
728: cleanValue(value));
729:
730: try {
731: value = db.getDatabaseProductVersion();
732: } catch (Throwable th) {
733: value = "n/a";
734: }
735: tagWriter.appendTag(dbInfo, indent, "database-product-version",
736: cleanValue(value));
737:
738: return dbInfo;
739: }
740:
741: /**
742: * Some DBMS have strange characters when reporting their name
743: * This method ensures that an XML "compatible" value is returned in
744: * getDatabaseInfoAsXml
745: */
746: private String cleanValue(String value) {
747: if (value == null)
748: return null;
749: int len = value.length();
750: StringBuilder result = new StringBuilder(len);
751: for (int i = 0; i < len; i++) {
752: char c = value.charAt(i);
753: if ((c > 32 && c != 127) || c == 9 || c == 10 || c == 13) {
754: result.append(c);
755: } else {
756: result.append(' ');
757: }
758: }
759: return result.toString();
760: }
761:
762: /**
763: * Some DBMS need to commit DDL (CREATE, DROP, ...) statements.
764: * If the current connection needs a commit for a DDL, this will return true.
765: * The metadata class reads the names of those DBMS from the Settings object!
766: */
767: protected boolean getDDLNeedsCommit() {
768: return this .metaData.getDbSettings().ddlNeedsCommit();
769: }
770:
771: /**
772: * Checks if DDL statement need a commit for this connection.
773: *
774: * @return false if autocommit is on or the DBMS does not support DDL transactions
775: * @see #getDdlNeedsCommit()
776: */
777: public boolean shouldCommitDDL() {
778: if (this .getAutoCommit())
779: return false;
780: return this .getDDLNeedsCommit();
781: }
782:
783: public synchronized void addChangeListener(PropertyChangeListener l) {
784: if (this .listeners == null)
785: this .listeners = new ArrayList<PropertyChangeListener>();
786: this .listeners.add(l);
787: }
788:
789: public synchronized void removeChangeListener(
790: PropertyChangeListener l) {
791: if (this .listeners == null)
792: return;
793: this .listeners.remove(l);
794: }
795:
796: private void fireConnectionStateChanged(String property,
797: String oldValue, String newValue) {
798: if (this .listeners != null) {
799: int count = this .listeners.size();
800: PropertyChangeEvent evt = new PropertyChangeEvent(this ,
801: property, oldValue, newValue);
802: for (int i = 0; i < count; i++) {
803: PropertyChangeListener l = this .listeners.get(i);
804: l.propertyChange(evt);
805: }
806: }
807: }
808:
809: public void catalogChanged(String oldCatalog, String newCatalog) {
810: this .fireConnectionStateChanged(PROP_CATALOG, oldCatalog,
811: newCatalog);
812: }
813:
814: public void schemaChanged(String oldSchema, String newSchema) {
815: this .fireConnectionStateChanged(PROP_SCHEMA, oldSchema,
816: newSchema);
817: }
818:
819: private void initKeepAlive() {
820: if (this .keepAlive != null) {
821: this .keepAlive.shutdown();
822: this .keepAlive = null;
823: }
824:
825: if (this .profile == null)
826: return;
827: String sql = this .profile.getIdleScript();
828: if (sql == null || sql.trim().length() == 0)
829: return;
830: long idleTime = this .profile.getIdleTime();
831: if (idleTime <= 0)
832: return;
833: this .keepAlive = new KeepAliveDaemon(idleTime, this , sql);
834: this .keepAlive.startThread();
835: }
836:
837: public boolean isBusy() {
838: return this .busy;
839: }
840:
841: public void setBusy(boolean flag) {
842: this .busy = flag;
843: if (flag && this .keepAlive != null) {
844: this .keepAlive.setLastDbAction(System.currentTimeMillis());
845: }
846: }
847:
848: public void executionStart(WbConnection conn, Object source) {
849: if (conn == this ) {
850: setBusy(true);
851: }
852: }
853:
854: /*
855: * Fired by the SqlPanel if DB access finished
856: */
857: public void executionEnd(WbConnection conn, Object source) {
858: if (conn == this ) {
859: setBusy(false);
860: }
861: }
862: }
|