1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.  Oracle designates this
9  * particular file as subject to the "Classpath" exception as provided
10  * by Oracle in the LICENSE file that accompanied this code.
11  *
12  * This code is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  * version 2 for more details (a copy is included in the LICENSE file that
16  * accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License version
19  * 2 along with this work; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23  * or visit www.oracle.com if you need additional information or have any
24  * questions.
25  */
26 
27 package java.sql;
28 
29 import java.util.Iterator;
30 import java.util.ServiceLoader;
31 import java.security.AccessController;
32 import java.security.PrivilegedAction;
33 import java.util.concurrent.CopyOnWriteArrayList;
34 
35 import sun.reflect.CallerSensitive;
36 import sun.reflect.Reflection;
37 
38 // Android-changed line 2 of the javadoc to "{@code DataSource}".
39 /**
40  * <P>The basic service for managing a set of JDBC drivers.<br>
41  * <B>NOTE:</B> The {@code DataSource} interface, new in the
42  * JDBC 2.0 API, provides another way to connect to a data source.
43  * The use of a <code>DataSource</code> object is the preferred means of
44  * connecting to a data source.
45  *
46  * <P>As part of its initialization, the <code>DriverManager</code> class will
47  * attempt to load the driver classes referenced in the "jdbc.drivers"
48  * system property. This allows a user to customize the JDBC Drivers
49  * used by their applications. For example in your
50  * ~/.hotjava/properties file you might specify:
51  * <pre>
52  * <CODE>jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.taste.ourDriver</CODE>
53  * </pre>
54  *<P> The <code>DriverManager</code> methods <code>getConnection</code> and
55  * <code>getDrivers</code> have been enhanced to support the Java Standard Edition
56  * <a href="../../../technotes/guides/jar/jar.html#Service%20Provider">Service Provider</a> mechanism. JDBC 4.0 Drivers must
57  * include the file <code>META-INF/services/java.sql.Driver</code>. This file contains the name of the JDBC drivers
58  * implementation of <code>java.sql.Driver</code>.  For example, to load the <code>my.sql.Driver</code> class,
59  * the <code>META-INF/services/java.sql.Driver</code> file would contain the entry:
60  * <pre>
61  * <code>my.sql.Driver</code>
62  * </pre>
63  *
64  * <P>Applications no longer need to explictly load JDBC drivers using <code>Class.forName()</code>. Existing programs
65  * which currently load JDBC drivers using <code>Class.forName()</code> will continue to work without
66  * modification.
67  *
68  * <P>When the method <code>getConnection</code> is called,
69  * the <code>DriverManager</code> will attempt to
70  * locate a suitable driver from amongst those loaded at
71  * initialization and those loaded explicitly using the same classloader
72  * as the current applet or application.
73  *
74  * <P>
75  * Starting with the Java 2 SDK, Standard Edition, version 1.3, a
76  * logging stream can be set only if the proper
77  * permission has been granted.  Normally this will be done with
78  * the tool PolicyTool, which can be used to grant <code>permission
79  * java.sql.SQLPermission "setLog"</code>.
80  * @see Driver
81  * @see Connection
82  */
83 public class DriverManager {
84 
85 
86     // List of registered JDBC drivers
87     private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();
88     private static volatile int loginTimeout = 0;
89     private static volatile java.io.PrintWriter logWriter = null;
90     private static volatile java.io.PrintStream logStream = null;
91     // Used in println() to synchronize logWriter
92     private final static  Object logSync = new Object();
93 
94     /* Prevent the DriverManager class from being instantiated. */
DriverManager()95     private DriverManager(){}
96 
97 
98     /**
99      * Load the initial JDBC drivers by checking the System property
100      * jdbc.properties and then use the {@code ServiceLoader} mechanism
101      */
102     static {
loadInitialDrivers()103         loadInitialDrivers();
104         println("JDBC DriverManager initialized");
105     }
106 
107     /**
108      * The <code>SQLPermission</code> constant that allows the
109      * setting of the logging stream.
110      * @since 1.3
111      */
112     final static SQLPermission SET_LOG_PERMISSION =
113         new SQLPermission("setLog");
114 
115     //--------------------------JDBC 2.0-----------------------------
116 
117     /**
118      * Retrieves the log writer.
119      *
120      * The <code>getLogWriter</code> and <code>setLogWriter</code>
121      * methods should be used instead
122      * of the <code>get/setlogStream</code> methods, which are deprecated.
123      * @return a <code>java.io.PrintWriter</code> object
124      * @see #setLogWriter
125      * @since 1.2
126      */
getLogWriter()127     public static java.io.PrintWriter getLogWriter() {
128             return logWriter;
129     }
130 
131     /**
132      * Sets the logging/tracing <code>PrintWriter</code> object
133      * that is used by the <code>DriverManager</code> and all drivers.
134      * <P>
135      * There is a minor versioning problem created by the introduction
136      * of the method <code>setLogWriter</code>.  The
137      * method <code>setLogWriter</code> cannot create a <code>PrintStream</code> object
138      * that will be returned by <code>getLogStream</code>---the Java platform does
139      * not provide a backward conversion.  As a result, a new application
140      * that uses <code>setLogWriter</code> and also uses a JDBC 1.0 driver that uses
141      * <code>getLogStream</code> will likely not see debugging information written
142      * by that driver.
143      *<P>
144      * Starting with the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
145      * to see that there is an <code>SQLPermission</code> object before setting
146      * the logging stream.  If a <code>SecurityManager</code> exists and its
147      * <code>checkPermission</code> method denies setting the log writer, this
148      * method throws a <code>java.lang.SecurityException</code>.
149      *
150      * @param out the new logging/tracing <code>PrintStream</code> object;
151      *      <code>null</code> to disable logging and tracing
152      * @throws SecurityException
153      *    if a security manager exists and its
154      *    <code>checkPermission</code> method denies
155      *    setting the log writer
156      *
157      * @see SecurityManager#checkPermission
158      * @see #getLogWriter
159      * @since 1.2
160      */
setLogWriter(java.io.PrintWriter out)161     public static void setLogWriter(java.io.PrintWriter out) {
162 
163         SecurityManager sec = System.getSecurityManager();
164         if (sec != null) {
165             sec.checkPermission(SET_LOG_PERMISSION);
166         }
167             logStream = null;
168             logWriter = out;
169     }
170 
171 
172     //---------------------------------------------------------------
173 
174     /**
175      * Attempts to establish a connection to the given database URL.
176      * The <code>DriverManager</code> attempts to select an appropriate driver from
177      * the set of registered JDBC drivers.
178      *
179      * @param url a database url of the form
180      * <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
181      * @param info a list of arbitrary string tag/value pairs as
182      * connection arguments; normally at least a "user" and
183      * "password" property should be included
184      * @return a Connection to the URL
185      * @exception SQLException if a database access error occurs
186      */
187     @CallerSensitive
getConnection(String url, java.util.Properties info)188     public static Connection getConnection(String url,
189         java.util.Properties info) throws SQLException {
190         return (getConnection(url, info, Reflection.getCallerClass()));
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     @CallerSensitive
getConnection(String url, String user, String password)207     public static Connection getConnection(String url,
208         String user, String password) throws SQLException {
209         java.util.Properties info = new java.util.Properties();
210 
211         if (user != null) {
212             info.put("user", user);
213         }
214         if (password != null) {
215             info.put("password", password);
216         }
217 
218         return (getConnection(url, info, Reflection.getCallerClass()));
219     }
220 
221     /**
222      * Attempts to establish a connection to the given database URL.
223      * The <code>DriverManager</code> attempts to select an appropriate driver from
224      * the set of registered JDBC drivers.
225      *
226      * @param url a database url of the form
227      *  <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
228      * @return a connection to the URL
229      * @exception SQLException if a database access error occurs
230      */
231     @CallerSensitive
getConnection(String url)232     public static Connection getConnection(String url)
233         throws SQLException {
234 
235         java.util.Properties info = new java.util.Properties();
236         return (getConnection(url, info, Reflection.getCallerClass()));
237     }
238 
239     /**
240      * Attempts to locate a driver that understands the given URL.
241      * The <code>DriverManager</code> attempts to select an appropriate driver from
242      * the set of registered JDBC drivers.
243      *
244      * @param url a database URL of the form
245      *     <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
246      * @return a <code>Driver</code> object representing a driver
247      * that can connect to the given URL
248      * @exception SQLException if a database access error occurs
249      */
250     @CallerSensitive
getDriver(String url)251     public static Driver getDriver(String url)
252         throws SQLException {
253 
254         println("DriverManager.getDriver(\"" + url + "\")");
255 
256         Class<?> callerClass = Reflection.getCallerClass();
257 
258         // Walk through the loaded registeredDrivers attempting to locate someone
259         // who understands the given URL.
260         for (DriverInfo aDriver : registeredDrivers) {
261             // If the caller does not have permission to load the driver then
262             // skip it.
263             if(isDriverAllowed(aDriver.driver, callerClass)) {
264                 try {
265                     if(aDriver.driver.acceptsURL(url)) {
266                         // Success!
267                         println("getDriver returning " + aDriver.driver.getClass().getName());
268                     return (aDriver.driver);
269                     }
270 
271                 } catch(SQLException sqe) {
272                     // Drop through and try the next driver.
273                 }
274             } else {
275                 println("    skipping: " + aDriver.driver.getClass().getName());
276             }
277 
278         }
279 
280         println("getDriver: no suitable driver");
281         throw new SQLException("No suitable driver", "08001");
282     }
283 
284 
285     /**
286      * Registers the given driver with the <code>DriverManager</code>.
287      * A newly-loaded driver class should call
288      * the method <code>registerDriver</code> to make itself
289      * known to the <code>DriverManager</code>.
290      *
291      * @param driver the new JDBC Driver that is to be registered with the
292      *               <code>DriverManager</code>
293      * @exception SQLException if a database access error occurs
294      */
registerDriver(java.sql.Driver driver)295     public static synchronized void registerDriver(java.sql.Driver driver)
296         throws SQLException {
297 
298         /* Register the driver if it has not already been added to our list */
299         if(driver != null) {
300             registeredDrivers.addIfAbsent(new DriverInfo(driver));
301         } else {
302             // This is for compatibility with the original DriverManager
303             throw new NullPointerException();
304         }
305 
306         println("registerDriver: " + driver);
307 
308     }
309 
310     /**
311      * Drops a driver from the <code>DriverManager</code>'s list.
312      *  Applets can only deregister drivers from their own classloaders.
313      *
314      * @param driver the JDBC Driver to drop
315      * @exception SQLException if a database access error occurs
316      */
317     @CallerSensitive
deregisterDriver(Driver driver)318     public static synchronized void deregisterDriver(Driver driver)
319         throws SQLException {
320         if (driver == null) {
321             return;
322         }
323 
324         println("DriverManager.deregisterDriver: " + driver);
325 
326         DriverInfo aDriver = new DriverInfo(driver);
327         if(registeredDrivers.contains(aDriver)) {
328             if (isDriverAllowed(driver, Reflection.getCallerClass())) {
329                  registeredDrivers.remove(aDriver);
330             } else {
331                 // If the caller does not have permission to load the driver then
332                 // throw a SecurityException.
333                 throw new SecurityException();
334             }
335         } else {
336             println("    couldn't find driver to unload");
337         }
338     }
339 
340     /**
341      * Retrieves an Enumeration with all of the currently loaded JDBC drivers
342      * to which the current caller has access.
343      *
344      * <P><B>Note:</B> The classname of a driver can be found using
345      * <CODE>d.getClass().getName()</CODE>
346      *
347      * @return the list of JDBC Drivers loaded by the caller's class loader
348      */
349     @CallerSensitive
getDrivers()350     public static java.util.Enumeration<Driver> getDrivers() {
351         java.util.Vector<Driver> result = new java.util.Vector<Driver>();
352 
353         Class<?> callerClass = Reflection.getCallerClass();
354 
355         // Walk through the loaded registeredDrivers.
356         for(DriverInfo aDriver : registeredDrivers) {
357             // If the caller does not have permission to load the driver then
358             // skip it.
359             if(isDriverAllowed(aDriver.driver, callerClass)) {
360                 result.addElement(aDriver.driver);
361             } else {
362                 println("    skipping: " + aDriver.getClass().getName());
363             }
364         }
365         return (result.elements());
366     }
367 
368 
369     /**
370      * Sets the maximum time in seconds that a driver will wait
371      * while attempting to connect to a database.
372      *
373      * @param seconds the login time limit in seconds; zero means there is no limit
374      * @see #getLoginTimeout
375      */
setLoginTimeout(int seconds)376     public static void setLoginTimeout(int seconds) {
377         loginTimeout = seconds;
378     }
379 
380     /**
381      * Gets the maximum time in seconds that a driver can wait
382      * when attempting to log in to a database.
383      *
384      * @return the driver login time limit in seconds
385      * @see #setLoginTimeout
386      */
getLoginTimeout()387     public static int getLoginTimeout() {
388         return (loginTimeout);
389     }
390 
391     // Android-changed: Added reason to @deprecated to improve the documentation.
392     /**
393      * Sets the logging/tracing PrintStream that is used
394      * by the <code>DriverManager</code>
395      * and all drivers.
396      *<P>
397      * In the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
398      * to see that there is an <code>SQLPermission</code> object before setting
399      * the logging stream.  If a <code>SecurityManager</code> exists and its
400      * <code>checkPermission</code> method denies setting the log writer, this
401      * method throws a <code>java.lang.SecurityException</code>.
402      *
403      * @param out the new logging/tracing PrintStream; to disable, set to <code>null</code>
404      * @deprecated Use {@code setLogWriter} instead.
405      * @throws SecurityException if a security manager exists and its
406      *    <code>checkPermission</code> method denies setting the log stream
407      *
408      * @see SecurityManager#checkPermission
409      * @see #getLogStream
410      */
411     // Android-added: @Deprecated annotation from OpenJDK8u121-b13 to fix build warnings.
412     @Deprecated
setLogStream(java.io.PrintStream out)413     public static void setLogStream(java.io.PrintStream out) {
414 
415         SecurityManager sec = System.getSecurityManager();
416         if (sec != null) {
417             sec.checkPermission(SET_LOG_PERMISSION);
418         }
419 
420         logStream = out;
421         if ( out != null )
422             logWriter = new java.io.PrintWriter(out);
423         else
424             logWriter = null;
425     }
426 
427     // Android-changed: Added reason to @deprecated to improve the documentation.
428     /**
429      * Retrieves the logging/tracing PrintStream that is used by the <code>DriverManager</code>
430      * and all drivers.
431      *
432      * @return the logging/tracing PrintStream; if disabled, is <code>null</code>
433      * @deprecated Use {@code getLogWriter} instead.
434      * @see #setLogStream
435      */
436     // Android-added: @Deprecated annotation from OpenJDK8u121-b13 to fix build warnings.
437     @Deprecated
getLogStream()438     public static java.io.PrintStream getLogStream() {
439         return logStream;
440     }
441 
442     /**
443      * Prints a message to the current JDBC log stream.
444      *
445      * @param message a log or tracing message
446      */
println(String message)447     public static void println(String message) {
448         synchronized (logSync) {
449             if (logWriter != null) {
450                 logWriter.println(message);
451 
452                 // automatic flushing is never enabled, so we must do it ourselves
453                 logWriter.flush();
454             }
455         }
456     }
457 
458     //------------------------------------------------------------------------
459 
460     // Indicates whether the class object that would be created if the code calling
461     // DriverManager is accessible.
isDriverAllowed(Driver driver, Class<?> caller)462     private static boolean isDriverAllowed(Driver driver, Class<?> caller) {
463         ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
464         return isDriverAllowed(driver, callerCL);
465     }
466 
isDriverAllowed(Driver driver, ClassLoader classLoader)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 
loadInitialDrivers()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                         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.
getConnection( String url, java.util.Properties info, Class<?> caller)548     private static Connection getConnection(
549         String url, java.util.Properties info, Class<?> caller) 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         ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
557         synchronized (DriverManager.class) {
558             // synchronize loading of the correct classloader.
559             if (callerCL == null) {
560                 callerCL = Thread.currentThread().getContextClassLoader();
561             }
562         }
563 
564         if(url == null) {
565             throw new SQLException("The url cannot be null", "08001");
566         }
567 
568         println("DriverManager.getConnection(\"" + url + "\")");
569 
570         // Walk through the loaded registeredDrivers attempting to make a connection.
571         // Remember the first exception that gets raised so we can reraise it.
572         SQLException reason = null;
573 
574         for(DriverInfo aDriver : registeredDrivers) {
575             // If the caller does not have permission to load the driver then
576             // skip it.
577             if(isDriverAllowed(aDriver.driver, callerCL)) {
578                 try {
579                     println("    trying " + aDriver.driver.getClass().getName());
580                     Connection con = aDriver.driver.connect(url, info);
581                     if (con != null) {
582                         // Success!
583                         println("getConnection returning " + aDriver.driver.getClass().getName());
584                         return (con);
585                     }
586                 } catch (SQLException ex) {
587                     if (reason == null) {
588                         reason = ex;
589                     }
590                 }
591 
592             } else {
593                 println("    skipping: " + aDriver.getClass().getName());
594             }
595 
596         }
597 
598         // if we got here nobody could connect.
599         if (reason != null)    {
600             println("getConnection failed: " + reason);
601             throw reason;
602         }
603 
604         println("getConnection: no suitable driver found for "+ url);
605         throw new SQLException("No suitable driver found for "+ url, "08001");
606     }
607 }
608 
609 /*
610  * Wrapper class for registered Drivers in order to not expose Driver.equals()
611  * to avoid the capture of the Driver it being compared to as it might not
612  * normally have access.
613  */
614 class DriverInfo {
615 
616     final Driver driver;
DriverInfo(Driver driver)617     DriverInfo(Driver driver) {
618         this.driver = driver;
619     }
620 
equals(Object other)621     public boolean equals(Object other) {
622         return (other instanceof DriverInfo)
623                 && this.driver == ((DriverInfo) other).driver;
624     }
625 
hashCode()626     public int hashCode() {
627         return driver.hashCode();
628     }
629 
toString()630     public String toString() {
631         return ("driver[className="  + driver + "]");
632     }
633 }
634