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 }