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