All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.osgijdbc.JDBCDriverLoader Maven / Gradle / Ivy

There is a newer version: 1.0.3
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2009-2011 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.glassfish.osgijdbc;

import com.sun.enterprise.util.SystemPropertyConstants;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;

public class JDBCDriverLoader {

    private static Logger logger = Logger.getLogger(JDBCDriverLoader.class.getPackage().getName());

    private static final String DRIVER_INTERFACE_NAME = "java.sql.Driver";
    private static final String META_INF_SERVICES_DRIVER_FILE = "META-INF/services/java.sql.Driver";
    private static final String DBVENDOR_MAPPINGS_ROOT =
            System.getProperty(SystemPropertyConstants.INSTALL_ROOT_PROPERTY) + File.separator +
                    "lib" + File.separator + "install" + File.separator + "databases" +
                    File.separator + "dbvendormapping" + File.separator;

    private static final String DS_PROPERTIES = "ds.properties";
    private static final String CPDS_PROPERTIES = "cpds.properties";
    private static final String XADS_PROPERTIES = "xads.properties";
    private static final String DRIVER_PROPERTIES = "driver.properties";
    private static final String VENDOR_PROPERTIES = "dbvendor.properties";

    private static final Locale locale = Locale.getDefault();

    public static final Map> dbVendorMappings = new HashMap>();

    static{
        loadMappings();
    }

    private ClassLoader cl;
    
    public JDBCDriverLoader(ClassLoader cl){
        this.cl = cl;
    }

    private static void loadMappings(){
        dbVendorMappings.put(Constants.DS, ((Map)loadProperties(DS_PROPERTIES)));
        dbVendorMappings.put(Constants.CPDS, ((Map)loadProperties(CPDS_PROPERTIES)));
        dbVendorMappings.put(Constants.XADS, ((Map)loadProperties(XADS_PROPERTIES)));
        dbVendorMappings.put(Constants.DRIVER, ((Map)loadProperties(DRIVER_PROPERTIES)));
        dbVendorMappings.put(Constants.DBVENDOR, ((Map)loadProperties(VENDOR_PROPERTIES)));
    }

    private static Properties loadProperties(String type){
        Properties fileProperties = new Properties();
        String fileName = DBVENDOR_MAPPINGS_ROOT + type;
        File mappingFile = new File(fileName);

        if (mappingFile.exists()) {
            try {
                FileInputStream fis = new FileInputStream(mappingFile);
                try {
                    fileProperties.load(fis);
                } finally {
                    fis.close();
                }
            } catch (IOException ioe) {
                logger.fine("IO Exception while loading properties file" +
                        " [ "+mappingFile.getAbsolutePath()+" ] : " +  ioe);
            }
        } else {
            logger.warning("File not found : " + mappingFile.getAbsolutePath());
        }
        return fileProperties;
    }

    /**
     * Get a set of common database vendor names supported in GlassFish.
     * @return database vendor names set.
     */
    private Set getDatabaseVendorNames() {
        Map vendors = dbVendorMappings.get(Constants.DBVENDOR);
        return vendors.keySet();
    }

    private String getDBVendor(String className, String type) {
        String dbVendor = null;
        Map map = dbVendorMappings.get(type);
        Set entrySet = map.entrySet();
        Iterator entryIterator = entrySet.iterator();
        while(entryIterator.hasNext()){
            Map.Entry entry = (Map.Entry)entryIterator.next();
            if(((String)entry.getValue()).equalsIgnoreCase(className)){
                dbVendor = (String)entry.getKey();
                break;
            }
        }
        return dbVendor;
    }

    private String getImplClassNameFromMapping(String dbVendor, String resType) {
        Map fileProperties = dbVendorMappings.get(resType);
        if(fileProperties == null){
            throw new IllegalStateException("Unknown resource type [ " + resType + " ]");
        }
        return (String)fileProperties.get(dbVendor.toUpperCase(locale));
    }


    public Properties loadDriverInformation(File f) {
        String implClass;
        JarFile jarFile = null;

        Properties properties = new Properties();
        try {
            jarFile = new JarFile(f);
            Enumeration e = jarFile.entries();
            while (e.hasMoreElements()) {

                ZipEntry zipEntry = (ZipEntry) e.nextElement();

                if (zipEntry != null) {

                    String entry = zipEntry.getName();
                    if (META_INF_SERVICES_DRIVER_FILE.equals(entry)) {

                        InputStream metaInf = jarFile.getInputStream(zipEntry);
                        implClass = processMetaInfServicesDriverFile(metaInf);
                        if (implClass != null) {
                            if (isLoaded(implClass, Constants.DRIVER, cl)) {
                                String vendor = getVendorFromManifest(f);

                                Set dbVendorNames = getDatabaseVendorNames();
                                if (vendor != null && dbVendorNames.contains(vendor)) {

                                    String dsClassName = getImplClassNameFromMapping(vendor, Constants.DS);
                                    String cpdsClassName = getImplClassNameFromMapping(vendor, Constants.CPDS);
                                    String xadsClassName = getImplClassNameFromMapping(vendor, Constants.XADS);
                                    //String driverClassName = getImplClassNameFromMapping(vendor,DRIVER);
                                    String driverClassName = implClass;

                                    properties.put(Constants.DS, dsClassName);
                                    properties.put(Constants.CPDS, cpdsClassName);
                                    properties.put(Constants.XADS, xadsClassName);
                                    properties.put(Constants.DRIVER, driverClassName);

                                    return properties;

                                } else if (vendor != null) {

                                    Set dsClasses = getImplClassesByIteration(f, Constants.DS, vendor, cl);
                                    if (dsClasses.size() == 1) {
                                        properties.put(Constants.DS, dsClasses.toArray()[0]);
                                    }

                                    Set cpdsClasses = getImplClassesByIteration(f, Constants.CPDS, vendor, cl);
                                    if (cpdsClasses.size() == 1) {
                                        properties.put(Constants.CPDS, cpdsClasses.toArray()[0]);
                                    }

                                    Set xadsClasses = getImplClassesByIteration(f, Constants.XADS, vendor, cl);
                                    if (xadsClasses.size() == 1) {
                                        properties.put(Constants.XADS, xadsClasses.toArray()[0]);
                                    }
                                    properties.put(Constants.DRIVER, implClass);

                                    return properties;
                                }
                            }
                        }
                        logger.finest("Driver loader : implClass = " + implClass);

                    }
                    if (entry.endsWith(".class")) {
                        //Read from MANIFEST.MF file for all jdbc40 drivers and resType
                        //java.sql.Driver.TODO : this should go outside .class check.
                        //TODO : Some classnames might not have these strings
                        //in their classname. Logic should be flexible in such cases.
                        if (entry.toUpperCase(locale).contains("DATASOURCE")) {
                            implClass = getClassName(entry);
                            if (implClass != null) {
                                if (isLoaded(implClass, Constants.XADS, cl)) {
                                    String dbVendor = getDBVendor(implClass, Constants.XADS);
                                    if (dbVendor != null) {
                                        detectImplClasses(properties, dbVendor);
                                        return properties;
                                    } else {
                                        properties.put(Constants.XADS, implClass);
                                    }
                                } else if (isLoaded(implClass, Constants.CPDS, cl)) {
                                    String dbVendor = getDBVendor(implClass, Constants.CPDS);
                                    if (dbVendor != null) {
                                        detectImplClasses(properties, dbVendor);
                                        return properties;
                                    } else {
                                        properties.put(Constants.CPDS, implClass);
                                    }
                                } else if (isLoaded(implClass, Constants.DS, cl)) {
                                    String dbVendor = getDBVendor(implClass, Constants.DS);
                                    if (dbVendor != null) {
                                        detectImplClasses(properties, dbVendor);
                                        return properties;
                                    } else {
                                        properties.put(Constants.DS, implClass);
                                    }
                                }
                            }
                        } else if (entry.toUpperCase(locale).contains("DRIVER")) {
                            implClass = getClassName(entry);
                            if (implClass != null) {
                                if (isLoaded(implClass, Constants.DRIVER, cl)) {
                                    String dbVendor = getDBVendor(implClass, Constants.DRIVER);
                                    if (dbVendor != null) {
                                        detectImplClasses(properties, dbVendor);
                                        return properties;
                                    } else {
                                        properties.put(Constants.DRIVER, implClass);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (IOException ex) {
            logger.log(Level.WARNING, "Error while getting Jdbc driver classnames ", ex);
        } finally {
            if (jarFile != null) {
                try {
                    jarFile.close();
                } catch (IOException ex) {
                    logger.log(Level.FINE, "Exception while closing JarFile '"
                            + jarFile.getName() + "' :", ex);
                }
            }
        }
        if (properties.get(Constants.DRIVER) != null) {
            return properties;
        } else {
            throw new RuntimeException("Unable to introspect jar [ "+f.getName()+" ] for Driver Class, " +
                    "no implementation for java.sql.Driver is found");
        }
    }

    private void detectImplClasses(Properties properties, String dbVendor) {
        String xads = getImplClassNameFromMapping(dbVendor, Constants.XADS);
        String cpds = getImplClassNameFromMapping(dbVendor, Constants.CPDS);
        String ds = getImplClassNameFromMapping(dbVendor, Constants.DS);
        String driver = getImplClassNameFromMapping(dbVendor, Constants.DRIVER);

        properties.put(Constants.XADS, xads);
        properties.put(Constants.CPDS, cpds);
        properties.put(Constants.DS, ds);
        properties.put(Constants.DRIVER, driver);
    }


    private Set getImplClassesByIteration(File f, String resType, String dbVendor, ClassLoader cl) {
        SortedSet implClassNames = new TreeSet();
        String implClass = null;
        JarFile jarFile = null;
        try {
            jarFile = new JarFile(f);
            Enumeration e = jarFile.entries();
            while (e.hasMoreElements()) {

                ZipEntry zipEntry = (ZipEntry) e.nextElement();

                if (zipEntry != null) {

                    String entry = zipEntry.getName();
                    if (DRIVER_INTERFACE_NAME.equals(resType)) {
                        if (META_INF_SERVICES_DRIVER_FILE.equals(entry)) {

                            InputStream inputStream = jarFile.getInputStream(zipEntry);
                            implClass = processMetaInfServicesDriverFile(inputStream);
                            if (implClass != null) {
                                if (isLoaded(implClass, resType, cl)) {
                                    //Add to the implClassNames only if vendor name matches.
                                    if (isVendorSpecific(f, dbVendor, implClass)) {
                                        implClassNames.add(implClass);
                                    }
                                }
                            }
                            logger.finest("Driver loader : implClass = " + implClass);

                        }
                    }
                    if (entry.endsWith(".class")) {
                        //Read from metainf file for all jdbc40 drivers and resType
                        //java.sql.Driver.TODO : this should go outside .class check.
                        //TODO : Some classnames might not have these strings
                        //in their classname. Logic should be flexible in such cases.
                        if (entry.toUpperCase(locale).indexOf("DATASOURCE") != -1 ||
                                entry.toUpperCase(locale).indexOf("DRIVER") != -1) {
                            implClass = getClassName(entry);
                            if (implClass != null) {
                                if (isLoaded(implClass, resType, cl)) {
                                    if (isVendorSpecific(f, dbVendor, implClass)) {
                                        implClassNames.add(implClass);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (IOException ex) {
            logger.log(Level.WARNING, "Error while getting Jdbc driver classnames ", ex);
        } finally {
            if (jarFile != null) {
                try {
                    jarFile.close();
                } catch (IOException ex) {
                    logger.log(Level.FINE, "Exception while closing JarFile '"
                            + jarFile.getName() + "' :", ex);
                }
            }
        }
        //Could be one or many depending on the connection definition class name 
        return implClassNames;
    }

    private boolean isNotAbstract(Class cls) {
        int modifier = cls.getModifiers();
        return !Modifier.isAbstract(modifier);
    }

    /**
     * Reads the META-INF/services/java.sql.Driver file contents and returns
     * the driver implementation class name.
     * In case of JDBC-4.0 drivers or above, the META-INF/services/java.sql.Driver file
     * contains the name of the driver class.
     *
     * @param inputStream
     * @return driver implementation class name
     */
    private String processMetaInfServicesDriverFile(InputStream inputStream) {
        String driverClassName = null;
        InputStreamReader reader = null;
        BufferedReader bufferedReader = null;
        try {
            reader = new InputStreamReader(inputStream);
            bufferedReader = new BufferedReader(reader);
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                driverClassName = line;
            }
        } catch (IOException ioex) {
            logger.finest("DriverLoader : exception while processing " +
                    "META-INF directory for DriverClassName " + ioex);
        } finally {
            try {
                if (bufferedReader != null)
                    bufferedReader.close();
            } catch (IOException ex) {
                logger.log(Level.FINE, "Error while closing file handle after reading META-INF file : ", ex);
            }
            try {
                if (reader != null)
                    reader.close();
            } catch (IOException ex) {
                logger.log(Level.FINE, "Error while closing file handle after reading META-INF file : ", ex);
            }
        }
        return driverClassName;
    }

    /**
     * Check if the class has been loaded and if it is a Driver or a
     * DataSource impl.
     *
     * @param classname class
     * @return boolean indicating whether the class is loaded or not
     */
    private boolean isLoaded(String classname, String resType, ClassLoader loader) {
        Class cls = null;
        try {
            cls = loader.loadClass(classname);
        } catch (Throwable t) {
            cls = null;
        }
        return (isResType(cls, resType));
    }

    /**
     * Find if the particular class has any implementations of java.sql.Driver or
     * javax.sql.DataSource or any other resTypes passed.
     *
     * @param cls     class to be introspected
     * @param resType resource-type
     * @return boolean indicating the status
     */
    private boolean isResType(Class cls, String resType) {
        boolean isResType = false;
        if (cls != null) {
            if (Constants.DS.equals(resType)) {
                if (javax.sql.DataSource.class.isAssignableFrom(cls)) {
                    isResType = isNotAbstract(cls);
                }
            } else if (Constants.CPDS.equals(resType)) {
                if (javax.sql.ConnectionPoolDataSource.class.isAssignableFrom(cls)) {
                    isResType = isNotAbstract(cls);
                }
            } else if (Constants.XADS.equals(resType)) {
                if (javax.sql.XADataSource.class.isAssignableFrom(cls)) {
                    isResType = isNotAbstract(cls);
                }
            } else if (Constants.DRIVER.equals(resType)) {
                if (java.sql.Driver.class.isAssignableFrom(cls)) {
                    isResType = isNotAbstract(cls);
                }
            }
        }
        return isResType;
    }


    private String getClassName(String classname) {
        classname = classname.replaceAll("/", ".");
        classname = classname.substring(0, classname.lastIndexOf(".class"));
        return classname;
    }

    private boolean isVendorSpecific(File f, String dbVendor, String className) {
        //File could be a jdbc jar file or a normal jar file
        boolean isVendorSpecific = false;
        String vendor = getVendorFromManifest(f);

        if (vendor == null) {
            //might have to do this part by going through the class names or some other method.
            //dbVendor might be used in this portion
            if (isVendorSpecific(dbVendor, className)) {
                isVendorSpecific = true;
            }
        } else {
            //Got from Manifest file.
            if (vendor.equalsIgnoreCase(dbVendor) ||
                    vendor.toUpperCase(locale).indexOf(dbVendor.toUpperCase(locale)) != -1) {
                isVendorSpecific = true;
            }
        }
        return isVendorSpecific;
    }

    /**
     * Utility method that checks if a classname is vendor specific.
     * This method is used for jar files that do not have a manifest file to
     * look up the classname.
     *
     * @param dbVendor  DB vendor name
     * @param className class name
     * @return true if the className in question is vendor specific.
     */
    private boolean isVendorSpecific(String dbVendor, String className) {
        return className.toUpperCase(locale).indexOf(dbVendor.toUpperCase(locale)) != -1;
    }

    /**
     * Get a vendor name from a Manifest entry in the file.
     *
     * @param f file
     * @return null if no manifest entry found.
     */
    private String getVendorFromManifest(File f) {
        String vendor = null;
        JarFile jarFile = null;
        try {
            jarFile = new JarFile(f);
            Manifest manifest = jarFile.getManifest();
            if (manifest != null) {
                Attributes mainAttributes = manifest.getMainAttributes();
                if (mainAttributes != null) {
                    vendor = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_VENDOR.toString());
                    if (vendor == null) {
                        vendor = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_VENDOR_ID.toString());
                    }
                }
            }
        } catch (IOException ex) {
            logger.log(Level.WARNING, "Exception while reading manifest file : ", ex);
        } finally {
            if (jarFile != null) {
                try {
                    jarFile.close();
                } catch (IOException ex) {
                    logger.log(Level.FINE, "Exception while closing JarFile '"
                            + jarFile.getName() + "' :", ex);
                }
            }
        }
        return vendor;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy