View Javadoc
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 }