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