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