001/*
002MIT License
003
004Copyright (c) 2020 FBSQL Team
005
006Permission is hereby granted, free of charge, to any person obtaining a copy
007of this software and associated documentation files (the "Software"), to deal
008in the Software without restriction, including without limitation the rights
009to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010copies of the Software, and to permit persons to whom the Software is
011furnished to do so, subject to the following conditions:
012
013The above copyright notice and this permission notice shall be included in all
014copies or substantial portions of the Software.
015
016THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022SOFTWARE.
023
024Home:   https://fbsql.github.io
025E-Mail: fbsql.team@gmail.com
026*/
027
028package org.fbsql.servlet;
029
030import java.lang.reflect.InvocationTargetException;
031import java.net.MalformedURLException;
032import java.net.URL;
033import java.net.URLClassLoader;
034import java.sql.Driver;
035import java.sql.DriverManager;
036import java.sql.SQLException;
037import java.util.List;
038import java.util.ServiceLoader;
039
040import javax.servlet.http.HttpServlet;
041
042public class ClassLoaderUtils extends HttpServlet {
043
044        /**
045         * Load JDBC driver class from jar files specified as value of "jdbc.jars" property name in configuration file
046         * Value "jdbc.jars" must contains comma-separated JARs file names or URLs
047         * 
048         * @param  array of JAR files
049         * @return custom URL ClassLoader based on specified JAR files
050         */
051        static ClassLoader createClassLoader(List<String> jars) {
052                URL[] urls = new URL[jars.size()];
053                for (int i = 0; i < urls.length; i++) {
054                        String el = jars.get(i).trim();
055                        if (el.isEmpty())
056                                continue;
057                        if (!el.startsWith("file://") && !el.startsWith("http://") && !el.startsWith("https://"))
058                                el = "file://" + el;
059                        try {
060                                urls[i] = new URL(el);
061                        } catch (MalformedURLException e) {
062                                System.out.println("WARNING Ignored malformed URL: " + el);
063                        }
064                }
065                return new URLClassLoader(urls);
066        }
067
068        /**
069         * Register JDBC driver
070         * 
071         * DriverManager will refuse to use a driver not loaded by
072         * the system ClassLoader
073         * The workaround for this is to use a shim class that implements
074         * java.sql.Driver. This shim class will do nothing but call the methods
075         * of an instance of a JDBC driver that is loaded dynamically. This works
076         * because DriverShim is loaded by the system class loader, and DriverManager
077         * doesn't care that it invokes a class that wasn't.
078         * 
079         * @see DriverShim
080         * 
081         * @param jars        value of "jdbc.jars" property name in configuration file
082         * @param classLoader custom URL ClassLoader
083         * 
084         * @throws InstantiationException
085         * @throws IllegalAccessException
086         * @throws IllegalArgumentException
087         * @throws InvocationTargetException
088         * @throws NoSuchMethodException
089         * @throws SecurityException
090         * @throws SQLException
091         * @throws ClassNotFoundException 
092         */
093        public static void registerJdbcDriver(String jdbcDriverClassName, List<String> driverJars) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, SQLException, ClassNotFoundException {
094                if (jdbcDriverClassName == null && driverJars != null) {
095                        ClassLoader           classLoader = ClassLoaderUtils.createClassLoader(driverJars);
096                        ServiceLoader<Driver> loader      = ServiceLoader.load(Driver.class, classLoader);
097                        for (Driver driver : loader)
098                                DriverManager.registerDriver(new DriverShim(driver));
099                } else if (jdbcDriverClassName != null && driverJars != null) {
100                        ClassLoader classLoader = ClassLoaderUtils.createClassLoader(driverJars);
101                        Driver      driver      = (Driver) Class.forName(jdbcDriverClassName, true, classLoader).getDeclaredConstructor().newInstance();
102                        DriverManager.registerDriver(new DriverShim(driver));
103                } else if (jdbcDriverClassName != null && driverJars == null) {
104                        Driver driver = (Driver) Class.forName(jdbcDriverClassName).getDeclaredConstructor().newInstance();
105                        DriverManager.registerDriver(new DriverShim(driver));
106                }
107        }
108
109}
110
111/*
112Please contact FBSQL Team by E-Mail fbsql.team@gmail.com
113or visit https://fbsql.github.io if you need additional
114information or have any questions.
115*/
116
117/* EOF */