001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
006: * Contact: sequoia@continuent.org
007: *
008: * Licensed under the Apache License, Version 2.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: * Initial developer(s): Marc Wick.
021: * Contributor(s): ______________________.
022: */package org.continuent.sequoia.controller.connection;
023:
024: import java.io.File;
025: import java.io.IOException;
026: import java.net.URL;
027: import java.sql.Connection;
028: import java.sql.Driver;
029: import java.sql.SQLException;
030: import java.util.HashMap;
031: import java.util.HashSet;
032: import java.util.Map;
033: import java.util.Set;
034:
035: import org.continuent.sequoia.common.i18n.Translate;
036: import org.continuent.sequoia.common.log.Trace;
037: import org.continuent.sequoia.controller.core.Controller;
038: import org.continuent.sequoia.controller.core.ControllerConstants;
039:
040: /**
041: * This class defines a DriverManager. In contrast to java.sql.DriverManager
042: * this class allows to use Drivers with the same name but with different
043: * versions, if no drivername is used it is a wrapper around
044: * java.sql.DriverManager.
045: *
046: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
047: * @version 1.0
048: */
049: public class DriverManager {
050:
051: /** Logger instance. */
052: static Trace logger = Trace
053: .getLogger("org.continuent.sequoia.controller.connection.DriverManager");
054:
055: /**
056: * Driver class names read from default drivers, without driverPath
057: */
058: private static Set defaultDrivers = new HashSet();
059:
060: /**
061: * We keep a reference to already loaded named drivers. Each named driver has
062: * been loaded with a separate classloader.
063: */
064: private static Map namedDrivers = new HashMap();
065:
066: /**
067: * Attempts to establish a connection to the given database URL. The
068: * <code>DriverManager</code> attempts to select an appropriate driver from
069: * the set of registered JDBC drivers.
070: *
071: * @param url a database url of the form
072: * <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
073: * @param user the database user on whose behalf the connection is being made
074: * @param password the user's password
075: * @param driverPathName the path where the driver classes are located, null
076: * if default directory
077: * @param driverClassName the class name of the driver
078: * @return a connection to the URL
079: * @exception SQLException if a database access error occurs
080: */
081: public static Connection getConnection(String url, String user,
082: String password, String driverPathName,
083: String driverClassName) throws SQLException {
084: Driver driver = null;
085: boolean isDefaultPath = false;
086:
087: if (driverPathName == null) {
088: // no path specified
089: // have we already loaded this driver
090: driver = (Driver) namedDrivers.get(driverClassName);
091: if (driver == null) {
092: // the driver has not yet been loaded
093: // first we try to load class from classpath
094: try {
095: if (driverClassName != null) {
096: loadDriverClass(driverClassName);
097: }
098: return java.sql.DriverManager.getConnection(url,
099: user, password);
100: } catch (ClassNotFoundException e) {
101: if (driverClassName == null) {
102: throw new SQLException(
103: "could not load driver as no class name is specified ");
104: }
105: try {
106: driverPathName = getDriversDir()
107: .getAbsolutePath();
108: isDefaultPath = true;
109: } catch (IOException ioExc) {
110: throw new SQLException(
111: "could not find default drivers directory");
112: }
113: }
114: }
115: }
116:
117: if (driver == null) {
118: // have we already loaded this named driver ?
119: driver = (Driver) namedDrivers.get(driverPathName);
120: }
121:
122: if (driver == null) {
123: // no driver with this name has been loaded so far
124: try {
125: File path = convertToAbsolutePath(driverPathName);
126: // we load the driver now
127: if (logger.isDebugEnabled()) {
128: logger.debug("loading driver with name "
129: + driverPathName + " for class "
130: + driverClassName);
131: }
132: driver = loadDriver(path, driverClassName);
133: } catch (Exception e) {
134: logger.error("Could not load driver for class "
135: + driverClassName, e);
136: throw new SQLException(
137: "could not load driver for class name "
138: + driverClassName + " and driverPath "
139: + driverPathName);
140: }
141:
142: // driver has been loaded successfully, we cache it for
143: // further use
144: if (isDefaultPath) {// we cache it with the class name
145: namedDrivers.put(driverClassName, driver);
146: } else {
147: // we cache it with the pathName
148: namedDrivers.put(driverPathName, driver);
149: }
150: }
151:
152: return getConnectionForDriver(url, user, password, driver);
153: }
154:
155: /**
156: * Load the driver class
157: *
158: * @param driverClassName the class name of the driver
159: * @throws ClassNotFoundException if the class could not be found
160: */
161: public static void loadDriverClass(String driverClassName)
162: throws ClassNotFoundException {
163: if (!defaultDrivers.contains(driverClassName)) {
164: if (logger.isDebugEnabled()) {
165: logger
166: .debug("we are using default classloader and driverClassName ="
167: + driverClassName);
168: }
169: Class.forName(driverClassName);
170: if (logger.isDebugEnabled())
171: logger.debug(Translate.get("backend.driver.loaded",
172: driverClassName));
173: // the driver was successfully loaded
174: defaultDrivers.add(driverClassName);
175: }
176: }
177:
178: /**
179: * convert a path into an absolute path if the path is already an absolute
180: * path, it is just returned otherwise a relative path is considered to be
181: * relative to the drivers directory
182: *
183: * @param pathName the relativ or absolute path
184: * @return the converted path
185: * @throws IOException if the converted path does not exist
186: */
187: public static File convertToAbsolutePath(String pathName)
188: throws IOException {
189: File dir = null;
190:
191: if (pathName != null) {
192: File path = new File(pathName);
193: if (path.canRead())
194: return path;
195: else
196: throw new IOException("Invalid path name " + pathName);
197: } else {
198: dir = getDriversDir();
199: }
200:
201: if (!dir.canRead()) {
202: String msg = Translate.get(
203: "controller.driver.dir.not.found", dir);
204: logger.error(msg);
205: throw new IOException(msg);
206: }
207:
208: return dir;
209: }
210:
211: private static File getDriversDir() throws IOException {
212: URL url = Controller.class
213: .getResource(ControllerConstants.SEQUOIA_DRIVER_JAR_FILE);
214: if (url == null) {
215: String msg = Translate.get(
216: "controller.driver.dir.not.found", url);
217: logger.error(msg);
218: throw new IOException(msg);
219: }
220:
221: File driversDir = new File(url.getFile()).getParentFile();
222:
223: if (!driversDir.exists()) {
224: String msg = Translate.get(
225: "controller.driver.dir.not.found", driversDir);
226: logger.error(msg);
227: throw new IOException(msg);
228: }
229: return driversDir;
230: }
231:
232: private static Connection getConnectionForDriver(String url,
233: String user, String password, Driver driver)
234: throws SQLException {
235: java.util.Properties info = new java.util.Properties();
236: if (user != null) {
237: info.put("user", user);
238: }
239: if (password != null) {
240: info.put("password", password);
241: }
242:
243: return driver.connect(url, info);
244: }
245:
246: private static Driver loadDriver(File path, String driverClassName)
247: throws ClassNotFoundException, InstantiationException,
248: IllegalAccessException {
249: ClassLoader loader = new DriverClassLoader(null, path);
250:
251: // load java.sql.DriverManager with the new classloader
252: // Driver instances register with the DriverManager and we want the new
253: // Driver to register with its own DriverManager
254: // Otherwise the new Driver would register with the default DriverManager
255: // and possibly overwrite an other driver with the same name
256: Class.forName(java.sql.DriverManager.class.getName(), true,
257: loader);
258:
259: // load class
260: Class driverClass = Class
261: .forName(driverClassName, true, loader);
262:
263: if (logger.isDebugEnabled())
264: logger.debug(Translate.get("backend.driver.loaded",
265: driverClassName));
266:
267: // get an instance of the class and return it
268: return (Driver) driverClass.newInstance();
269:
270: }
271:
272: }
|