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