001 /*
002 * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.sql;
027
028 import java.util.Iterator;
029 import java.sql.Driver;
030 import java.util.ServiceLoader;
031 import java.security.AccessController;
032 import java.security.PrivilegedAction;
033
034 /**
035 * <P>The basic service for managing a set of JDBC drivers.<br>
036 * <B>NOTE:</B> The {@link <code>DataSource</code>} interface, new in the
037 * JDBC 2.0 API, provides another way to connect to a data source.
038 * The use of a <code>DataSource</code> object is the preferred means of
039 * connecting to a data source.
040 *
041 * <P>As part of its initialization, the <code>DriverManager</code> class will
042 * attempt to load the driver classes referenced in the "jdbc.drivers"
043 * system property. This allows a user to customize the JDBC Drivers
044 * used by their applications. For example in your
045 * ~/.hotjava/properties file you might specify:
046 * <pre>
047 * <CODE>jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.taste.ourDriver</CODE>
048 * </pre>
049 *<P> The <code>DriverManager</code> methods <code>getConnection</code> and
050 * <code>getDrivers</code> have been enhanced to support the Java Standard Edition
051 * <a href="../../../technotes/guides/jar/jar.html#Service%20Provider">Service Provider</a> mechanism. JDBC 4.0 Drivers must
052 * include the file <code>META-INF/services/java.sql.Driver</code>. This file contains the name of the JDBC drivers
053 * implementation of <code>java.sql.Driver</code>. For example, to load the <code>my.sql.Driver</code> class,
054 * the <code>META-INF/services/java.sql.Driver</code> file would contain the entry:
055 * <pre>
056 * <code>my.sql.Driver</code>
057 * </pre>
058 *
059 * <P>Applications no longer need to explictly load JDBC drivers using <code>Class.forName()</code>. Existing programs
060 * which currently load JDBC drivers using <code>Class.forName()</code> will continue to work without
061 * modification.
062 *
063 * <P>When the method <code>getConnection</code> is called,
064 * the <code>DriverManager</code> will attempt to
065 * locate a suitable driver from amongst those loaded at
066 * initialization and those loaded explicitly using the same classloader
067 * as the current applet or application.
068 *
069 * <P>
070 * Starting with the Java 2 SDK, Standard Edition, version 1.3, a
071 * logging stream can be set only if the proper
072 * permission has been granted. Normally this will be done with
073 * the tool PolicyTool, which can be used to grant <code>permission
074 * java.sql.SQLPermission "setLog"</code>.
075 * @see Driver
076 * @see Connection
077 */
078 public class DriverManager {
079
080 /**
081 * The <code>SQLPermission</code> constant that allows the
082 * setting of the logging stream.
083 * @since 1.3
084 */
085 final static SQLPermission SET_LOG_PERMISSION = new SQLPermission(
086 "setLog");
087
088 //--------------------------JDBC 2.0-----------------------------
089
090 /**
091 * Retrieves the log writer.
092 *
093 * The <code>getLogWriter</code> and <code>setLogWriter</code>
094 * methods should be used instead
095 * of the <code>get/setlogStream</code> methods, which are deprecated.
096 * @return a <code>java.io.PrintWriter</code> object
097 * @see #setLogWriter
098 * @since 1.2
099 */
100 public static java.io.PrintWriter getLogWriter() {
101 return logWriter;
102 }
103
104 /**
105 * Sets the logging/tracing <code>PrintWriter</code> object
106 * that is used by the <code>DriverManager</code> and all drivers.
107 * <P>
108 * There is a minor versioning problem created by the introduction
109 * of the method <code>setLogWriter</code>. The
110 * method <code>setLogWriter</code> cannot create a <code>PrintStream</code> object
111 * that will be returned by <code>getLogStream</code>---the Java platform does
112 * not provide a backward conversion. As a result, a new application
113 * that uses <code>setLogWriter</code> and also uses a JDBC 1.0 driver that uses
114 * <code>getLogStream</code> will likely not see debugging information written
115 * by that driver.
116 *<P>
117 * Starting with the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
118 * to see that there is an <code>SQLPermission</code> object before setting
119 * the logging stream. If a <code>SecurityManager</code> exists and its
120 * <code>checkPermission</code> method denies setting the log writer, this
121 * method throws a <code>java.lang.SecurityException</code>.
122 *
123 * @param out the new logging/tracing <code>PrintStream</code> object;
124 * <code>null</code> to disable logging and tracing
125 * @throws SecurityException
126 * if a security manager exists and its
127 * <code>checkPermission</code> method denies
128 * setting the log writer
129 *
130 * @see SecurityManager#checkPermission
131 * @see #getLogWriter
132 * @since 1.2
133 */
134 public static void setLogWriter(java.io.PrintWriter out) {
135
136 SecurityManager sec = System.getSecurityManager();
137 if (sec != null) {
138 sec.checkPermission(SET_LOG_PERMISSION);
139 }
140 logStream = null;
141 logWriter = out;
142 }
143
144 //---------------------------------------------------------------
145
146 /**
147 * Attempts to establish a connection to the given database URL.
148 * The <code>DriverManager</code> attempts to select an appropriate driver from
149 * the set of registered JDBC drivers.
150 *
151 * @param url a database url of the form
152 * <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
153 * @param info a list of arbitrary string tag/value pairs as
154 * connection arguments; normally at least a "user" and
155 * "password" property should be included
156 * @return a Connection to the URL
157 * @exception SQLException if a database access error occurs
158 */
159 public static Connection getConnection(String url,
160 java.util.Properties info) throws SQLException {
161
162 // Gets the classloader of the code that called this method, may
163 // be null.
164 ClassLoader callerCL = DriverManager.getCallerClassLoader();
165
166 return (getConnection(url, info, callerCL));
167 }
168
169 /**
170 * Attempts to establish a connection to the given database URL.
171 * The <code>DriverManager</code> attempts to select an appropriate driver from
172 * the set of registered JDBC drivers.
173 *
174 * @param url a database url of the form
175 * <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
176 * @param user the database user on whose behalf the connection is being
177 * made
178 * @param password the user's password
179 * @return a connection to the URL
180 * @exception SQLException if a database access error occurs
181 */
182 public static Connection getConnection(String url, String user,
183 String password) throws SQLException {
184 java.util.Properties info = new java.util.Properties();
185
186 // Gets the classloader of the code that called this method, may
187 // be null.
188 ClassLoader callerCL = DriverManager.getCallerClassLoader();
189
190 if (user != null) {
191 info.put("user", user);
192 }
193 if (password != null) {
194 info.put("password", password);
195 }
196
197 return (getConnection(url, info, callerCL));
198 }
199
200 /**
201 * Attempts to establish a connection to the given database URL.
202 * The <code>DriverManager</code> attempts to select an appropriate driver from
203 * the set of registered JDBC drivers.
204 *
205 * @param url a database url of the form
206 * <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
207 * @return a connection to the URL
208 * @exception SQLException if a database access error occurs
209 */
210 public static Connection getConnection(String url)
211 throws SQLException {
212
213 java.util.Properties info = new java.util.Properties();
214
215 // Gets the classloader of the code that called this method, may
216 // be null.
217 ClassLoader callerCL = DriverManager.getCallerClassLoader();
218
219 return (getConnection(url, info, callerCL));
220 }
221
222 /**
223 * Attempts to locate a driver that understands the given URL.
224 * The <code>DriverManager</code> attempts to select an appropriate driver from
225 * the set of registered JDBC drivers.
226 *
227 * @param url a database URL of the form
228 * <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
229 * @return a <code>Driver</code> object representing a driver
230 * that can connect to the given URL
231 * @exception SQLException if a database access error occurs
232 */
233 public static Driver getDriver(String url) throws SQLException {
234 java.util.Vector drivers = null;
235
236 println("DriverManager.getDriver(\"" + url + "\")");
237
238 if (!initialized) {
239 initialize();
240 }
241
242 synchronized (DriverManager.class) {
243 // use the read copy of the drivers vector
244 drivers = readDrivers;
245 }
246
247 // Gets the classloader of the code that called this method, may
248 // be null.
249 ClassLoader callerCL = DriverManager.getCallerClassLoader();
250
251 // Walk through the loaded drivers attempting to locate someone
252 // who understands the given URL.
253 for (int i = 0; i < drivers.size(); i++) {
254 DriverInfo di = (DriverInfo) drivers.elementAt(i);
255 // If the caller does not have permission to load the driver then
256 // skip it.
257 if (getCallerClass(callerCL, di.driverClassName) != di.driverClass) {
258 println(" skipping: " + di);
259 continue;
260 }
261 try {
262 println(" trying " + di);
263 if (di.driver.acceptsURL(url)) {
264 // Success!
265 println("getDriver returning " + di);
266 return (di.driver);
267 }
268 } catch (SQLException ex) {
269 // Drop through and try the next driver.
270 }
271 }
272
273 println("getDriver: no suitable driver");
274 throw new SQLException("No suitable driver", "08001");
275 }
276
277 /**
278 * Registers the given driver with the <code>DriverManager</code>.
279 * A newly-loaded driver class should call
280 * the method <code>registerDriver</code> to make itself
281 * known to the <code>DriverManager</code>.
282 *
283 * @param driver the new JDBC Driver that is to be registered with the
284 * <code>DriverManager</code>
285 * @exception SQLException if a database access error occurs
286 */
287 public static synchronized void registerDriver(
288 java.sql.Driver driver) throws SQLException {
289 if (!initialized) {
290 initialize();
291 }
292
293 DriverInfo di = new DriverInfo();
294
295 di.driver = driver;
296 di.driverClass = driver.getClass();
297 di.driverClassName = di.driverClass.getName();
298
299 // Not Required -- drivers.addElement(di);
300
301 writeDrivers.addElement(di);
302 println("registerDriver: " + di);
303
304 /* update the read copy of drivers vector */
305 readDrivers = (java.util.Vector) writeDrivers.clone();
306
307 }
308
309 /**
310 * Drops a driver from the <code>DriverManager</code>'s list.
311 * Applets can only deregister drivers from their own classloaders.
312 *
313 * @param driver the JDBC Driver to drop
314 * @exception SQLException if a database access error occurs
315 */
316 public static synchronized void deregisterDriver(Driver driver)
317 throws SQLException {
318 // Gets the classloader of the code that called this method,
319 // may be null.
320 ClassLoader callerCL = DriverManager.getCallerClassLoader();
321 println("DriverManager.deregisterDriver: " + driver);
322
323 // Walk through the loaded drivers.
324 int i;
325 DriverInfo di = null;
326 for (i = 0; i < writeDrivers.size(); i++) {
327 di = (DriverInfo) writeDrivers.elementAt(i);
328 if (di.driver == driver) {
329 break;
330 }
331 }
332 // If we can't find the driver just return.
333 if (i >= writeDrivers.size()) {
334 println(" couldn't find driver to unload");
335 return;
336 }
337
338 // If the caller does not have permission to load the driver then
339 // throw a security exception.
340 if (getCallerClass(callerCL, di.driverClassName) != di.driverClass) {
341 throw new SecurityException();
342 }
343
344 // Remove the driver. Other entries in drivers get shuffled down.
345 writeDrivers.removeElementAt(i);
346
347 /* update the read copy of drivers vector */
348 readDrivers = (java.util.Vector) writeDrivers.clone();
349 }
350
351 /**
352 * Retrieves an Enumeration with all of the currently loaded JDBC drivers
353 * to which the current caller has access.
354 *
355 * <P><B>Note:</B> The classname of a driver can be found using
356 * <CODE>d.getClass().getName()</CODE>
357 *
358 * @return the list of JDBC Drivers loaded by the caller's class loader
359 */
360 public static java.util.Enumeration<Driver> getDrivers() {
361 java.util.Vector<Driver> result = new java.util.Vector<Driver>();
362 java.util.Vector drivers = null;
363
364 if (!initialized) {
365 initialize();
366 }
367
368 synchronized (DriverManager.class) {
369 // use the readcopy of drivers
370 drivers = readDrivers;
371 }
372
373 // Gets the classloader of the code that called this method, may
374 // be null.
375 ClassLoader callerCL = DriverManager.getCallerClassLoader();
376
377 // Walk through the loaded drivers.
378 for (int i = 0; i < drivers.size(); i++) {
379 DriverInfo di = (DriverInfo) drivers.elementAt(i);
380 // If the caller does not have permission to load the driver then
381 // skip it.
382 if (getCallerClass(callerCL, di.driverClassName) != di.driverClass) {
383 println(" skipping: " + di);
384 continue;
385 }
386 result.addElement(di.driver);
387 }
388
389 return (result.elements());
390 }
391
392 /**
393 * Sets the maximum time in seconds that a driver will wait
394 * while attempting to connect to a database.
395 *
396 * @param seconds the login time limit in seconds; zero means there is no limit
397 * @see #getLoginTimeout
398 */
399 public static void setLoginTimeout(int seconds) {
400 loginTimeout = seconds;
401 }
402
403 /**
404 * Gets the maximum time in seconds that a driver can wait
405 * when attempting to log in to a database.
406 *
407 * @return the driver login time limit in seconds
408 * @see #setLoginTimeout
409 */
410 public static int getLoginTimeout() {
411 return (loginTimeout);
412 }
413
414 /**
415 * Sets the logging/tracing PrintStream that is used
416 * by the <code>DriverManager</code>
417 * and all drivers.
418 *<P>
419 * In the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
420 * to see that there is an <code>SQLPermission</code> object before setting
421 * the logging stream. If a <code>SecurityManager</code> exists and its
422 * <code>checkPermission</code> method denies setting the log writer, this
423 * method throws a <code>java.lang.SecurityException</code>.
424 *
425 * @param out the new logging/tracing PrintStream; to disable, set to <code>null</code>
426 * @deprecated
427 * @throws SecurityException if a security manager exists and its
428 * <code>checkPermission</code> method denies setting the log stream
429 *
430 * @see SecurityManager#checkPermission
431 * @see #getLogStream
432 */
433 public static void setLogStream(java.io.PrintStream out) {
434
435 SecurityManager sec = System.getSecurityManager();
436 if (sec != null) {
437 sec.checkPermission(SET_LOG_PERMISSION);
438 }
439
440 logStream = out;
441 if (out != null)
442 logWriter = new java.io.PrintWriter(out);
443 else
444 logWriter = null;
445 }
446
447 /**
448 * Retrieves the logging/tracing PrintStream that is used by the <code>DriverManager</code>
449 * and all drivers.
450 *
451 * @return the logging/tracing PrintStream; if disabled, is <code>null</code>
452 * @deprecated
453 * @see #setLogStream
454 */
455 public static java.io.PrintStream getLogStream() {
456 return logStream;
457 }
458
459 /**
460 * Prints a message to the current JDBC log stream.
461 *
462 * @param message a log or tracing message
463 */
464 public static void println(String message) {
465 synchronized (logSync) {
466 if (logWriter != null) {
467 logWriter.println(message);
468
469 // automatic flushing is never enabled, so we must do it ourselves
470 logWriter.flush();
471 }
472 }
473 }
474
475 //------------------------------------------------------------------------
476
477 // Returns the class object that would be created if the code calling the
478 // driver manager had loaded the driver class, or null if the class
479 // is inaccessible.
480 private static Class getCallerClass(ClassLoader callerClassLoader,
481 String driverClassName) {
482 Class callerC = null;
483
484 try {
485 callerC = Class.forName(driverClassName, true,
486 callerClassLoader);
487 } catch (Exception ex) {
488 callerC = null; // being very careful
489 }
490
491 return callerC;
492 }
493
494 private static void loadInitialDrivers() {
495 String drivers;
496 try {
497 drivers = (String) AccessController
498 .doPrivileged(new PrivilegedAction() {
499 public Object run() {
500 return System.getProperty("jdbc.drivers");
501 }
502 });
503 } catch (Exception ex) {
504 drivers = null;
505 }
506 // If the driver is packaged as a Service Provider, load it.
507 // Get all the drivers through the classloader
508 // exposed as a java.sql.Driver.class service.
509 // ServiceLoader.load() replaces the sun.misc.Providers()
510
511 AccessController.doPrivileged(new PrivilegedAction() {
512 public Object run() {
513
514 ServiceLoader<Driver> loadedDrivers = ServiceLoader
515 .load(Driver.class);
516 Iterator driversIterator = loadedDrivers.iterator();
517
518 /* Load these drivers, so that they can be instantiated.
519 * It may be the case that the driver class may not be there
520 * i.e. there may be a packaged driver with the service class
521 * as implementation of java.sql.Driver but the actual class
522 * may be missing. In that case a java.util.ServiceConfigurationError
523 * will be thrown at runtime by the VM trying to locate
524 * and load the service.
525 *
526 * Adding a try catch block to catch those runtime errors
527 * if driver not available in classpath but it's
528 * packaged as service and that service is there in classpath.
529 */
530 try {
531 while (driversIterator.hasNext()) {
532 println(" Loading done by the java.util.ServiceLoader : "
533 + driversIterator.next());
534 }
535 } catch (Throwable t) {
536 // Do nothing
537 }
538 return null;
539 }
540 });
541
542 println("DriverManager.initialize: jdbc.drivers = " + drivers);
543 if (drivers == null) {
544 return;
545 }
546 while (drivers.length() != 0) {
547 int x = drivers.indexOf(':');
548 String driver;
549 if (x < 0) {
550 driver = drivers;
551 drivers = "";
552 } else {
553 driver = drivers.substring(0, x);
554 drivers = drivers.substring(x + 1);
555 }
556 if (driver.length() == 0) {
557 continue;
558 }
559 try {
560 println("DriverManager.Initialize: loading " + driver);
561 Class.forName(driver, true, ClassLoader
562 .getSystemClassLoader());
563 } catch (Exception ex) {
564 println("DriverManager.Initialize: load failed: " + ex);
565 }
566 }
567 }
568
569 // Worker method called by the public getConnection() methods.
570 private static Connection getConnection(String url,
571 java.util.Properties info, ClassLoader callerCL)
572 throws SQLException {
573 java.util.Vector drivers = null;
574 /*
575 * When callerCl is null, we should check the application's
576 * (which is invoking this class indirectly)
577 * classloader, so that the JDBC driver class outside rt.jar
578 * can be loaded from here.
579 */
580 synchronized (DriverManager.class) {
581 // synchronize loading of the correct classloader.
582 if (callerCL == null) {
583 callerCL = Thread.currentThread()
584 .getContextClassLoader();
585 }
586 }
587
588 if (url == null) {
589 throw new SQLException("The url cannot be null", "08001");
590 }
591
592 println("DriverManager.getConnection(\"" + url + "\")");
593
594 if (!initialized) {
595 initialize();
596 }
597
598 synchronized (DriverManager.class) {
599 // use the readcopy of drivers
600 drivers = readDrivers;
601 }
602
603 // Walk through the loaded drivers attempting to make a connection.
604 // Remember the first exception that gets raised so we can reraise it.
605 SQLException reason = null;
606 for (int i = 0; i < drivers.size(); i++) {
607 DriverInfo di = (DriverInfo) drivers.elementAt(i);
608
609 // If the caller does not have permission to load the driver then
610 // skip it.
611 if (getCallerClass(callerCL, di.driverClassName) != di.driverClass) {
612 println(" skipping: " + di);
613 continue;
614 }
615 try {
616 println(" trying " + di);
617 Connection result = di.driver.connect(url, info);
618 if (result != null) {
619 // Success!
620 println("getConnection returning " + di);
621 return (result);
622 }
623 } catch (SQLException ex) {
624 if (reason == null) {
625 reason = ex;
626 }
627 }
628 }
629
630 // if we got here nobody could connect.
631 if (reason != null) {
632 println("getConnection failed: " + reason);
633 throw reason;
634 }
635
636 println("getConnection: no suitable driver found for " + url);
637 throw new SQLException("No suitable driver found for " + url,
638 "08001");
639 }
640
641 // Class initialization.
642 static void initialize() {
643 if (initialized) {
644 return;
645 }
646 initialized = true;
647 loadInitialDrivers();
648 println("JDBC DriverManager initialized");
649 }
650
651 /* Prevent the DriverManager class from being instantiated. */
652 private DriverManager() {
653 }
654
655 /* write copy of the drivers vector */
656 private static java.util.Vector writeDrivers = new java.util.Vector();
657
658 /* write copy of the drivers vector */
659 private static java.util.Vector readDrivers = new java.util.Vector();
660
661 private static int loginTimeout = 0;
662 private static java.io.PrintWriter logWriter = null;
663 private static java.io.PrintStream logStream = null;
664 private static boolean initialized = false;
665
666 private static Object logSync = new Object();
667
668 /* Returns the caller's class loader, or null if none */
669 private static native ClassLoader getCallerClassLoader();
670
671 }
672
673 // DriverInfo is a package-private support class.
674 class DriverInfo {
675 Driver driver;
676 Class driverClass;
677 String driverClassName;
678
679 public String toString() {
680 return ("driver[className=" + driverClassName + "," + driver + "]");
681 }
682 }
|