Path: blob/master/src/java.sql/share/classes/java/sql/DriverManager.java
41153 views
/*1* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.sql;2627import java.util.ArrayList;28import java.util.Collections;29import java.util.Enumeration;30import java.util.Iterator;31import java.util.List;32import java.util.ServiceLoader;33import java.security.AccessController;34import java.security.PrivilegedAction;35import java.util.concurrent.CopyOnWriteArrayList;36import java.util.stream.Stream;3738import jdk.internal.reflect.CallerSensitive;39import jdk.internal.reflect.Reflection;404142/**43* The basic service for managing a set of JDBC drivers.44* <p>45* <strong>NOTE:</strong> The {@link javax.sql.DataSource} interface, provides46* another way to connect to a data source.47* The use of a {@code DataSource} object is the preferred means of48* connecting to a data source.49* <P>50* As part of its initialization, the {@code DriverManager} class will51* attempt to load available JDBC drivers by using:52* <ul>53* <li>The {@code jdbc.drivers} system property which contains a54* colon separated list of fully qualified class names of JDBC drivers. Each55* driver is loaded using the {@linkplain ClassLoader#getSystemClassLoader56* system class loader}:57* <ul>58* <li>{@code jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.taste.ourDriver}59* </ul>60*61* <li>Service providers of the {@code java.sql.Driver} class, that are loaded62* via the {@linkplain ServiceLoader#load service-provider loading} mechanism.63*</ul>64*65* @implNote66* {@code DriverManager} initialization is done lazily and looks up service67* providers using the thread context class loader. The drivers loaded and68* available to an application will depend on the thread context class loader of69* the thread that triggers driver initialization by {@code DriverManager}.70*71* <P>When the method {@code getConnection} is called,72* the {@code DriverManager} will attempt to73* locate a suitable driver from amongst those loaded at74* initialization and those loaded explicitly using the same class loader75* as the current application.76*77* @see Driver78* @see Connection79* @since 1.180*/81public class DriverManager {828384// List of registered JDBC drivers85private static final CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();86private static volatile int loginTimeout = 0;87private static volatile java.io.PrintWriter logWriter = null;88private static volatile java.io.PrintStream logStream = null;89// Used in println() to synchronize logWriter90private static final Object logSync = new Object();91// Used in ensureDriversInitialized() to synchronize driversInitialized92private static final Object lockForInitDrivers = new Object();93private static volatile boolean driversInitialized;94private static final String JDBC_DRIVERS_PROPERTY = "jdbc.drivers";9596/* Prevent the DriverManager class from being instantiated. */97private DriverManager(){}9899/**100* The {@code SQLPermission} constant that allows the101* setting of the logging stream.102* @since 1.3103*/104static final SQLPermission SET_LOG_PERMISSION =105new SQLPermission("setLog");106107/**108* The {@code SQLPermission} constant that allows the109* un-register a registered JDBC driver.110* @since 1.8111*/112static final SQLPermission DEREGISTER_DRIVER_PERMISSION =113new SQLPermission("deregisterDriver");114115//--------------------------JDBC 2.0-----------------------------116117/**118* Retrieves the log writer.119*120* The {@code getLogWriter} and {@code setLogWriter}121* methods should be used instead122* of the {@code get/setlogStream} methods, which are deprecated.123* @return a {@code java.io.PrintWriter} object124* @see #setLogWriter125* @since 1.2126*/127public static java.io.PrintWriter getLogWriter() {128return logWriter;129}130131/**132* Sets the logging/tracing {@code PrintWriter} object133* that is used by the {@code DriverManager} and all drivers.134*<P>135* If a security manager exists, its {@code checkPermission}136* method is first called with a {@code SQLPermission("setLog")}137* permission to check that the caller is allowed to call {@code setLogWriter}.138*139* @param out the new logging/tracing {@code PrintStream} object;140* {@code null} to disable logging and tracing141* @throws SecurityException if a security manager exists and its142* {@code checkPermission} method denies permission to set the log writer.143* @see SecurityManager#checkPermission144* @see #getLogWriter145* @since 1.2146*/147public static void setLogWriter(java.io.PrintWriter out) {148149@SuppressWarnings("removal")150SecurityManager sec = System.getSecurityManager();151if (sec != null) {152sec.checkPermission(SET_LOG_PERMISSION);153}154logStream = null;155logWriter = out;156}157158159//---------------------------------------------------------------160161/**162* Attempts to establish a connection to the given database URL.163* The {@code DriverManager} attempts to select an appropriate driver from164* the set of registered JDBC drivers.165*<p>166* <B>Note:</B> If a property is specified as part of the {@code url} and167* is also specified in the {@code Properties} object, it is168* implementation-defined as to which value will take precedence.169* For maximum portability, an application should only specify a170* property once.171*172* @param url a database url of the form173* <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>174* @param info a list of arbitrary string tag/value pairs as175* connection arguments; normally at least a "user" and176* "password" property should be included177* @return a Connection to the URL178* @throws SQLException if a database access error occurs or the url is179* {@code null}180* @throws SQLTimeoutException when the driver has determined that the181* timeout value specified by the {@code setLoginTimeout} method182* has been exceeded and has at least tried to cancel the183* current database connection attempt184*/185@CallerSensitive186public static Connection getConnection(String url,187java.util.Properties info) throws SQLException {188189return (getConnection(url, info, Reflection.getCallerClass()));190}191192/**193* Attempts to establish a connection to the given database URL.194* The {@code DriverManager} attempts to select an appropriate driver from195* the set of registered JDBC drivers.196*<p>197* <B>Note:</B> If the {@code user} or {@code password} property are198* also specified as part of the {@code url}, it is199* implementation-defined as to which value will take precedence.200* For maximum portability, an application should only specify a201* property once.202*203* @param url a database url of the form204* <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>205* @param user the database user on whose behalf the connection is being206* made207* @param password the user's password208* @return a connection to the URL209* @throws SQLException if a database access error occurs or the url is210* {@code null}211* @throws SQLTimeoutException when the driver has determined that the212* timeout value specified by the {@code setLoginTimeout} method213* has been exceeded and has at least tried to cancel the214* current database connection attempt215*/216@CallerSensitive217public static Connection getConnection(String url,218String user, String password) throws SQLException {219java.util.Properties info = new java.util.Properties();220221if (user != null) {222info.put("user", user);223}224if (password != null) {225info.put("password", password);226}227228return (getConnection(url, info, Reflection.getCallerClass()));229}230231/**232* Attempts to establish a connection to the given database URL.233* The {@code DriverManager} attempts to select an appropriate driver from234* the set of registered JDBC drivers.235*236* @param url a database url of the form237* <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>238* @return a connection to the URL239* @throws SQLException if a database access error occurs or the url is240* {@code null}241* @throws SQLTimeoutException when the driver has determined that the242* timeout value specified by the {@code setLoginTimeout} method243* has been exceeded and has at least tried to cancel the244* current database connection attempt245*/246@CallerSensitive247public static Connection getConnection(String url)248throws SQLException {249250java.util.Properties info = new java.util.Properties();251return (getConnection(url, info, Reflection.getCallerClass()));252}253254/**255* Attempts to locate a driver that understands the given URL.256* The {@code DriverManager} attempts to select an appropriate driver from257* the set of registered JDBC drivers.258*259* @param url a database URL of the form260* <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>261* @return a {@code Driver} object representing a driver262* that can connect to the given URL263* @throws SQLException if a database access error occurs264*/265@CallerSensitive266public static Driver getDriver(String url)267throws SQLException {268269println("DriverManager.getDriver(\"" + url + "\")");270271ensureDriversInitialized();272273Class<?> callerClass = Reflection.getCallerClass();274275// Walk through the loaded registeredDrivers attempting to locate someone276// who understands the given URL.277for (DriverInfo aDriver : registeredDrivers) {278// If the caller does not have permission to load the driver then279// skip it.280if (isDriverAllowed(aDriver.driver, callerClass)) {281try {282if (aDriver.driver.acceptsURL(url)) {283// Success!284println("getDriver returning " + aDriver.driver.getClass().getName());285return (aDriver.driver);286}287288} catch(SQLException sqe) {289// Drop through and try the next driver.290}291} else {292println(" skipping: " + aDriver.driver.getClass().getName());293}294295}296297println("getDriver: no suitable driver");298throw new SQLException("No suitable driver", "08001");299}300301302/**303* Registers the given driver with the {@code DriverManager}.304* A newly-loaded driver class should call305* the method {@code registerDriver} to make itself306* known to the {@code DriverManager}. If the driver is currently307* registered, no action is taken.308*309* @param driver the new JDBC Driver that is to be registered with the310* {@code DriverManager}311* @throws SQLException if a database access error occurs312* @throws NullPointerException if {@code driver} is null313*/314public static void registerDriver(java.sql.Driver driver)315throws SQLException {316317registerDriver(driver, null);318}319320/**321* Registers the given driver with the {@code DriverManager}.322* A newly-loaded driver class should call323* the method {@code registerDriver} to make itself324* known to the {@code DriverManager}. If the driver is currently325* registered, no action is taken.326*327* @param driver the new JDBC Driver that is to be registered with the328* {@code DriverManager}329* @param da the {@code DriverAction} implementation to be used when330* {@code DriverManager#deregisterDriver} is called331* @throws SQLException if a database access error occurs332* @throws NullPointerException if {@code driver} is null333* @since 1.8334*/335public static void registerDriver(java.sql.Driver driver,336DriverAction da)337throws SQLException {338339/* Register the driver if it has not already been added to our list */340if (driver != null) {341registeredDrivers.addIfAbsent(new DriverInfo(driver, da));342} else {343// This is for compatibility with the original DriverManager344throw new NullPointerException();345}346347println("registerDriver: " + driver);348349}350351/**352* Removes the specified driver from the {@code DriverManager}'s list of353* registered drivers.354* <p>355* If a {@code null} value is specified for the driver to be removed, then no356* action is taken.357* <p>358* If a security manager exists, its {@code checkPermission}359* method is first called with a {@code SQLPermission("deregisterDriver")}360* permission to check that the caller is allowed to deregister a JDBC Driver.361* <p>362* If the specified driver is not found in the list of registered drivers,363* then no action is taken. If the driver was found, it will be removed364* from the list of registered drivers.365* <p>366* If a {@code DriverAction} instance was specified when the JDBC driver was367* registered, its deregister method will be called368* prior to the driver being removed from the list of registered drivers.369*370* @param driver the JDBC Driver to remove371* @throws SQLException if a database access error occurs372* @throws SecurityException if a security manager exists and its373* {@code checkPermission} method denies permission to deregister a driver.374*375* @see SecurityManager#checkPermission376*/377@CallerSensitive378public static void deregisterDriver(Driver driver) throws SQLException {379if (driver == null) {380return;381}382383@SuppressWarnings("removal")384SecurityManager sec = System.getSecurityManager();385if (sec != null) {386sec.checkPermission(DEREGISTER_DRIVER_PERMISSION);387}388389println("DriverManager.deregisterDriver: " + driver);390391DriverInfo aDriver = new DriverInfo(driver, null);392synchronized (lockForInitDrivers) {393if (registeredDrivers.contains(aDriver)) {394if (isDriverAllowed(driver, Reflection.getCallerClass())) {395DriverInfo di = registeredDrivers.get(registeredDrivers.indexOf(aDriver));396// If a DriverAction was specified, Call it to notify the397// driver that it has been deregistered398if (di.action() != null) {399di.action().deregister();400}401registeredDrivers.remove(aDriver);402} else {403// If the caller does not have permission to load the driver then404// throw a SecurityException.405throw new SecurityException();406}407} else {408println(" couldn't find driver to unload");409}410}411}412413/**414* Retrieves an Enumeration with all of the currently loaded JDBC drivers415* to which the current caller has access.416*417* <P><B>Note:</B> The classname of a driver can be found using418* {@code d.getClass().getName()}419*420* @return the list of JDBC Drivers loaded by the caller's class loader421* @see #drivers()422*/423@CallerSensitive424public static Enumeration<Driver> getDrivers() {425ensureDriversInitialized();426427return Collections.enumeration(getDrivers(Reflection.getCallerClass()));428}429430/**431* Retrieves a Stream with all of the currently loaded JDBC drivers432* to which the current caller has access.433*434* @return the stream of JDBC Drivers loaded by the caller's class loader435* @since 9436*/437@CallerSensitive438public static Stream<Driver> drivers() {439ensureDriversInitialized();440441return getDrivers(Reflection.getCallerClass()).stream();442}443444private static List<Driver> getDrivers(Class<?> callerClass) {445List<Driver> result = new ArrayList<>();446// Walk through the loaded registeredDrivers.447for (DriverInfo aDriver : registeredDrivers) {448// If the caller does not have permission to load the driver then449// skip it.450if (isDriverAllowed(aDriver.driver, callerClass)) {451result.add(aDriver.driver);452} else {453println(" skipping: " + aDriver.driver.getClass().getName());454}455}456return result;457}458459/**460* Sets the maximum time in seconds that a driver will wait461* while attempting to connect to a database once the driver has462* been identified.463*464* @param seconds the login time limit in seconds; zero means there is no limit465* @see #getLoginTimeout466*/467public static void setLoginTimeout(int seconds) {468loginTimeout = seconds;469}470471/**472* Gets the maximum time in seconds that a driver can wait473* when attempting to log in to a database.474*475* @return the driver login time limit in seconds476* @see #setLoginTimeout477*/478public static int getLoginTimeout() {479return (loginTimeout);480}481482/**483* Sets the logging/tracing PrintStream that is used484* by the {@code DriverManager}485* and all drivers.486*<P>487* If a security manager exists, its {@code checkPermission}488* method is first called with a {@code SQLPermission("setLog")}489* permission to check that the caller is allowed to call {@code setLogStream}.490*491* @param out the new logging/tracing PrintStream; to disable, set to {@code null}492* @deprecated Use {@code setLogWriter}493* @throws SecurityException if a security manager exists and its494* {@code checkPermission} method denies permission to set the log stream.495* @see SecurityManager#checkPermission496* @see #getLogStream497*/498@Deprecated(since="1.2")499public static void setLogStream(java.io.PrintStream out) {500501@SuppressWarnings("removal")502SecurityManager sec = System.getSecurityManager();503if (sec != null) {504sec.checkPermission(SET_LOG_PERMISSION);505}506507logStream = out;508if ( out != null )509logWriter = new java.io.PrintWriter(out);510else511logWriter = null;512}513514/**515* Retrieves the logging/tracing PrintStream that is used by the {@code DriverManager}516* and all drivers.517*518* @return the logging/tracing PrintStream; if disabled, is {@code null}519* @deprecated Use {@code getLogWriter}520* @see #setLogStream521*/522@Deprecated(since="1.2")523public static java.io.PrintStream getLogStream() {524return logStream;525}526527/**528* Prints a message to the current JDBC log stream.529*530* @param message a log or tracing message531*/532public static void println(String message) {533synchronized (logSync) {534if (logWriter != null) {535logWriter.println(message);536537// automatic flushing is never enabled, so we must do it ourselves538logWriter.flush();539}540}541}542543//------------------------------------------------------------------------544545// Indicates whether the class object that would be created if the code calling546// DriverManager is accessible.547private static boolean isDriverAllowed(Driver driver, Class<?> caller) {548ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;549return isDriverAllowed(driver, callerCL);550}551552private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {553boolean result = false;554if (driver != null) {555Class<?> aClass = null;556try {557aClass = Class.forName(driver.getClass().getName(), true, classLoader);558} catch (Exception ex) {559result = false;560}561562result = ( aClass == driver.getClass() ) ? true : false;563}564565return result;566}567568/*569* Load the initial JDBC drivers by checking the System property570* jdbc.drivers and then use the {@code ServiceLoader} mechanism571*/572@SuppressWarnings("removal")573private static void ensureDriversInitialized() {574if (driversInitialized) {575return;576}577578synchronized (lockForInitDrivers) {579if (driversInitialized) {580return;581}582String drivers;583try {584drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {585public String run() {586return System.getProperty(JDBC_DRIVERS_PROPERTY);587}588});589} catch (Exception ex) {590drivers = null;591}592// If the driver is packaged as a Service Provider, load it.593// Get all the drivers through the classloader594// exposed as a java.sql.Driver.class service.595// ServiceLoader.load() replaces the sun.misc.Providers()596597AccessController.doPrivileged(new PrivilegedAction<Void>() {598public Void run() {599600ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);601Iterator<Driver> driversIterator = loadedDrivers.iterator();602603/* Load these drivers, so that they can be instantiated.604* It may be the case that the driver class may not be there605* i.e. there may be a packaged driver with the service class606* as implementation of java.sql.Driver but the actual class607* may be missing. In that case a java.util.ServiceConfigurationError608* will be thrown at runtime by the VM trying to locate609* and load the service.610*611* Adding a try catch block to catch those runtime errors612* if driver not available in classpath but it's613* packaged as service and that service is there in classpath.614*/615try {616while (driversIterator.hasNext()) {617driversIterator.next();618}619} catch (Throwable t) {620// Do nothing621}622return null;623}624});625626println("DriverManager.initialize: jdbc.drivers = " + drivers);627628if (drivers != null && !drivers.isEmpty()) {629String[] driversList = drivers.split(":");630println("number of Drivers:" + driversList.length);631for (String aDriver : driversList) {632try {633println("DriverManager.Initialize: loading " + aDriver);634Class.forName(aDriver, true,635ClassLoader.getSystemClassLoader());636} catch (Exception ex) {637println("DriverManager.Initialize: load failed: " + ex);638}639}640}641642driversInitialized = true;643println("JDBC DriverManager initialized");644}645}646647648// Worker method called by the public getConnection() methods.649private static Connection getConnection(650String url, java.util.Properties info, Class<?> caller) throws SQLException {651/*652* When callerCl is null, we should check the application's653* (which is invoking this class indirectly)654* classloader, so that the JDBC driver class outside rt.jar655* can be loaded from here.656*/657ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;658if (callerCL == null || callerCL == ClassLoader.getPlatformClassLoader()) {659callerCL = Thread.currentThread().getContextClassLoader();660}661662if (url == null) {663throw new SQLException("The url cannot be null", "08001");664}665666println("DriverManager.getConnection(\"" + url + "\")");667668ensureDriversInitialized();669670// Walk through the loaded registeredDrivers attempting to make a connection.671// Remember the first exception that gets raised so we can reraise it.672SQLException reason = null;673674for (DriverInfo aDriver : registeredDrivers) {675// If the caller does not have permission to load the driver then676// skip it.677if (isDriverAllowed(aDriver.driver, callerCL)) {678try {679println(" trying " + aDriver.driver.getClass().getName());680Connection con = aDriver.driver.connect(url, info);681if (con != null) {682// Success!683println("getConnection returning " + aDriver.driver.getClass().getName());684return (con);685}686} catch (SQLException ex) {687if (reason == null) {688reason = ex;689}690}691692} else {693println(" skipping: " + aDriver.driver.getClass().getName());694}695696}697698// if we got here nobody could connect.699if (reason != null) {700println("getConnection failed: " + reason);701throw reason;702}703704println("getConnection: no suitable driver found for "+ url);705throw new SQLException("No suitable driver found for "+ url, "08001");706}707708709}710711/*712* Wrapper class for registered Drivers in order to not expose Driver.equals()713* to avoid the capture of the Driver it being compared to as it might not714* normally have access.715*/716class DriverInfo {717718final Driver driver;719DriverAction da;720DriverInfo(Driver driver, DriverAction action) {721this.driver = driver;722da = action;723}724725@Override726public boolean equals(Object other) {727return (other instanceof DriverInfo)728&& this.driver == ((DriverInfo) other).driver;729}730731@Override732public int hashCode() {733return driver.hashCode();734}735736@Override737public String toString() {738return ("driver[className=" + driver + "]");739}740741DriverAction action() {742return da;743}744}745746747