001: /*
002: * DbDriver.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.io.BufferedWriter;
015: import java.io.File;
016: import java.io.FileWriter;
017: import java.io.PrintWriter;
018: import java.net.InetAddress;
019: import java.net.URL;
020: import java.net.URLClassLoader;
021: import java.sql.Connection;
022: import java.sql.Driver;
023: import java.sql.SQLException;
024: import java.util.Enumeration;
025: import java.util.Properties;
026:
027: import workbench.log.LogMgr;
028: import workbench.resource.ResourceMgr;
029: import workbench.resource.Settings;
030: import workbench.util.StringUtil;
031: import java.sql.DriverManager;
032: import java.util.ArrayList;
033: import java.util.List;
034: import workbench.WbManager;
035:
036: /**
037: * Represents a JDBC Driver definition.
038: * The definition includes a (logical) name, a driver class
039: * and (optional) a library from which the driver is to
040: * be loaded.
041: *
042: * @author support@sql-workbench.net
043: */
044: public class DbDriver implements Comparable<DbDriver> {
045: private Driver driverClassInstance;
046: private URLClassLoader classLoader;
047:
048: protected String name;
049: private String driverClass;
050: private List<String> libraryList;
051:
052: private String sampleUrl;
053:
054: public DbDriver() {
055: }
056:
057: public DbDriver(Driver aDriverClassInstance) {
058: this .driverClassInstance = aDriverClassInstance;
059: this .driverClass = aDriverClassInstance.getClass().getName();
060: this .name = this .driverClass;
061: }
062:
063: public DbDriver(String aDriverClassname) {
064: this .setDriverClass(aDriverClassname);
065: this .setName(aDriverClassname);
066: }
067:
068: public DbDriver(String aName, String aClass, String aLibrary) {
069: this .setName(aName);
070: this .setDriverClass(aClass);
071: this .setLibrary(aLibrary);
072: }
073:
074: public String getName() {
075: return this .name;
076: }
077:
078: public void setName(String name) {
079: this .name = name;
080: }
081:
082: public String getDriverClass() {
083: return this .driverClass;
084: }
085:
086: public void setDriverClass(String aClass) {
087: this .driverClass = aClass.trim();
088: this .driverClassInstance = null;
089: this .classLoader = null;
090: }
091:
092: public String getDescription() {
093: StringBuilder b = new StringBuilder(100);
094: if (this .name != null) {
095: b.append(this .name);
096: b.append(" (");
097: b.append(this .driverClass);
098: b.append(')');
099: } else {
100: b.append(this .driverClass);
101: }
102: return b.toString();
103: }
104:
105: public String getLibraryString() {
106: return createLibraryString(StringUtil.getPathSeparator());
107: }
108:
109: private String createLibraryString(String separator) {
110: if (this .libraryList == null)
111: return null;
112: StringBuilder result = new StringBuilder(this .libraryList
113: .size() * 30);
114: for (int i = 0; i < libraryList.size(); i++) {
115: if (i > 0)
116: result.append(separator);
117: result.append(libraryList.get(i));
118: }
119: return result.toString();
120: }
121:
122: public String getLibrary() {
123: return createLibraryString("|");
124: }
125:
126: public void setLibrary(String libList) {
127: this .libraryList = null;
128:
129: if (libList.indexOf("|") > -1) {
130: this .libraryList = StringUtil.stringToList(libList, "|",
131: true, true, false);
132: } else if (!StringUtil.isEmptyString(libList)) {
133: this .libraryList = StringUtil.stringToList(libList,
134: StringUtil.getPathSeparator(), true, true, false);
135: }
136: this .driverClassInstance = null;
137: this .classLoader = null;
138: }
139:
140: public boolean canReadLibrary() {
141: if (WbManager.getInstance().isTestMode())
142: return true;
143: if (libraryList != null) {
144: for (String lib : libraryList) {
145: lib = Settings.getInstance().replaceLibDirKey(lib);
146: File f = new File(lib);
147: if (f.getParentFile() == null) {
148: f = new File(Settings.getInstance().getLibDir(),
149: lib);
150: }
151: if (!f.exists())
152: return false;
153: }
154: return true;
155: }
156: return false;
157: }
158:
159: public String toString() {
160: return this .getDescription();
161: }
162:
163: public void setSampleUrl(String anUrl) {
164: this .sampleUrl = anUrl;
165: }
166:
167: public String getSampleUrl() {
168: return this .sampleUrl;
169: }
170:
171: public Class loadClassFromDriverLib(String className)
172: throws ClassNotFoundException {
173: if (this .classLoader == null)
174: return null;
175: Class clz = this .classLoader.loadClass(className);
176: return clz;
177: }
178:
179: private void loadDriverClass() throws ClassNotFoundException,
180: Exception {
181: if (this .driverClassInstance != null)
182: return;
183:
184: try {
185: if (this .classLoader == null && this .libraryList != null) {
186: URL[] url = new URL[libraryList.size()];
187: int index = 0;
188: for (String fname : libraryList) {
189: String realFile = Settings.getInstance()
190: .replaceLibDirKey(fname);
191: File f = new File(realFile);
192: if (f.getParentFile() == null) {
193: f = new File(
194: Settings.getInstance().getLibDir(),
195: realFile);
196: }
197: url[index] = f.toURL();
198: LogMgr.logInfo("DbDriver.loadDriverClass()",
199: "Adding ClassLoader URL="
200: + url[index].toString());
201: index++;
202: }
203: this .classLoader = new URLClassLoader(url, ClassLoader
204: .getSystemClassLoader());
205: }
206:
207: Class drvClass = null;
208: if (this .classLoader != null) {
209: // New Firebird 2.0 driver needs this, and it does not seem to do any harm
210: // for other drivers
211: setContextClassLoader();
212: drvClass = this .classLoader.loadClass(this .driverClass);
213: } else {
214: // Assume the driver class is available on the classpath
215: //LogMgr.logDebug("DbDriver.loadDriverClass()", "Assuming driver " + this.driverClass + " is in current classpath");
216: drvClass = Class.forName(this .driverClass);
217: }
218:
219: this .driverClassInstance = (Driver) drvClass.newInstance();
220: if (Settings.getInstance().getBoolProperty(
221: "workbench.db.registerdriver", false)) {
222: // Some drivers expect to be registered with the DriverManager...
223: try {
224: LogMgr.logDebug("DbDriver.loadDriverClass()",
225: "Registering new driver instance for "
226: + this .driverClass
227: + " with DriverManager");
228: DriverManager
229: .registerDriver(this .driverClassInstance);
230: } catch (Throwable th) {
231: LogMgr
232: .logError(
233: "DbDriver.loadDriverClass()",
234: "Error registering driver instance with DriverManager",
235: th);
236: }
237: }
238:
239: String dbLog = Settings.getInstance().getProperty(
240: "workbench.db.driver.log", null);
241: if (!StringUtil.isEmptyString(dbLog)) {
242: try {
243: PrintWriter pw = new PrintWriter(
244: new BufferedWriter(new FileWriter(dbLog)));
245: DriverManager.setLogWriter(pw);
246: } catch (Exception e) {
247: LogMgr.logError("DbDriver.loadDriverClass()",
248: "Error setting driverManager logWriter", e);
249: }
250: }
251:
252: } catch (ClassNotFoundException e) {
253: LogMgr.logError("DbDriver.loadDriverClass()",
254: "Class not found when loading driver", e);
255: throw e;
256: } catch (Throwable e) {
257: this .classLoader = null;
258: LogMgr.logError("DbDriver.loadDriverClass()",
259: "Error loading driver class: " + this .driverClass,
260: e);
261: throw new Exception("Could not load driver class "
262: + this .driverClass);
263: }
264: }
265:
266: public DbDriver createCopy() {
267: DbDriver copy = new DbDriver();
268: copy.driverClass = this .driverClass;
269: copy.libraryList = new ArrayList<String>();
270: copy.libraryList.addAll(this .libraryList);
271: copy.sampleUrl = this .sampleUrl;
272: copy.name = this .name;
273: return copy;
274: }
275:
276: Connection connect(String url, String user, String password,
277: String id, Properties connProps)
278: throws ClassNotFoundException, SQLException {
279: Connection c = null;
280: try {
281: this .loadDriverClass();
282:
283: // as we are not using the DriverManager, we need to supply username
284: // and password in the connection properties!
285: Properties props = new Properties();
286: if (user != null && user.trim().length() > 0)
287: props.put("user", user);
288: if (password != null && password.trim().length() > 0)
289: props.put("password", password);
290:
291: // copy the user defined connection properties into the actually used ones!
292: if (connProps != null) {
293: Enumeration keys = connProps.propertyNames();
294: while (keys.hasMoreElements()) {
295: String key = (String) keys.nextElement();
296: if (!props.containsKey(key)) {
297: String value = connProps.getProperty(key);
298: props.put(key, value);
299: }
300: }
301: }
302:
303: // identify the program name when connecting
304: // this is different for each DBMS.
305: String propName = null;
306: if (url.startsWith("jdbc:oracle")) {
307: propName = "v$session.program";
308: if (id != null)
309: props.put("v$session.terminal", id);
310:
311: // it seems that the Oracle 10 driver does not
312: // add this to the properties automatically
313: // (as the drivers for 8 and 9 did)
314: user = System.getProperty("user.name", null);
315: if (user != null)
316: props.put("v$session.osuser", user);
317: } else if (url.startsWith("jdbc:inetdae")) {
318: propName = "appname";
319: } else if (url.startsWith("jdbc:jtds")) {
320: propName = "APPNAME";
321: } else if (url.startsWith("jdbc:microsoft:sqlserver")) {
322: // Old MS SQL Server driver
323: propName = "ProgramName";
324: } else if (url.startsWith("jdbc:sqlserver:")) {
325: // New SQL Server 2005 JDBC driver
326: propName = "applicationName";
327: if (!props.containsKey("workstationID")) {
328: InetAddress localhost = InetAddress.getLocalHost();
329: String localName = (localhost != null ? localhost
330: .getHostName() : null);
331: if (localName != null) {
332: props.put("workstationID", localName);
333: }
334: }
335: }
336: if (propName != null && !props.containsKey(propName)) {
337: String appName = ResourceMgr.TXT_PRODUCT_NAME + " ("
338: + ResourceMgr.getBuildNumber() + ")";
339: props.put(propName, appName);
340: }
341:
342: c = this .driverClassInstance.connect(url, props);
343: if (c == null) {
344: LogMgr.logError("DbDriver.connect()",
345: "No connection returned by driver "
346: + this .driverClass + " for URL=" + url,
347: null);
348: throw new SQLException(
349: "Driver did not return a connection for url="
350: + url);
351: }
352: } catch (ClassNotFoundException e) {
353: // do not log this error, the caller will log it
354: //LogMgr.logError("DbDriver.connect()", "Driver class not found", e);
355: throw e;
356: } catch (SQLException e) {
357: // do not log this error, the caller will log it
358: //LogMgr.logError("DbDriver.connect()", "Error connecting to driver " + this.driverClass, e);
359: throw e;
360: } catch (Throwable th) {
361: LogMgr.logError("DbDriver.connect()",
362: "Error connecting to driver " + this .driverClass,
363: th);
364: throw new SQLException("Error connecting to database. ("
365: + th.getClass().getName() + " - " + th.getMessage()
366: + ")");
367: }
368:
369: return c;
370: }
371:
372: /**
373: * This is a "simplified version of the connect() method
374: * for issuing a "shutdown command" to Cloudscape
375: */
376: public void commandConnect(String url) throws SQLException,
377: ClassNotFoundException, Exception {
378: this .loadDriverClass();
379: Properties props = new Properties();
380: LogMgr.logDebug("DbDriver.commandConnect()",
381: "Sending command URL=" + url + " to database");
382: this .driverClassInstance.connect(url, props);
383: }
384:
385: public boolean equals(Object other) {
386: if (other == null)
387: return false;
388: if (this .driverClass == null)
389: return false;
390:
391: if (other instanceof DbDriver) {
392: DbDriver o = (DbDriver) other;
393: if (o.driverClass != null
394: && o.driverClass.equals(this .driverClass)) {
395: return (this .name != null && this .name
396: .equalsIgnoreCase(o.name));
397: } else {
398: return false;
399: }
400: } else if (other instanceof String) {
401: return (this .driverClass != null && this .driverClass
402: .equals((String) other));
403: } else {
404: return false;
405: }
406: }
407:
408: private void setContextClassLoader() {
409: if (this .classLoader != null) {
410: Thread.currentThread().setContextClassLoader(
411: this .classLoader);
412: }
413: }
414:
415: protected String getId() {
416: StringBuilder b = new StringBuilder(60);
417: b.append(driverClass == null ? "" : driverClass);
418: b.append('$');
419: b.append(name);
420: return b.toString();
421: }
422:
423: public int hashCode() {
424: return getId().hashCode();
425: }
426:
427: public int compareTo(DbDriver o) {
428: return getDescription().compareTo(o.getDescription());
429: }
430:
431: }
|