1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.sql; 19 20 import dalvik.system.VMStack; 21 import java.io.PrintStream; 22 import java.io.PrintWriter; 23 import java.security.AccessController; 24 import java.util.ArrayList; 25 import java.util.Enumeration; 26 import java.util.Iterator; 27 import java.util.List; 28 import java.util.Properties; 29 import java.util.Vector; 30 import org.apache.harmony.luni.util.PriviAction; 31 32 /** 33 * Provides facilities for managing JDBC drivers. 34 * <p> 35 * The {@code DriverManager} class loads JDBC drivers during its initialization, 36 * from the list of drivers referenced by the system property {@code 37 * "jdbc.drivers"}. 38 */ 39 public class DriverManager { 40 41 /* 42 * Facilities for logging. The Print Stream is deprecated but is maintained 43 * here for compatibility. 44 */ 45 private static PrintStream thePrintStream; 46 47 private static PrintWriter thePrintWriter; 48 49 // Login timeout value - by default set to 0 -> "wait forever" 50 private static int loginTimeout = 0; 51 52 /* 53 * Set to hold Registered Drivers - initial capacity 10 drivers (will expand 54 * automatically if necessary. 55 */ 56 private static final List<Driver> theDrivers = new ArrayList<Driver>(10); 57 58 // Permission for setting log 59 private static final SQLPermission logPermission = new SQLPermission("setLog"); 60 61 /* 62 * Load drivers on initialization 63 */ 64 static { loadInitialDrivers()65 loadInitialDrivers(); 66 } 67 68 /* 69 * Loads the set of JDBC drivers defined by the Property "jdbc.drivers" if 70 * it is defined. 71 */ loadInitialDrivers()72 private static void loadInitialDrivers() { 73 String theDriverList = AccessController 74 .doPrivileged(new PriviAction<String>("jdbc.drivers", null)); 75 76 if (theDriverList == null) { 77 return; 78 } 79 80 /* 81 * Get the names of the drivers as an array of Strings from the system 82 * property by splitting the property at the separator character ':' 83 */ 84 String[] theDriverNames = theDriverList.split(":"); 85 86 for (String element : theDriverNames) { 87 try { 88 // Load the driver class 89 Class 90 .forName(element, true, ClassLoader 91 .getSystemClassLoader()); 92 } catch (Throwable t) { 93 // Ignored 94 } 95 } 96 } 97 98 /* 99 * A private constructor to prevent allocation 100 */ DriverManager()101 private DriverManager() { 102 super(); 103 } 104 105 /** 106 * Removes a driver from the {@code DriverManager}'s registered driver list. 107 * This will only succeed when the caller's class loader loaded the driver 108 * that is to be removed. If the driver was loaded by a different class 109 * loader, the removal of the driver fails silently. 110 * <p> 111 * If the removal succeeds, the {@code DriverManager} will not use this 112 * driver in the future when asked to get a {@code Connection}. 113 * 114 * @param driver 115 * the JDBC driver to remove. 116 * @throws SQLException 117 * if there is a problem interfering with accessing the 118 * database. 119 */ deregisterDriver(Driver driver)120 public static void deregisterDriver(Driver driver) throws SQLException { 121 if (driver == null) { 122 return; 123 } 124 ClassLoader callerClassLoader = VMStack.getCallingClassLoader(); 125 if (!DriverManager.isClassFromClassLoader(driver, callerClassLoader)) { 126 throw new SecurityException("calling class not authorized to deregister JDBC driver"); 127 } 128 synchronized (theDrivers) { 129 theDrivers.remove(driver); 130 } 131 } 132 133 /** 134 * Attempts to establish a connection to the given database URL. 135 * 136 * @param url 137 * a URL string representing the database target to connect with. 138 * @return a {@code Connection} to the database identified by the URL. 139 * {@code null} if no connection can be established. 140 * @throws SQLException 141 * if there is an error while attempting to connect to the 142 * database identified by the URL. 143 */ getConnection(String url)144 public static Connection getConnection(String url) throws SQLException { 145 return getConnection(url, new Properties()); 146 } 147 148 /** 149 * Attempts to establish a connection to the given database URL. 150 * 151 * @param url 152 * a URL string representing the database target to connect with 153 * @param info 154 * a set of properties to use as arguments to set up the 155 * connection. Properties are arbitrary string/value pairs. 156 * Normally, at least the properties {@code "user"} and {@code 157 * "password"} should be passed, with appropriate settings for 158 * the user ID and its corresponding password to get access to 159 * the corresponding database. 160 * @return a {@code Connection} to the database identified by the URL. 161 * {@code null} if no connection can be established. 162 * @throws SQLException 163 * if there is an error while attempting to connect to the 164 * database identified by the URL. 165 */ getConnection(String url, Properties info)166 public static Connection getConnection(String url, Properties info) throws SQLException { 167 // 08 - connection exception 168 // 001 - SQL-client unable to establish SQL-connection 169 String sqlState = "08001"; 170 if (url == null) { 171 throw new SQLException("The url cannot be null", sqlState); 172 } 173 synchronized (theDrivers) { 174 /* 175 * Loop over the drivers in the DriverSet checking to see if one can 176 * open a connection to the supplied URL - return the first 177 * connection which is returned 178 */ 179 for (Driver theDriver : theDrivers) { 180 Connection theConnection = theDriver.connect(url, info); 181 if (theConnection != null) { 182 return theConnection; 183 } 184 } 185 } 186 // If we get here, none of the drivers are able to resolve the URL 187 throw new SQLException("No suitable driver", sqlState); 188 } 189 190 /** 191 * Attempts to establish a connection to the given database URL. 192 * 193 * @param url 194 * a URL string representing the database target to connect with. 195 * @param user 196 * a user ID used to login to the database. 197 * @param password 198 * a password for the user ID to login to the database. 199 * @return a {@code Connection} to the database identified by the URL. 200 * {@code null} if no connection can be established. 201 * @throws SQLException 202 * if there is an error while attempting to connect to the 203 * database identified by the URL. 204 */ getConnection(String url, String user, String password)205 public static Connection getConnection(String url, String user, 206 String password) throws SQLException { 207 Properties theProperties = new Properties(); 208 if (null != user) { 209 theProperties.setProperty("user", user); 210 } 211 if (null != password) { 212 theProperties.setProperty("password", password); 213 } 214 return getConnection(url, theProperties); 215 } 216 217 /** 218 * Tries to find a driver that can interpret the supplied URL. 219 * 220 * @param url 221 * the URL of a database. 222 * @return a {@code Driver} that matches the provided URL. {@code null} if 223 * no {@code Driver} understands the URL 224 * @throws SQLException 225 * if there is any kind of problem accessing the database. 226 */ getDriver(String url)227 public static Driver getDriver(String url) throws SQLException { 228 // BEGIN android-changed 229 ClassLoader callerClassLoader = VMStack.getCallingClassLoader(); 230 // END android-changed 231 232 synchronized (theDrivers) { 233 /* 234 * Loop over the drivers in the DriverSet checking to see if one 235 * does understand the supplied URL - return the first driver which 236 * does understand the URL 237 */ 238 Iterator<Driver> theIterator = theDrivers.iterator(); 239 while (theIterator.hasNext()) { 240 Driver theDriver = theIterator.next(); 241 if (theDriver.acceptsURL(url) 242 && DriverManager.isClassFromClassLoader(theDriver, 243 callerClassLoader)) { 244 return theDriver; 245 } 246 } 247 } 248 // If no drivers understand the URL, throw an SQLException 249 // SQLState: 08 - connection exception 250 // 001 - SQL-client unable to establish SQL-connection 251 throw new SQLException("No suitable driver", "08001"); 252 } 253 254 /** 255 * Returns an {@code Enumeration} that contains all of the loaded JDBC 256 * drivers that the current caller can access. 257 * 258 * @return An {@code Enumeration} containing all the currently loaded JDBC 259 * {@code Drivers}. 260 */ getDrivers()261 public static Enumeration<Driver> getDrivers() { 262 // BEGIN android-changed 263 ClassLoader callerClassLoader = VMStack.getCallingClassLoader(); 264 // END android-changed 265 /* 266 * Synchronize to avoid clashes with additions and removals of drivers 267 * in the DriverSet 268 */ 269 synchronized (theDrivers) { 270 /* 271 * Create the Enumeration by building a Vector from the elements of 272 * the DriverSet 273 */ 274 Vector<Driver> theVector = new Vector<Driver>(); 275 Iterator<Driver> theIterator = theDrivers.iterator(); 276 while (theIterator.hasNext()) { 277 Driver theDriver = theIterator.next(); 278 if (DriverManager.isClassFromClassLoader(theDriver, 279 callerClassLoader)) { 280 theVector.add(theDriver); 281 } 282 } 283 return theVector.elements(); 284 } 285 } 286 287 /** 288 * Returns the login timeout when connecting to a database in seconds. 289 * 290 * @return the login timeout in seconds. 291 */ getLoginTimeout()292 public static int getLoginTimeout() { 293 return loginTimeout; 294 } 295 296 /** 297 * Gets the log {@code PrintStream} used by the {@code DriverManager} and 298 * all the JDBC Drivers. 299 * 300 * @deprecated use {@link #getLogWriter()} instead. 301 * @return the {@code PrintStream} used for logging activities. 302 */ 303 @Deprecated getLogStream()304 public static PrintStream getLogStream() { 305 return thePrintStream; 306 } 307 308 /** 309 * Retrieves the log writer. 310 * 311 * @return A {@code PrintWriter} object used as the log writer. {@code null} 312 * if no log writer is set. 313 */ getLogWriter()314 public static PrintWriter getLogWriter() { 315 return thePrintWriter; 316 } 317 318 /** 319 * Prints a message to the current JDBC log stream. This is either the 320 * {@code PrintWriter} or (deprecated) the {@code PrintStream}, if set. 321 * 322 * @param message 323 * the message to print to the JDBC log stream. 324 */ println(String message)325 public static void println(String message) { 326 if (thePrintWriter != null) { 327 thePrintWriter.println(message); 328 thePrintWriter.flush(); 329 } else if (thePrintStream != null) { 330 thePrintStream.println(message); 331 thePrintStream.flush(); 332 } 333 /* 334 * If neither the PrintWriter not the PrintStream are set, then silently 335 * do nothing the message is not recorded and no exception is generated. 336 */ 337 return; 338 } 339 340 /** 341 * Registers a given JDBC driver with the {@code DriverManager}. 342 * <p> 343 * A newly loaded JDBC driver class should register itself with the 344 * {@code DriverManager} by calling this method. 345 * 346 * @param driver 347 * the {@code Driver} to register with the {@code DriverManager}. 348 * @throws SQLException 349 * if a database access error occurs. 350 */ registerDriver(Driver driver)351 public static void registerDriver(Driver driver) throws SQLException { 352 if (driver == null) { 353 throw new NullPointerException(); 354 } 355 synchronized (theDrivers) { 356 theDrivers.add(driver); 357 } 358 } 359 360 /** 361 * Sets the login timeout when connecting to a database in seconds. 362 * 363 * @param seconds 364 * seconds until timeout. 0 indicates wait forever. 365 */ setLoginTimeout(int seconds)366 public static void setLoginTimeout(int seconds) { 367 loginTimeout = seconds; 368 return; 369 } 370 371 /** 372 * Sets the print stream to use for logging data from the {@code 373 * DriverManager} and the JDBC drivers. 374 * 375 * @deprecated Use {@link #setLogWriter} instead. 376 * @param out 377 * the {@code PrintStream} to use for logging. 378 */ 379 @Deprecated setLogStream(PrintStream out)380 public static void setLogStream(PrintStream out) { 381 checkLogSecurity(); 382 thePrintStream = out; 383 } 384 385 /** 386 * Sets the {@code PrintWriter} that is used by all loaded drivers, and also 387 * the {@code DriverManager}. 388 * 389 * @param out 390 * the {@code PrintWriter} to be used. 391 */ setLogWriter(PrintWriter out)392 public static void setLogWriter(PrintWriter out) { 393 checkLogSecurity(); 394 thePrintWriter = out; 395 } 396 397 /* 398 * Method which checks to see if setting a logging stream is allowed by the 399 * Security manager 400 */ checkLogSecurity()401 private static void checkLogSecurity() { 402 SecurityManager securityManager = System.getSecurityManager(); 403 if (securityManager != null) { 404 // Throws a SecurityException if setting the log is not permitted 405 securityManager.checkPermission(logPermission); 406 } 407 } 408 409 /** 410 * Determines whether the supplied object was loaded by the given {@code ClassLoader}. 411 * 412 * @param theObject 413 * the object to check. 414 * @param theClassLoader 415 * the {@code ClassLoader}. 416 * @return {@code true} if the Object does belong to the {@code ClassLoader} 417 * , {@code false} otherwise 418 */ isClassFromClassLoader(Object theObject, ClassLoader theClassLoader)419 private static boolean isClassFromClassLoader(Object theObject, 420 ClassLoader theClassLoader) { 421 422 if ((theObject == null) || (theClassLoader == null)) { 423 return false; 424 } 425 426 Class<?> objectClass = theObject.getClass(); 427 428 try { 429 Class<?> checkClass = Class.forName(objectClass.getName(), true, 430 theClassLoader); 431 if (checkClass == objectClass) { 432 return true; 433 } 434 } catch (Throwable t) { 435 // Empty 436 } 437 return false; 438 } 439 } 440