summaryrefslogtreecommitdiffstats
path: root/luni/src/main/java/java/sql/DriverManager.java
blob: 4cee1fe1c5dc541ff9b4582f10c86888d92932f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package java.sql;

import dalvik.system.VMStack;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

/**
 * Provides facilities for managing JDBC drivers.
 * <p>
 * The {@code DriverManager} class loads JDBC drivers during its initialization,
 * from the list of drivers referenced by the system property {@code
 * "jdbc.drivers"}.
 */
public class DriverManager {

    /*
     * Facilities for logging. The Print Stream is deprecated but is maintained
     * here for compatibility.
     */
    private static PrintStream thePrintStream;

    private static PrintWriter thePrintWriter;

    // Login timeout value - by default set to 0 -> "wait forever"
    private static int loginTimeout = 0;

    /*
     * Set to hold Registered Drivers - initial capacity 10 drivers (will expand
     * automatically if necessary.
     */
    private static final List<Driver> theDrivers = new ArrayList<Driver>(10);

    // Permission for setting log
    private static final SQLPermission logPermission = new SQLPermission("setLog");

    /*
     * Load drivers on initialization
     */
    static {
        loadInitialDrivers();
    }

    /*
     * Loads the set of JDBC drivers defined by the Property "jdbc.drivers" if
     * it is defined.
     */
    private static void loadInitialDrivers() {
        String theDriverList = System.getProperty("jdbc.drivers", null);
        if (theDriverList == null) {
            return;
        }

        /*
         * Get the names of the drivers as an array of Strings from the system
         * property by splitting the property at the separator character ':'
         */
        String[] theDriverNames = theDriverList.split(":");

        for (String element : theDriverNames) {
            try {
                // Load the driver class
                Class
                        .forName(element, true, ClassLoader
                                .getSystemClassLoader());
            } catch (Throwable t) {
                // Ignored
            }
        }
    }

    /*
     * A private constructor to prevent allocation
     */
    private DriverManager() {
    }

    /**
     * Removes a driver from the {@code DriverManager}'s registered driver list.
     * This will only succeed when the caller's class loader loaded the driver
     * that is to be removed. If the driver was loaded by a different class
     * loader, the removal of the driver fails silently.
     * <p>
     * If the removal succeeds, the {@code DriverManager} will not use this
     * driver in the future when asked to get a {@code Connection}.
     *
     * @param driver
     *            the JDBC driver to remove.
     * @throws SQLException
     *             if there is a problem interfering with accessing the
     *             database.
     */
    public static void deregisterDriver(Driver driver) throws SQLException {
        if (driver == null) {
            return;
        }
        ClassLoader callerClassLoader = VMStack.getCallingClassLoader();
        if (!DriverManager.isClassFromClassLoader(driver, callerClassLoader)) {
            throw new SecurityException("calling class not authorized to deregister JDBC driver");
        }
        synchronized (theDrivers) {
            theDrivers.remove(driver);
        }
    }

    /**
     * Attempts to establish a connection to the given database URL.
     *
     * @param url
     *            a URL string representing the database target to connect with.
     * @return a {@code Connection} to the database identified by the URL.
     *         {@code null} if no connection can be established.
     * @throws SQLException
     *             if there is an error while attempting to connect to the
     *             database identified by the URL.
     */
    public static Connection getConnection(String url) throws SQLException {
        return getConnection(url, new Properties());
    }

    /**
     * Attempts to establish a connection to the given database URL.
     *
     * @param url
     *            a URL string representing the database target to connect with
     * @param info
     *            a set of properties to use as arguments to set up the
     *            connection. Properties are arbitrary string/value pairs.
     *            Normally, at least the properties {@code "user"} and {@code
     *            "password"} should be passed, with appropriate settings for
     *            the user ID and its corresponding password to get access to
     *            the corresponding database.
     * @return a {@code Connection} to the database identified by the URL.
     *         {@code null} if no connection can be established.
     * @throws SQLException
     *             if there is an error while attempting to connect to the
     *             database identified by the URL.
     */
    public static Connection getConnection(String url, Properties info) throws SQLException {
        // 08 - connection exception
        // 001 - SQL-client unable to establish SQL-connection
        String sqlState = "08001";
        if (url == null) {
            throw new SQLException("The url cannot be null", sqlState);
        }
        synchronized (theDrivers) {
            /*
             * Loop over the drivers in the DriverSet checking to see if one can
             * open a connection to the supplied URL - return the first
             * connection which is returned
             */
            for (Driver theDriver : theDrivers) {
                Connection theConnection = theDriver.connect(url, info);
                if (theConnection != null) {
                    return theConnection;
                }
            }
        }
        // If we get here, none of the drivers are able to resolve the URL
        throw new SQLException("No suitable driver", sqlState);
    }

    /**
     * Attempts to establish a connection to the given database URL.
     *
     * @param url
     *            a URL string representing the database target to connect with.
     * @param user
     *            a user ID used to login to the database.
     * @param password
     *            a password for the user ID to login to the database.
     * @return a {@code Connection} to the database identified by the URL.
     *         {@code null} if no connection can be established.
     * @throws SQLException
     *             if there is an error while attempting to connect to the
     *             database identified by the URL.
     */
    public static Connection getConnection(String url, String user, String password)
            throws SQLException {
        Properties theProperties = new Properties();
        if (user != null) {
            theProperties.setProperty("user", user);
        }
        if (password != null) {
            theProperties.setProperty("password", password);
        }
        return getConnection(url, theProperties);
    }

    /**
     * Tries to find a driver that can interpret the supplied URL.
     *
     * @param url
     *            the URL of a database.
     * @return a {@code Driver} that matches the provided URL. {@code null} if
     *         no {@code Driver} understands the URL
     * @throws SQLException
     *             if there is any kind of problem accessing the database.
     */
    public static Driver getDriver(String url) throws SQLException {
        ClassLoader callerClassLoader = VMStack.getCallingClassLoader();
        synchronized (theDrivers) {
            /*
             * Loop over the drivers in the DriverSet checking to see if one
             * does understand the supplied URL - return the first driver which
             * does understand the URL
             */
            for (Driver driver : theDrivers) {
                if (driver.acceptsURL(url) &&
                        DriverManager.isClassFromClassLoader(driver, callerClassLoader)) {
                    return driver;
                }
            }
        }
        // If no drivers understand the URL, throw an SQLException
        // SQLState: 08 - connection exception
        // 001 - SQL-client unable to establish SQL-connection
        throw new SQLException("No suitable driver", "08001");
    }

    /**
     * Returns an {@code Enumeration} that contains all of the loaded JDBC
     * drivers that the current caller can access.
     *
     * @return An {@code Enumeration} containing all the currently loaded JDBC
     *         {@code Drivers}.
     */
    public static Enumeration<Driver> getDrivers() {
        /*
         * Synchronize to avoid clashes with additions and removals of drivers
         * in the DriverSet
         */
        ClassLoader callerClassLoader = VMStack.getCallingClassLoader();
        synchronized (theDrivers) {
            ArrayList<Driver> result = new ArrayList<Driver>();
            for (Driver driver : theDrivers) {
                if (DriverManager.isClassFromClassLoader(driver, callerClassLoader)) {
                    result.add(driver);
                }
            }
            return Collections.enumeration(result);
        }
    }

    /**
     * Returns the login timeout when connecting to a database in seconds.
     *
     * @return the login timeout in seconds.
     */
    public static int getLoginTimeout() {
        return loginTimeout;
    }

    /**
     * Gets the log {@code PrintStream} used by the {@code DriverManager} and
     * all the JDBC Drivers.
     *
     * @deprecated use {@link #getLogWriter()} instead.
     * @return the {@code PrintStream} used for logging activities.
     */
    @Deprecated
    public static PrintStream getLogStream() {
        return thePrintStream;
    }

    /**
     * Retrieves the log writer.
     *
     * @return A {@code PrintWriter} object used as the log writer. {@code null}
     *         if no log writer is set.
     */
    public static PrintWriter getLogWriter() {
        return thePrintWriter;
    }

    /**
     * Prints a message to the current JDBC log stream. This is either the
     * {@code PrintWriter} or (deprecated) the {@code PrintStream}, if set.
     *
     * @param message
     *            the message to print to the JDBC log stream.
     */
    public static void println(String message) {
        if (thePrintWriter != null) {
            thePrintWriter.println(message);
            thePrintWriter.flush();
        } else if (thePrintStream != null) {
            thePrintStream.println(message);
            thePrintStream.flush();
        }
        /*
         * If neither the PrintWriter not the PrintStream are set, then silently
         * do nothing the message is not recorded and no exception is generated.
         */
    }

    /**
     * Registers a given JDBC driver with the {@code DriverManager}.
     * <p>
     * A newly loaded JDBC driver class should register itself with the
     * {@code DriverManager} by calling this method.
     *
     * @param driver
     *            the {@code Driver} to register with the {@code DriverManager}.
     * @throws SQLException
     *             if a database access error occurs.
     */
    public static void registerDriver(Driver driver) throws SQLException {
        if (driver == null) {
            throw new NullPointerException();
        }
        synchronized (theDrivers) {
            theDrivers.add(driver);
        }
    }

    /**
     * Sets the login timeout when connecting to a database in seconds.
     *
     * @param seconds
     *            seconds until timeout. 0 indicates wait forever.
     */
    public static void setLoginTimeout(int seconds) {
        loginTimeout = seconds;
    }

    /**
     * Sets the print stream to use for logging data from the {@code
     * DriverManager} and the JDBC drivers.
     *
     * @deprecated Use {@link #setLogWriter} instead.
     * @param out
     *            the {@code PrintStream} to use for logging.
     */
    @Deprecated
    public static void setLogStream(PrintStream out) {
        thePrintStream = out;
    }

    /**
     * Sets the {@code PrintWriter} that is used by all loaded drivers, and also
     * the {@code DriverManager}.
     *
     * @param out
     *            the {@code PrintWriter} to be used.
     */
    public static void setLogWriter(PrintWriter out) {
        thePrintWriter = out;
    }

    /**
     * Determines whether the supplied object was loaded by the given {@code ClassLoader}.
     *
     * @param theObject
     *            the object to check.
     * @param theClassLoader
     *            the {@code ClassLoader}.
     * @return {@code true} if the Object does belong to the {@code ClassLoader}
     *         , {@code false} otherwise
     */
    private static boolean isClassFromClassLoader(Object theObject,
            ClassLoader theClassLoader) {

        if ((theObject == null) || (theClassLoader == null)) {
            return false;
        }

        Class<?> objectClass = theObject.getClass();

        try {
            Class<?> checkClass = Class.forName(objectClass.getName(), true,
                    theClassLoader);
            if (checkClass == objectClass) {
                return true;
            }
        } catch (Throwable t) {
            // Empty
        }
        return false;
    }
}