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