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