DriverLoader.java
/*
* This file is part of dependency-check-core.
*
* Licensed 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.
*
* Copyright (c) 2014 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.data.nvdcve;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.concurrent.ThreadSafe;
/**
* DriverLoader is a utility class that is used to load database drivers.
*
* @author Jeremy Long
*/
@ThreadSafe
public final class DriverLoader {
/**
* The logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(DriverLoader.class);
/**
* De-registers the driver.
*
* @param driver the driver to de-register
*/
public static void cleanup(Driver driver) {
try {
LOGGER.debug("Begin deregister driver");
DriverManager.deregisterDriver(driver);
LOGGER.debug("End deregister driver");
} catch (SQLException ex) {
LOGGER.debug("An error occurred unloading the database driver", ex);
} catch (Throwable unexpected) {
LOGGER.debug("An unexpected throwable occurred unloading the database driver", unexpected);
}
}
/**
* Private constructor for a utility class.
*/
private DriverLoader() {
}
/**
* Loads the specified class using the system class loader and registers the
* driver with the driver manager.
*
* @param className the fully qualified name of the desired class
* @return the loaded Driver
* @throws DriverLoadException thrown if the driver cannot be loaded
*/
public static Driver load(String className) throws DriverLoadException {
final ClassLoader loader = DriverLoader.class.getClassLoader();
return load(className, loader);
}
/**
* Loads the specified class by registering the supplied paths to the class
* loader and then registers the driver with the driver manager. The
* pathToDriver argument is added to the class loader so that an external
* driver can be loaded. Note, the pathToDriver can contain a semi-colon
* separated list of paths so any dependencies can be added as needed. If a
* path in the pathToDriver argument is a directory all files in the
* directory are added to the class path.
*
* @param className the fully qualified name of the desired class
* @param pathToDriver the path to the JAR file containing the driver; note,
* this can be a semi-colon separated list of paths
* @return the loaded Driver
* @throws DriverLoadException thrown if the driver cannot be loaded
*/
@SuppressWarnings("StringSplitter")
public static Driver load(String className, String pathToDriver) throws DriverLoadException {
final ClassLoader parent = ClassLoader.getSystemClassLoader();
final List<URL> urls = new ArrayList<>();
final String[] paths = pathToDriver.split(File.pathSeparator);
for (String path : paths) {
final File file = new File(path);
if (file.isDirectory()) {
final File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
try {
urls.add(f.toURI().toURL());
} catch (MalformedURLException ex) {
LOGGER.debug("Unable to load database driver '{}'; invalid path provided '{}'",
className, f.getAbsoluteFile(), ex);
throw new DriverLoadException("Unable to load database driver. Invalid path provided", ex);
}
}
}
} else if (file.exists()) {
try {
urls.add(file.toURI().toURL());
} catch (MalformedURLException ex) {
LOGGER.debug("Unable to load database driver '{}'; invalid path provided '{}'",
className, file.getAbsoluteFile(), ex);
throw new DriverLoadException("Unable to load database driver. Invalid path provided", ex);
}
}
}
final URLClassLoader loader = AccessController.doPrivileged((PrivilegedAction<URLClassLoader>) () ->
new URLClassLoader(urls.toArray(new URL[0]), parent));
return load(className, loader);
}
/**
* Loads the specified class using the supplied class loader and registers
* the driver with the driver manager.
*
* @param className the fully qualified name of the desired class
* @param loader the class loader to use when loading the driver
* @return the loaded Driver
* @throws DriverLoadException thrown if the driver cannot be loaded
*/
private static Driver load(String className, ClassLoader loader) throws DriverLoadException {
try {
final Class<?> c = Class.forName(className, true, loader);
//final Class c = loader.loadClass(className);
final Driver driver = (Driver) c.getDeclaredConstructor().newInstance();
//TODO add usage count so we don't de-register a driver that is in use.
final Driver shim = new DriverShim(driver);
//using the DriverShim to get around the fact that the DriverManager won't register a driver not in the base class path
DriverManager.registerDriver(shim);
return shim;
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | SQLException
| NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
final String msg = String.format("Unable to load database driver '%s'", className);
LOGGER.debug(msg, ex);
throw new DriverLoadException(msg, ex);
}
}
}