0N/A/*
6014N/A * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage java.sql;
0N/A
0N/Aimport java.util.Iterator;
0N/Aimport java.util.ServiceLoader;
0N/Aimport java.security.AccessController;
0N/Aimport java.security.PrivilegedAction;
3768N/Aimport java.util.concurrent.CopyOnWriteArrayList;
6338N/Aimport sun.reflect.CallerSensitive;
6338N/Aimport sun.reflect.Reflection;
0N/A
0N/A/**
0N/A * <P>The basic service for managing a set of JDBC drivers.<br>
0N/A * <B>NOTE:</B> The {@link <code>DataSource</code>} interface, new in the
0N/A * JDBC 2.0 API, provides another way to connect to a data source.
0N/A * The use of a <code>DataSource</code> object is the preferred means of
0N/A * connecting to a data source.
0N/A *
0N/A * <P>As part of its initialization, the <code>DriverManager</code> class will
0N/A * attempt to load the driver classes referenced in the "jdbc.drivers"
0N/A * system property. This allows a user to customize the JDBC Drivers
0N/A * used by their applications. For example in your
0N/A * ~/.hotjava/properties file you might specify:
0N/A * <pre>
0N/A * <CODE>jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.taste.ourDriver</CODE>
0N/A * </pre>
0N/A *<P> The <code>DriverManager</code> methods <code>getConnection</code> and
0N/A * <code>getDrivers</code> have been enhanced to support the Java Standard Edition
0N/A * <a href="../../../technotes/guides/jar/jar.html#Service%20Provider">Service Provider</a> mechanism. JDBC 4.0 Drivers must
0N/A * include the file <code>META-INF/services/java.sql.Driver</code>. This file contains the name of the JDBC drivers
0N/A * implementation of <code>java.sql.Driver</code>. For example, to load the <code>my.sql.Driver</code> class,
0N/A * the <code>META-INF/services/java.sql.Driver</code> file would contain the entry:
0N/A * <pre>
0N/A * <code>my.sql.Driver</code>
0N/A * </pre>
0N/A *
0N/A * <P>Applications no longer need to explictly load JDBC drivers using <code>Class.forName()</code>. Existing programs
0N/A * which currently load JDBC drivers using <code>Class.forName()</code> will continue to work without
0N/A * modification.
0N/A *
0N/A * <P>When the method <code>getConnection</code> is called,
0N/A * the <code>DriverManager</code> will attempt to
0N/A * locate a suitable driver from amongst those loaded at
0N/A * initialization and those loaded explicitly using the same classloader
0N/A * as the current applet or application.
0N/A *
0N/A * <P>
0N/A * Starting with the Java 2 SDK, Standard Edition, version 1.3, a
0N/A * logging stream can be set only if the proper
0N/A * permission has been granted. Normally this will be done with
0N/A * the tool PolicyTool, which can be used to grant <code>permission
0N/A * java.sql.SQLPermission "setLog"</code>.
0N/A * @see Driver
0N/A * @see Connection
0N/A */
0N/Apublic class DriverManager {
0N/A
0N/A
3768N/A // List of registered JDBC drivers
3981N/A private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();
3768N/A private static volatile int loginTimeout = 0;
3768N/A private static volatile java.io.PrintWriter logWriter = null;
3768N/A private static volatile java.io.PrintStream logStream = null;
3768N/A // Used in println() to synchronize logWriter
3768N/A private final static Object logSync = new Object();
3768N/A
3768N/A /* Prevent the DriverManager class from being instantiated. */
3768N/A private DriverManager(){}
3768N/A
3768N/A
3768N/A /**
3768N/A * Load the initial JDBC drivers by checking the System property
3768N/A * jdbc.properties and then use the {@code ServiceLoader} mechanism
3768N/A */
3768N/A static {
3768N/A loadInitialDrivers();
3768N/A println("JDBC DriverManager initialized");
3768N/A }
3768N/A
0N/A /**
0N/A * The <code>SQLPermission</code> constant that allows the
0N/A * setting of the logging stream.
0N/A * @since 1.3
0N/A */
0N/A final static SQLPermission SET_LOG_PERMISSION =
0N/A new SQLPermission("setLog");
0N/A
0N/A //--------------------------JDBC 2.0-----------------------------
0N/A
0N/A /**
0N/A * Retrieves the log writer.
0N/A *
0N/A * The <code>getLogWriter</code> and <code>setLogWriter</code>
0N/A * methods should be used instead
0N/A * of the <code>get/setlogStream</code> methods, which are deprecated.
0N/A * @return a <code>java.io.PrintWriter</code> object
0N/A * @see #setLogWriter
0N/A * @since 1.2
0N/A */
0N/A public static java.io.PrintWriter getLogWriter() {
0N/A return logWriter;
0N/A }
0N/A
0N/A /**
0N/A * Sets the logging/tracing <code>PrintWriter</code> object
0N/A * that is used by the <code>DriverManager</code> and all drivers.
0N/A * <P>
0N/A * There is a minor versioning problem created by the introduction
0N/A * of the method <code>setLogWriter</code>. The
0N/A * method <code>setLogWriter</code> cannot create a <code>PrintStream</code> object
0N/A * that will be returned by <code>getLogStream</code>---the Java platform does
0N/A * not provide a backward conversion. As a result, a new application
0N/A * that uses <code>setLogWriter</code> and also uses a JDBC 1.0 driver that uses
0N/A * <code>getLogStream</code> will likely not see debugging information written
0N/A * by that driver.
0N/A *<P>
0N/A * Starting with the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
0N/A * to see that there is an <code>SQLPermission</code> object before setting
0N/A * the logging stream. If a <code>SecurityManager</code> exists and its
0N/A * <code>checkPermission</code> method denies setting the log writer, this
0N/A * method throws a <code>java.lang.SecurityException</code>.
0N/A *
0N/A * @param out the new logging/tracing <code>PrintStream</code> object;
0N/A * <code>null</code> to disable logging and tracing
0N/A * @throws SecurityException
0N/A * if a security manager exists and its
0N/A * <code>checkPermission</code> method denies
0N/A * setting the log writer
0N/A *
0N/A * @see SecurityManager#checkPermission
0N/A * @see #getLogWriter
0N/A * @since 1.2
0N/A */
0N/A public static void setLogWriter(java.io.PrintWriter out) {
0N/A
0N/A SecurityManager sec = System.getSecurityManager();
0N/A if (sec != null) {
0N/A sec.checkPermission(SET_LOG_PERMISSION);
0N/A }
0N/A logStream = null;
0N/A logWriter = out;
0N/A }
0N/A
0N/A
0N/A //---------------------------------------------------------------
0N/A
0N/A /**
0N/A * Attempts to establish a connection to the given database URL.
0N/A * The <code>DriverManager</code> attempts to select an appropriate driver from
0N/A * the set of registered JDBC drivers.
0N/A *
0N/A * @param url a database url of the form
0N/A * <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
0N/A * @param info a list of arbitrary string tag/value pairs as
0N/A * connection arguments; normally at least a "user" and
0N/A * "password" property should be included
0N/A * @return a Connection to the URL
0N/A * @exception SQLException if a database access error occurs
0N/A */
6338N/A @CallerSensitive
0N/A public static Connection getConnection(String url,
0N/A java.util.Properties info) throws SQLException {
6338N/A return (getConnection(url, info, Reflection.getCallerClass()));
0N/A }
0N/A
0N/A /**
0N/A * Attempts to establish a connection to the given database URL.
0N/A * The <code>DriverManager</code> attempts to select an appropriate driver from
0N/A * the set of registered JDBC drivers.
0N/A *
0N/A * @param url a database url of the form
0N/A * <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
0N/A * @param user the database user on whose behalf the connection is being
0N/A * made
0N/A * @param password the user's password
0N/A * @return a connection to the URL
0N/A * @exception SQLException if a database access error occurs
0N/A */
6338N/A @CallerSensitive
0N/A public static Connection getConnection(String url,
0N/A String user, String password) throws SQLException {
0N/A java.util.Properties info = new java.util.Properties();
0N/A
0N/A if (user != null) {
0N/A info.put("user", user);
0N/A }
0N/A if (password != null) {
0N/A info.put("password", password);
0N/A }
0N/A
6338N/A return (getConnection(url, info, Reflection.getCallerClass()));
0N/A }
0N/A
0N/A /**
0N/A * Attempts to establish a connection to the given database URL.
0N/A * The <code>DriverManager</code> attempts to select an appropriate driver from
0N/A * the set of registered JDBC drivers.
0N/A *
0N/A * @param url a database url of the form
0N/A * <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
0N/A * @return a connection to the URL
0N/A * @exception SQLException if a database access error occurs
0N/A */
6338N/A @CallerSensitive
0N/A public static Connection getConnection(String url)
0N/A throws SQLException {
0N/A
0N/A java.util.Properties info = new java.util.Properties();
6338N/A return (getConnection(url, info, Reflection.getCallerClass()));
0N/A }
0N/A
0N/A /**
0N/A * Attempts to locate a driver that understands the given URL.
0N/A * The <code>DriverManager</code> attempts to select an appropriate driver from
0N/A * the set of registered JDBC drivers.
0N/A *
0N/A * @param url a database URL of the form
0N/A * <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
0N/A * @return a <code>Driver</code> object representing a driver
0N/A * that can connect to the given URL
0N/A * @exception SQLException if a database access error occurs
0N/A */
6338N/A @CallerSensitive
0N/A public static Driver getDriver(String url)
0N/A throws SQLException {
0N/A
0N/A println("DriverManager.getDriver(\"" + url + "\")");
0N/A
6338N/A Class<?> callerClass = Reflection.getCallerClass();
0N/A
3768N/A // Walk through the loaded registeredDrivers attempting to locate someone
0N/A // who understands the given URL.
3981N/A for (DriverInfo aDriver : registeredDrivers) {
0N/A // If the caller does not have permission to load the driver then
0N/A // skip it.
6338N/A if(isDriverAllowed(aDriver.driver, callerClass)) {
3768N/A try {
3981N/A if(aDriver.driver.acceptsURL(url)) {
3768N/A // Success!
3981N/A println("getDriver returning " + aDriver.driver.getClass().getName());
3981N/A return (aDriver.driver);
3768N/A }
3768N/A
3768N/A } catch(SQLException sqe) {
3768N/A // Drop through and try the next driver.
3768N/A }
3768N/A } else {
3981N/A println(" skipping: " + aDriver.driver.getClass().getName());
0N/A }
3768N/A
0N/A }
0N/A
0N/A println("getDriver: no suitable driver");
0N/A throw new SQLException("No suitable driver", "08001");
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Registers the given driver with the <code>DriverManager</code>.
0N/A * A newly-loaded driver class should call
0N/A * the method <code>registerDriver</code> to make itself
0N/A * known to the <code>DriverManager</code>.
0N/A *
0N/A * @param driver the new JDBC Driver that is to be registered with the
0N/A * <code>DriverManager</code>
0N/A * @exception SQLException if a database access error occurs
0N/A */
0N/A public static synchronized void registerDriver(java.sql.Driver driver)
0N/A throws SQLException {
3768N/A
3768N/A /* Register the driver if it has not already been added to our list */
3768N/A if(driver != null) {
3981N/A registeredDrivers.addIfAbsent(new DriverInfo(driver));
3768N/A } else {
3768N/A // This is for compatibility with the original DriverManager
3768N/A throw new NullPointerException();
0N/A }
0N/A
3768N/A println("registerDriver: " + driver);
0N/A
0N/A }
0N/A
0N/A /**
0N/A * Drops a driver from the <code>DriverManager</code>'s list.
0N/A * Applets can only deregister drivers from their own classloaders.
0N/A *
0N/A * @param driver the JDBC Driver to drop
0N/A * @exception SQLException if a database access error occurs
0N/A */
6338N/A @CallerSensitive
0N/A public static synchronized void deregisterDriver(Driver driver)
0N/A throws SQLException {
3768N/A if (driver == null) {
3768N/A return;
3768N/A }
3768N/A
0N/A println("DriverManager.deregisterDriver: " + driver);
0N/A
3981N/A DriverInfo aDriver = new DriverInfo(driver);
3981N/A if(registeredDrivers.contains(aDriver)) {
6338N/A if (isDriverAllowed(driver, Reflection.getCallerClass())) {
3981N/A registeredDrivers.remove(aDriver);
3768N/A } else {
3768N/A // If the caller does not have permission to load the driver then
3768N/A // throw a SecurityException.
3768N/A throw new SecurityException();
0N/A }
3768N/A } else {
0N/A println(" couldn't find driver to unload");
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Retrieves an Enumeration with all of the currently loaded JDBC drivers
0N/A * to which the current caller has access.
0N/A *
0N/A * <P><B>Note:</B> The classname of a driver can be found using
0N/A * <CODE>d.getClass().getName()</CODE>
0N/A *
0N/A * @return the list of JDBC Drivers loaded by the caller's class loader
0N/A */
6338N/A @CallerSensitive
0N/A public static java.util.Enumeration<Driver> getDrivers() {
3768N/A java.util.Vector<Driver> result = new java.util.Vector<Driver>();
0N/A
6338N/A Class<?> callerClass = Reflection.getCallerClass();
0N/A
3768N/A // Walk through the loaded registeredDrivers.
3981N/A for(DriverInfo aDriver : registeredDrivers) {
0N/A // If the caller does not have permission to load the driver then
0N/A // skip it.
6338N/A if(isDriverAllowed(aDriver.driver, callerClass)) {
3981N/A result.addElement(aDriver.driver);
3768N/A } else {
3768N/A println(" skipping: " + aDriver.getClass().getName());
0N/A }
0N/A }
0N/A return (result.elements());
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Sets the maximum time in seconds that a driver will wait
0N/A * while attempting to connect to a database.
0N/A *
0N/A * @param seconds the login time limit in seconds; zero means there is no limit
0N/A * @see #getLoginTimeout
0N/A */
0N/A public static void setLoginTimeout(int seconds) {
0N/A loginTimeout = seconds;
0N/A }
0N/A
0N/A /**
0N/A * Gets the maximum time in seconds that a driver can wait
0N/A * when attempting to log in to a database.
0N/A *
0N/A * @return the driver login time limit in seconds
0N/A * @see #setLoginTimeout
0N/A */
0N/A public static int getLoginTimeout() {
0N/A return (loginTimeout);
0N/A }
0N/A
0N/A /**
0N/A * Sets the logging/tracing PrintStream that is used
0N/A * by the <code>DriverManager</code>
0N/A * and all drivers.
0N/A *<P>
0N/A * In the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
0N/A * to see that there is an <code>SQLPermission</code> object before setting
0N/A * the logging stream. If a <code>SecurityManager</code> exists and its
0N/A * <code>checkPermission</code> method denies setting the log writer, this
0N/A * method throws a <code>java.lang.SecurityException</code>.
0N/A *
0N/A * @param out the new logging/tracing PrintStream; to disable, set to <code>null</code>
0N/A * @deprecated
0N/A * @throws SecurityException if a security manager exists and its
0N/A * <code>checkPermission</code> method denies setting the log stream
0N/A *
0N/A * @see SecurityManager#checkPermission
0N/A * @see #getLogStream
0N/A */
0N/A public static void setLogStream(java.io.PrintStream out) {
0N/A
0N/A SecurityManager sec = System.getSecurityManager();
0N/A if (sec != null) {
0N/A sec.checkPermission(SET_LOG_PERMISSION);
0N/A }
0N/A
0N/A logStream = out;
0N/A if ( out != null )
0N/A logWriter = new java.io.PrintWriter(out);
0N/A else
0N/A logWriter = null;
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the logging/tracing PrintStream that is used by the <code>DriverManager</code>
0N/A * and all drivers.
0N/A *
0N/A * @return the logging/tracing PrintStream; if disabled, is <code>null</code>
0N/A * @deprecated
0N/A * @see #setLogStream
0N/A */
0N/A public static java.io.PrintStream getLogStream() {
0N/A return logStream;
0N/A }
0N/A
0N/A /**
0N/A * Prints a message to the current JDBC log stream.
0N/A *
0N/A * @param message a log or tracing message
0N/A */
0N/A public static void println(String message) {
0N/A synchronized (logSync) {
0N/A if (logWriter != null) {
0N/A logWriter.println(message);
0N/A
0N/A // automatic flushing is never enabled, so we must do it ourselves
0N/A logWriter.flush();
0N/A }
0N/A }
0N/A }
0N/A
0N/A //------------------------------------------------------------------------
0N/A
3768N/A // Indicates whether the class object that would be created if the code calling
3768N/A // DriverManager is accessible.
6338N/A private static boolean isDriverAllowed(Driver driver, Class<?> caller) {
6338N/A ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
6338N/A return isDriverAllowed(driver, callerCL);
6338N/A }
6338N/A
3768N/A private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
3768N/A boolean result = false;
3768N/A if(driver != null) {
3768N/A Class<?> aClass = null;
3768N/A try {
3768N/A aClass = Class.forName(driver.getClass().getName(), true, classLoader);
3768N/A } catch (Exception ex) {
3768N/A result = false;
3768N/A }
0N/A
3768N/A result = ( aClass == driver.getClass() ) ? true : false;
0N/A }
0N/A
3768N/A return result;
0N/A }
0N/A
0N/A private static void loadInitialDrivers() {
0N/A String drivers;
0N/A try {
3985N/A drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
3985N/A public String run() {
0N/A return System.getProperty("jdbc.drivers");
0N/A }
0N/A });
0N/A } catch (Exception ex) {
0N/A drivers = null;
0N/A }
0N/A // If the driver is packaged as a Service Provider, load it.
0N/A // Get all the drivers through the classloader
0N/A // exposed as a java.sql.Driver.class service.
0N/A // ServiceLoader.load() replaces the sun.misc.Providers()
0N/A
3985N/A AccessController.doPrivileged(new PrivilegedAction<Void>() {
3985N/A public Void run() {
0N/A
0N/A ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
0N/A Iterator driversIterator = loadedDrivers.iterator();
0N/A
0N/A /* Load these drivers, so that they can be instantiated.
0N/A * It may be the case that the driver class may not be there
0N/A * i.e. there may be a packaged driver with the service class
0N/A * as implementation of java.sql.Driver but the actual class
0N/A * may be missing. In that case a java.util.ServiceConfigurationError
0N/A * will be thrown at runtime by the VM trying to locate
0N/A * and load the service.
0N/A *
0N/A * Adding a try catch block to catch those runtime errors
0N/A * if driver not available in classpath but it's
0N/A * packaged as service and that service is there in classpath.
0N/A */
0N/A try{
0N/A while(driversIterator.hasNext()) {
6014N/A driversIterator.next();
0N/A }
0N/A } catch(Throwable t) {
0N/A // Do nothing
0N/A }
0N/A return null;
0N/A }
0N/A });
0N/A
0N/A println("DriverManager.initialize: jdbc.drivers = " + drivers);
3768N/A
3768N/A if (drivers == null || drivers.equals("")) {
0N/A return;
0N/A }
3768N/A String[] driversList = drivers.split(":");
3768N/A println("number of Drivers:" + driversList.length);
3768N/A for (String aDriver : driversList) {
0N/A try {
3768N/A println("DriverManager.Initialize: loading " + aDriver);
3768N/A Class.forName(aDriver, true,
3768N/A ClassLoader.getSystemClassLoader());
0N/A } catch (Exception ex) {
0N/A println("DriverManager.Initialize: load failed: " + ex);
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A // Worker method called by the public getConnection() methods.
0N/A private static Connection getConnection(
6338N/A String url, java.util.Properties info, Class<?> caller) throws SQLException {
0N/A /*
0N/A * When callerCl is null, we should check the application's
0N/A * (which is invoking this class indirectly)
0N/A * classloader, so that the JDBC driver class outside rt.jar
0N/A * can be loaded from here.
0N/A */
6338N/A ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
6338N/A synchronized (DriverManager.class) {
6338N/A // synchronize loading of the correct classloader.
6338N/A if (callerCL == null) {
6338N/A callerCL = Thread.currentThread().getContextClassLoader();
6338N/A }
0N/A }
0N/A
0N/A if(url == null) {
0N/A throw new SQLException("The url cannot be null", "08001");
0N/A }
0N/A
0N/A println("DriverManager.getConnection(\"" + url + "\")");
0N/A
3768N/A // Walk through the loaded registeredDrivers attempting to make a connection.
0N/A // Remember the first exception that gets raised so we can reraise it.
0N/A SQLException reason = null;
0N/A
3981N/A for(DriverInfo aDriver : registeredDrivers) {
0N/A // If the caller does not have permission to load the driver then
0N/A // skip it.
3981N/A if(isDriverAllowed(aDriver.driver, callerCL)) {
3768N/A try {
3981N/A println(" trying " + aDriver.driver.getClass().getName());
3981N/A Connection con = aDriver.driver.connect(url, info);
3768N/A if (con != null) {
3768N/A // Success!
3981N/A println("getConnection returning " + aDriver.driver.getClass().getName());
3768N/A return (con);
3768N/A }
3768N/A } catch (SQLException ex) {
3768N/A if (reason == null) {
3768N/A reason = ex;
3768N/A }
3768N/A }
3768N/A
3768N/A } else {
3768N/A println(" skipping: " + aDriver.getClass().getName());
0N/A }
3768N/A
0N/A }
0N/A
0N/A // if we got here nobody could connect.
0N/A if (reason != null) {
0N/A println("getConnection failed: " + reason);
0N/A throw reason;
0N/A }
0N/A
0N/A println("getConnection: no suitable driver found for "+ url);
0N/A throw new SQLException("No suitable driver found for "+ url, "08001");
0N/A }
0N/A}
3981N/A
3981N/A/*
3981N/A * Wrapper class for registered Drivers in order to not expose Driver.equals()
3981N/A * to avoid the capture of the Driver it being compared to as it might not
3981N/A * normally have access.
3981N/A */
3981N/Aclass DriverInfo {
3981N/A
3981N/A final Driver driver;
3981N/A DriverInfo(Driver driver) {
3981N/A this.driver = driver;
3981N/A }
3981N/A
3981N/A public boolean equals(Object other) {
3981N/A return (other instanceof DriverInfo)
3981N/A && this.driver == ((DriverInfo) other).driver;
3981N/A }
3981N/A
3981N/A public int hashCode() {
3981N/A return driver.hashCode();
3981N/A }
3981N/A
3981N/A public String toString() {
3981N/A return ("driver[className=" + driver + "]");
3981N/A }
3981N/A}