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