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