1 /*
2 * Copyright (c) 1996, 2011, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.sql;
27
28 import java.util.Iterator;
29 import java.util.ServiceLoader;
30 import java.security.AccessController;
31 import java.security.PrivilegedAction;
32 import java.util.concurrent.CopyOnWriteArrayList;
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 // List of registered JDBC drivers
83 private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();
84 private static volatile int loginTimeout = 0;
85 private static volatile java.io.PrintWriter logWriter = null;
86 private static volatile java.io.PrintStream logStream = null;
87 // Used in println() to synchronize logWriter
88 private final static Object logSync = new Object();
89
90 /* Prevent the DriverManager class from being instantiated. */
91 private DriverManager(){}
92
93
94 /**
95 * Load the initial JDBC drivers by checking the System property
96 * jdbc.properties and then use the {@code ServiceLoader} mechanism
97 */
98 static {
99 loadInitialDrivers();
100 println("JDBC DriverManager initialized");
101 }
102
103 /**
104 * The <code>SQLPermission</code> constant that allows the
105 * setting of the logging stream.
106 * @since 1.3
107 */
108 final static SQLPermission SET_LOG_PERMISSION =
109 new SQLPermission("setLog");
110
111 //--------------------------JDBC 2.0-----------------------------
112
113 /**
114 * Retrieves the log writer.
115 *
116 * The <code>getLogWriter</code> and <code>setLogWriter</code>
117 * methods should be used instead
118 * of the <code>get/setlogStream</code> methods, which are deprecated.
119 * @return a <code>java.io.PrintWriter</code> object
120 * @see #setLogWriter
121 * @since 1.2
122 */
123 public static java.io.PrintWriter getLogWriter() {
124 return logWriter;
125 }
126
127 /**
128 * Sets the logging/tracing <code>PrintWriter</code> object
129 * that is used by the <code>DriverManager</code> and all drivers.
130 * <P>
131 * There is a minor versioning problem created by the introduction
132 * of the method <code>setLogWriter</code>. The
133 * method <code>setLogWriter</code> cannot create a <code>PrintStream</code> object
134 * that will be returned by <code>getLogStream</code>---the Java platform does
135 * not provide a backward conversion. As a result, a new application
136 * that uses <code>setLogWriter</code> and also uses a JDBC 1.0 driver that uses
137 * <code>getLogStream</code> will likely not see debugging information written
138 * by that driver.
139 *<P>
140 * Starting with the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
141 * to see that there is an <code>SQLPermission</code> object before setting
142 * the logging stream. If a <code>SecurityManager</code> exists and its
143 * <code>checkPermission</code> method denies setting the log writer, this
144 * method throws a <code>java.lang.SecurityException</code>.
145 *
146 * @param out the new logging/tracing <code>PrintStream</code> object;
147 * <code>null</code> to disable logging and tracing
148 * @throws SecurityException
149 * if a security manager exists and its
150 * <code>checkPermission</code> method denies
151 * setting the log writer
152 *
153 * @see SecurityManager#checkPermission
154 * @see #getLogWriter
155 * @since 1.2
156 */
157 public static void setLogWriter(java.io.PrintWriter out) {
158
159 SecurityManager sec = System.getSecurityManager();
160 if (sec != null) {
161 sec.checkPermission(SET_LOG_PERMISSION);
162 }
163 logStream = null;
164 logWriter = out;
165 }
166
167
168 //---------------------------------------------------------------
169
170 /**
171 * Attempts to establish a connection to the given database URL.
172 * The <code>DriverManager</code> attempts to select an appropriate driver from
173 * the set of registered JDBC drivers.
174 *
175 * @param url a database url of the form
176 * <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
177 * @param info a list of arbitrary string tag/value pairs as
178 * connection arguments; normally at least a "user" and
179 * "password" property should be included
180 * @return a Connection to the URL
181 * @exception SQLException if a database access error occurs
182 */
183 public static Connection getConnection(String url,
184 java.util.Properties info) throws SQLException {
185
186 // Gets the classloader of the code that called this method, may
187 // be null.
188 ClassLoader callerCL = DriverManager.getCallerClassLoader();
189
190 return (getConnection(url, info, callerCL));
191 }
192
193 /**
194 * Attempts to establish a connection to the given database URL.
195 * The <code>DriverManager</code> attempts to select an appropriate driver from
196 * the set of registered JDBC drivers.
197 *
198 * @param url a database url of the form
199 * <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
200 * @param user the database user on whose behalf the connection is being
201 * made
202 * @param password the user's password
203 * @return a connection to the URL
204 * @exception SQLException if a database access error occurs
205 */
206 public static Connection getConnection(String url,
207 String user, String password) throws SQLException {
208 java.util.Properties info = new java.util.Properties();
209
210 // Gets the classloader of the code that called this method, may
211 // be null.
212 ClassLoader callerCL = DriverManager.getCallerClassLoader();
213
214 if (user != null) {
215 info.put("user", user);
216 }
217 if (password != null) {
218 info.put("password", password);
219 }
220
221 return (getConnection(url, info, callerCL));
222 }
223
224 /**
225 * Attempts to establish a connection to the given database URL.
226 * The <code>DriverManager</code> attempts to select an appropriate driver from
227 * the set of registered JDBC drivers.
228 *
229 * @param url a database url of the form
230 * <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
231 * @return a connection to the URL
232 * @exception SQLException if a database access error occurs
233 */
234 public static Connection getConnection(String url)
235 throws SQLException {
236
237 java.util.Properties info = new java.util.Properties();
238
239 // Gets the classloader of the code that called this method, may
240 // be null.
241 ClassLoader callerCL = DriverManager.getCallerClassLoader();
242
243 return (getConnection(url, info, callerCL));
244 }
245
246 /**
247 * Attempts to locate a driver that understands the given URL.
248 * The <code>DriverManager</code> attempts to select an appropriate driver from
249 * the set of registered JDBC drivers.
250 *
251 * @param url a database URL of the form
252 * <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
253 * @return a <code>Driver</code> object representing a driver
254 * that can connect to the given URL
255 * @exception SQLException if a database access error occurs
256 */
257 public static Driver getDriver(String url)
258 throws SQLException {
259
260 println("DriverManager.getDriver(\"" + url + "\")");
261
262 // Gets the classloader of the code that called this method, may
263 // be null.
264 ClassLoader callerCL = DriverManager.getCallerClassLoader();
265
266 // Walk through the loaded registeredDrivers attempting to locate someone
267 // who understands the given URL.
268 for (DriverInfo aDriver : registeredDrivers) {
269 // If the caller does not have permission to load the driver then
270 // skip it.
271 if(isDriverAllowed(aDriver.driver, callerCL)) {
272 try {
273 if(aDriver.driver.acceptsURL(url)) {
274 // Success!
275 println("getDriver returning " + aDriver.driver.getClass().getName());
276 return (aDriver.driver);
277 }
278
279 } catch(SQLException sqe) {
280 // Drop through and try the next driver.
281 }
282 } else {
283 println(" skipping: " + aDriver.driver.getClass().getName());
284 }
285
286 }
287
288 println("getDriver: no suitable driver");
289 throw new SQLException("No suitable driver", "08001");
290 }
291
292
293 /**
294 * Registers the given driver with the <code>DriverManager</code>.
295 * A newly-loaded driver class should call
296 * the method <code>registerDriver</code> to make itself
297 * known to the <code>DriverManager</code>.
298 *
299 * @param driver the new JDBC Driver that is to be registered with the
300 * <code>DriverManager</code>
301 * @exception SQLException if a database access error occurs
302 */
303 public static synchronized void registerDriver(java.sql.Driver driver)
304 throws SQLException {
305
306 /* Register the driver if it has not already been added to our list */
307 if(driver != null) {
308 registeredDrivers.addIfAbsent(new DriverInfo(driver));
309 } else {
310 // This is for compatibility with the original DriverManager
311 throw new NullPointerException();
312 }
313
314 println("registerDriver: " + driver);
315
316 }
317
318 /**
319 * Drops a driver from the <code>DriverManager</code>'s list.
320 * Applets can only deregister drivers from their own classloaders.
321 *
322 * @param driver the JDBC Driver to drop
323 * @exception SQLException if a database access error occurs
324 */
325 public static synchronized void deregisterDriver(Driver driver)
326 throws SQLException {
327 if (driver == null) {
328 return;
329 }
330
331 // Gets the classloader of the code that called this method,
332 // may be null.
333 ClassLoader callerCL = DriverManager.getCallerClassLoader();
334 println("DriverManager.deregisterDriver: " + driver);
335
336 DriverInfo aDriver = new DriverInfo(driver);
337 if(registeredDrivers.contains(aDriver)) {
338 if (isDriverAllowed(driver, callerCL)) {
339 registeredDrivers.remove(aDriver);
340 } else {
341 // If the caller does not have permission to load the driver then
342 // throw a SecurityException.
343 throw new SecurityException();
344 }
345 } else {
346 println(" couldn't find driver to unload");
347 }
348 }
349
350 /**
351 * Retrieves an Enumeration with all of the currently loaded JDBC drivers
352 * to which the current caller has access.
353 *
354 * <P><B>Note:</B> The classname of a driver can be found using
355 * <CODE>d.getClass().getName()</CODE>
356 *
357 * @return the list of JDBC Drivers loaded by the caller's class loader
358 */
359 public static java.util.Enumeration<Driver> getDrivers() {
360 java.util.Vector<Driver> result = new java.util.Vector<Driver>();
361
362 // Gets the classloader of the code that called this method, may
363 // be null.
364 ClassLoader callerCL = DriverManager.getCallerClassLoader();
365
366 // Walk through the loaded registeredDrivers.
367 for(DriverInfo aDriver : registeredDrivers) {
368 // If the caller does not have permission to load the driver then
369 // skip it.
370 if(isDriverAllowed(aDriver.driver, callerCL)) {
371 result.addElement(aDriver.driver);
372 } else {
373 println(" skipping: " + aDriver.getClass().getName());
374 }
375 }
376 return (result.elements());
377 }
378
379
380 /**
381 * Sets the maximum time in seconds that a driver will wait
382 * while attempting to connect to a database.
383 *
384 * @param seconds the login time limit in seconds; zero means there is no limit
385 * @see #getLoginTimeout
386 */
387 public static void setLoginTimeout(int seconds) {
388 loginTimeout = seconds;
389 }
390
391 /**
392 * Gets the maximum time in seconds that a driver can wait
393 * when attempting to log in to a database.
394 *
395 * @return the driver login time limit in seconds
396 * @see #setLoginTimeout
397 */
398 public static int getLoginTimeout() {
399 return (loginTimeout);
400 }
401
402 /**
403 * Sets the logging/tracing PrintStream that is used
404 * by the <code>DriverManager</code>
405 * and all drivers.
406 *<P>
407 * In the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
408 * to see that there is an <code>SQLPermission</code> object before setting
409 * the logging stream. If a <code>SecurityManager</code> exists and its
410 * <code>checkPermission</code> method denies setting the log writer, this
411 * method throws a <code>java.lang.SecurityException</code>.
412 *
413 * @param out the new logging/tracing PrintStream; to disable, set to <code>null</code>
414 * @deprecated
415 * @throws SecurityException if a security manager exists and its
416 * <code>checkPermission</code> method denies setting the log stream
417 *
418 * @see SecurityManager#checkPermission
419 * @see #getLogStream
420 */
421 public static void setLogStream(java.io.PrintStream out) {
422
423 SecurityManager sec = System.getSecurityManager();
424 if (sec != null) {
425 sec.checkPermission(SET_LOG_PERMISSION);
426 }
427
428 logStream = out;
429 if ( out != null )
430 logWriter = new java.io.PrintWriter(out);
431 else
432 logWriter = null;
433 }
434
435 /**
436 * Retrieves the logging/tracing PrintStream that is used by the <code>DriverManager</code>
437 * and all drivers.
438 *
439 * @return the logging/tracing PrintStream; if disabled, is <code>null</code>
440 * @deprecated
441 * @see #setLogStream
442 */
443 public static java.io.PrintStream getLogStream() {
444 return logStream;
445 }
446
447 /**
448 * Prints a message to the current JDBC log stream.
449 *
450 * @param message a log or tracing message
451 */
452 public static void println(String message) {
453 synchronized (logSync) {
454 if (logWriter != null) {
455 logWriter.println(message);
456
457 // automatic flushing is never enabled, so we must do it ourselves
458 logWriter.flush();
459 }
460 }
461 }
462
463 //------------------------------------------------------------------------
464
465 // Indicates whether the class object that would be created if the code calling
466 // DriverManager is accessible.
467 private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
468 boolean result = false;
469 if(driver != null) {
470 Class<?> aClass = null;
471 try {
472 aClass = Class.forName(driver.getClass().getName(), true, classLoader);
473 } catch (Exception ex) {
474 result = false;
475 }
476
477 result = ( aClass == driver.getClass() ) ? true : false;
478 }
479
480 return result;
481 }
482
483 private static void loadInitialDrivers() {
484 String drivers;
485 try {
486 drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
487 public String run() {
488 return System.getProperty("jdbc.drivers");
489 }
490 });
491 } catch (Exception ex) {
492 drivers = null;
493 }
494 // If the driver is packaged as a Service Provider, load it.
495 // Get all the drivers through the classloader
496 // exposed as a java.sql.Driver.class service.
497 // ServiceLoader.load() replaces the sun.misc.Providers()
498
499 AccessController.doPrivileged(new PrivilegedAction<Void>() {
500 public Void run() {
501
502 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
503 Iterator driversIterator = loadedDrivers.iterator();
504
505 /* Load these drivers, so that they can be instantiated.
506 * It may be the case that the driver class may not be there
507 * i.e. there may be a packaged driver with the service class
508 * as implementation of java.sql.Driver but the actual class
509 * may be missing. In that case a java.util.ServiceConfigurationError
510 * will be thrown at runtime by the VM trying to locate
511 * and load the service.
512 *
513 * Adding a try catch block to catch those runtime errors
514 * if driver not available in classpath but it's
515 * packaged as service and that service is there in classpath.
516 */
517 try{
518 while(driversIterator.hasNext()) {
519 println(" Loading done by the java.util.ServiceLoader : "+driversIterator.next());
520 }
521 } catch(Throwable t) {
522 // Do nothing
523 }
524 return null;
525 }
526 });
527
528 println("DriverManager.initialize: jdbc.drivers = " + drivers);
529
530 if (drivers == null || drivers.equals("")) {
531 return;
532 }
533 String[] driversList = drivers.split(":");
534 println("number of Drivers:" + driversList.length);
535 for (String aDriver : driversList) {
536 try {
537 println("DriverManager.Initialize: loading " + aDriver);
538 Class.forName(aDriver, true,
539 ClassLoader.getSystemClassLoader());
540 } catch (Exception ex) {
541 println("DriverManager.Initialize: load failed: " + ex);
542 }
543 }
544 }
545
546
547 // Worker method called by the public getConnection() methods.
548 private static Connection getConnection(
549 String url, java.util.Properties info, ClassLoader callerCL) throws SQLException {
550 /*
551 * When callerCl is null, we should check the application's
552 * (which is invoking this class indirectly)
553 * classloader, so that the JDBC driver class outside rt.jar
554 * can be loaded from here.
555 */
556 synchronized(DriverManager.class) {
557 // synchronize loading of the correct classloader.
558 if(callerCL == null) {
559 callerCL = Thread.currentThread().getContextClassLoader();
560 }
561 }
562
563 if(url == null) {
564 throw new SQLException("The url cannot be null", "08001");
565 }
566
567 println("DriverManager.getConnection(\"" + url + "\")");
568
569 // Walk through the loaded registeredDrivers attempting to make a connection.
570 // Remember the first exception that gets raised so we can reraise it.
571 SQLException reason = null;
572
573 for(DriverInfo aDriver : registeredDrivers) {
574 // If the caller does not have permission to load the driver then
575 // skip it.
576 if(isDriverAllowed(aDriver.driver, callerCL)) {
577 try {
578 println(" trying " + aDriver.driver.getClass().getName());
579 Connection con = aDriver.driver.connect(url, info);
580 if (con != null) {
581 // Success!
582 println("getConnection returning " + aDriver.driver.getClass().getName());
583 return (con);
584 }
585 } catch (SQLException ex) {
586 if (reason == null) {
587 reason = ex;
588 }
589 }
590
591 } else {
592 println(" skipping: " + aDriver.getClass().getName());
593 }
594
595 }
596
597 // if we got here nobody could connect.
598 if (reason != null) {
599 println("getConnection failed: " + reason);
600 throw reason;
601 }
602
603 println("getConnection: no suitable driver found for "+ url);
604 throw new SQLException("No suitable driver found for "+ url, "08001");
605 }
606
607 /* Returns the caller's class loader, or null if none */
608 private static native ClassLoader getCallerClassLoader();
609
610 }
611
612 /*
613 * Wrapper class for registered Drivers in order to not expose Driver.equals()
614 * to avoid the capture of the Driver it being compared to as it might not
615 * normally have access.
616 */
617 class DriverInfo {
618
619 final Driver driver;
620 DriverInfo(Driver driver) {
621 this.driver = driver;
622 }
623
624 public boolean equals(Object other) {
625 return (other instanceof DriverInfo)
626 && this.driver == ((DriverInfo) other).driver;
627 }
628
629 public int hashCode() {
630 return driver.hashCode();
631 }
632
633 public String toString() {
634 return ("driver[className=" + driver + "]");
635 }
636 }