1 /*
2 * This file is part of dependency-check-core.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * Copyright (c) 2014 Jeremy Long. All Rights Reserved.
17 */
18 package org.owasp.dependencycheck.data.nvdcve;
19
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 import java.io.File;
24 import java.lang.reflect.InvocationTargetException;
25 import java.net.MalformedURLException;
26 import java.net.URL;
27 import java.net.URLClassLoader;
28 import java.security.AccessController;
29 import java.security.PrivilegedAction;
30 import java.sql.Driver;
31 import java.sql.DriverManager;
32 import java.sql.SQLException;
33 import java.util.ArrayList;
34 import java.util.List;
35 import javax.annotation.concurrent.ThreadSafe;
36
37 /**
38 * DriverLoader is a utility class that is used to load database drivers.
39 *
40 * @author Jeremy Long
41 */
42 @ThreadSafe
43 public final class DriverLoader {
44
45 /**
46 * The logger.
47 */
48 private static final Logger LOGGER = LoggerFactory.getLogger(DriverLoader.class);
49
50 /**
51 * De-registers the driver.
52 *
53 * @param driver the driver to de-register
54 */
55 public static void cleanup(Driver driver) {
56 try {
57 LOGGER.debug("Begin deregister driver");
58 DriverManager.deregisterDriver(driver);
59 LOGGER.debug("End deregister driver");
60 } catch (SQLException ex) {
61 LOGGER.debug("An error occurred unloading the database driver", ex);
62 } catch (Throwable unexpected) {
63 LOGGER.debug("An unexpected throwable occurred unloading the database driver", unexpected);
64 }
65 }
66
67 /**
68 * Private constructor for a utility class.
69 */
70 private DriverLoader() {
71 }
72
73 /**
74 * Loads the specified class using the system class loader and registers the
75 * driver with the driver manager.
76 *
77 * @param className the fully qualified name of the desired class
78 * @return the loaded Driver
79 * @throws DriverLoadException thrown if the driver cannot be loaded
80 */
81 public static Driver load(String className) throws DriverLoadException {
82 final ClassLoader loader = DriverLoader.class.getClassLoader();
83 return load(className, loader);
84 }
85
86 /**
87 * Loads the specified class by registering the supplied paths to the class
88 * loader and then registers the driver with the driver manager. The
89 * pathToDriver argument is added to the class loader so that an external
90 * driver can be loaded. Note, the pathToDriver can contain a semi-colon
91 * separated list of paths so any dependencies can be added as needed. If a
92 * path in the pathToDriver argument is a directory all files in the
93 * directory are added to the class path.
94 *
95 * @param className the fully qualified name of the desired class
96 * @param pathToDriver the path to the JAR file containing the driver; note,
97 * this can be a semi-colon separated list of paths
98 * @return the loaded Driver
99 * @throws DriverLoadException thrown if the driver cannot be loaded
100 */
101 @SuppressWarnings("StringSplitter")
102 public static Driver load(String className, String pathToDriver) throws DriverLoadException {
103 final ClassLoader parent = ClassLoader.getSystemClassLoader();
104 final List<URL> urls = new ArrayList<>();
105 final String[] paths = pathToDriver.split(File.pathSeparator);
106 for (String path : paths) {
107 final File file = new File(path);
108 if (file.isDirectory()) {
109 final File[] files = file.listFiles();
110 if (files != null) {
111 for (File f : files) {
112 try {
113 urls.add(f.toURI().toURL());
114 } catch (MalformedURLException ex) {
115 LOGGER.debug("Unable to load database driver '{}'; invalid path provided '{}'",
116 className, f.getAbsoluteFile(), ex);
117 throw new DriverLoadException("Unable to load database driver. Invalid path provided", ex);
118 }
119 }
120 }
121 } else if (file.exists()) {
122 try {
123 urls.add(file.toURI().toURL());
124 } catch (MalformedURLException ex) {
125 LOGGER.debug("Unable to load database driver '{}'; invalid path provided '{}'",
126 className, file.getAbsoluteFile(), ex);
127 throw new DriverLoadException("Unable to load database driver. Invalid path provided", ex);
128 }
129 }
130 }
131 final URLClassLoader loader = AccessController.doPrivileged((PrivilegedAction<URLClassLoader>) () ->
132 new URLClassLoader(urls.toArray(new URL[0]), parent));
133
134 return load(className, loader);
135 }
136
137 /**
138 * Loads the specified class using the supplied class loader and registers
139 * the driver with the driver manager.
140 *
141 * @param className the fully qualified name of the desired class
142 * @param loader the class loader to use when loading the driver
143 * @return the loaded Driver
144 * @throws DriverLoadException thrown if the driver cannot be loaded
145 */
146 private static Driver load(String className, ClassLoader loader) throws DriverLoadException {
147 try {
148 final Class<?> c = Class.forName(className, true, loader);
149 //final Class c = loader.loadClass(className);
150 final Driver driver = (Driver) c.getDeclaredConstructor().newInstance();
151
152 //TODO add usage count so we don't de-register a driver that is in use.
153 final Driver shim = new DriverShim(driver);
154 //using the DriverShim to get around the fact that the DriverManager won't register a driver not in the base class path
155 DriverManager.registerDriver(shim);
156 return shim;
157 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | SQLException
158 | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
159 final String msg = String.format("Unable to load database driver '%s'", className);
160 LOGGER.debug(msg, ex);
161 throw new DriverLoadException(msg, ex);
162 }
163 }
164 }