/* * RapidMiner * * Copyright (C) 2001-2008 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.tools.jdbc; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.jar.JarFile; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import com.rapidminer.tools.LogService; import com.rapidminer.tools.ParameterService; import com.rapidminer.tools.Tools; /** * This service class dynamically registers (additional) JDBC drivers. Please note that drivers * cannot be created by Class.forName() but will just be instantiated automatically via * DriverManager.getConnection(...). * * @author Ingo Mierswa * @version $Id: DatabaseService.java,v 1.9 2008/08/15 19:15:00 ingomierswa Exp $ * */ public class DatabaseService { private static List<JDBCProperties> jdbcProperties = new ArrayList<JDBCProperties>(); public static void init(File jdbcDir, boolean searchForJDBDriversInLibDirectory, boolean searchForJDBCDriversInClasspath) { registerAllJDBCDrivers(jdbcDir, searchForJDBDriversInLibDirectory, searchForJDBCDriversInClasspath); // then try properties from the etc directory if available File etcPropertyFile = ParameterService.getConfigFile("jdbc_properties.xml"); if ((etcPropertyFile != null) && (etcPropertyFile.exists())) { InputStream in = null; try { in = new FileInputStream(etcPropertyFile); loadJDBCProperties(in, "etc:jdbc_properties.xml"); } catch (IOException e) { LogService.getGlobal().logError("Cannot load JDBC properties from etc directory."); } finally { if (in != null) { try { in.close(); } catch (IOException e) { LogService.getGlobal().logError("Cannot close connection for JDBC properties file in the etc directory."); } } } } else { // use the delivered default properties in the resources (e.g. in the jar file) URL propertyURL = Tools.getResource("jdbc_properties.xml"); if (propertyURL != null) { InputStream in = null; try { in = propertyURL.openStream(); loadJDBCProperties(in, "resources:jdbc_properties.xml"); } catch (IOException e) { LogService.getGlobal().logError("Cannot load JDBC properties from program resources."); } finally { if (in != null) { try { in.close(); } catch (IOException e) { LogService.getGlobal().logError("Cannot close connection for JDBC properties file in the resources."); } } } } } } private static void registerAllJDBCDrivers(File jdbcDir, boolean searchForJDBDriversInLibDirectory, boolean searchForJDBCDriversInClasspath) { if (searchForJDBDriversInLibDirectory) { File jdbcDirectory = jdbcDir; if (jdbcDirectory == null) { jdbcDirectory = ParameterService.getLibraryFile("jdbc"); } if ((jdbcDirectory != null) && (jdbcDirectory.exists())) { File[] allFiles = jdbcDirectory.listFiles(); if (allFiles != null) { for (File f : allFiles) { if ((f.getName().endsWith(".jar")) || ((f.getName().endsWith(".zip")))) { registerDynamicJDBCDrivers(f); } } } } } if (searchForJDBCDriversInClasspath) { String classpath = System.getProperty("java.class.path"); String pathComponents[] = classpath.split(File.pathSeparator); for (int i = 0; i < pathComponents.length; i++) { String path = pathComponents[i].trim(); if ((path.endsWith(".jar")) || ((path.endsWith(".zip")))) { registerClasspathJDBCDrivers(new File(path).getAbsoluteFile()); } } } } private static void registerDynamicJDBCDrivers(File file) { URLClassLoader ucl = null; try { URL u = new URL("jar:file:" + file.getAbsolutePath() + "!/"); ucl = new URLClassLoader(new URL[] { u }); } catch (MalformedURLException e) { LogService.getGlobal().log("DatabaseService: cannot create class loader for file '" + file + "': " + e.getMessage(), LogService.ERROR); } if (ucl != null) { try { JarFile jarFile = new JarFile(file); List<String> driverNames = new LinkedList<String>(); Tools.findImplementationsInJar(ucl, jarFile, java.sql.Driver.class, driverNames); Iterator<String> i = driverNames.iterator(); while (i.hasNext()) { registerDynamicJDBCDriver(ucl, i.next()); } } catch (Exception e) { LogService.getGlobal().log("DatabaseService: cannot register drivers for file '" + file + "': " + e.getMessage(), LogService.ERROR); } } } private static void registerDynamicJDBCDriver(URLClassLoader ucl, String driverName) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException { if (!driverName.equals(DriverAdapter.class.getName())) { Driver d = (Driver)Class.forName(driverName, true, ucl).newInstance(); DriverManager.registerDriver(new DriverAdapter(d)); } } private static void registerClasspathJDBCDrivers(File file) { JarFile jarFile = null; try { jarFile = new JarFile(file); } catch (IOException e) { LogService.getGlobal().log("DatabaseService: cannot register drivers from file '" + file + "': " + e.getMessage(), LogService.ERROR); return; } List<String> driverNames = new LinkedList<String>(); Tools.findImplementationsInJar(jarFile, java.sql.Driver.class, driverNames); Iterator<String> i = driverNames.iterator(); while (i.hasNext()) { String driverName = i.next(); try { Class.forName(driverName); } catch (Exception e) { LogService.getGlobal().log("DatabaseService: cannot register driver '"+driverName+"' from file '" + file + "': " + e.getMessage(), LogService.ERROR); } } } private static void loadJDBCProperties(InputStream in, String name) { jdbcProperties.clear(); LogService.getGlobal().log("Loading JDBC driver information from '" + name + "'.", LogService.INIT); Document document = null; try { document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in); } catch (Exception e) { LogService.getGlobal().log("Cannot read JDBC driver description file '" + name + "': no valid XML: " + e.getMessage(), LogService.ERROR); } if (document != null) { if (!document.getDocumentElement().getTagName().toLowerCase().equals("drivers")) { LogService.getGlobal().log("JDBC driver description file '" + name + "': outermost tag must be <drivers>!", LogService.ERROR); return; } NodeList driverTags = document.getDocumentElement().getElementsByTagName("driver"); for (int i = 0; i < driverTags.getLength(); i++) { Element currentElement = (Element) driverTags.item(i); try { addDriverInformation(currentElement); } catch (Exception e) { Attr currentNameAttr = currentElement.getAttributeNode("name"); if (currentNameAttr != null) LogService.getGlobal().log("JDBC driver description: cannot register '" + currentNameAttr.getValue() + "': " + e, LogService.ERROR); else LogService.getGlobal().log("JDBC driver registration: cannot register '" + currentElement + "': " + e, LogService.ERROR); } } } } private static void addDriverInformation(Element driverElement) throws Exception { Attr nameAttr = driverElement.getAttributeNode("name"); Attr portAttr = driverElement.getAttributeNode("defaultport"); Attr urlAttr = driverElement.getAttributeNode("urlprefix"); Attr dbNameAttr = driverElement.getAttributeNode("dbnameseperator"); Attr varcharNameAttr = driverElement.getAttributeNode("type_varchar"); Attr integerNameAttr = driverElement.getAttributeNode("type_integer"); Attr realNameAttr = driverElement.getAttributeNode("type_real"); Attr quoteOpenAttr = driverElement.getAttributeNode("identifier_quote_open"); Attr quoteCloseAttr = driverElement.getAttributeNode("identifier_quote_close"); if (nameAttr == null) throw new Exception("Missing name for <driver> tag"); if (portAttr == null) throw new Exception("Missing defaultport for <driver> tag"); if (urlAttr == null) throw new Exception("Missing urlprefix for <driver> tag"); if (dbNameAttr == null) throw new Exception("Missing dbnameseperator for <driver> tag"); String varcharString = "VARCHAR"; if (varcharNameAttr != null) { varcharString = varcharNameAttr.getValue(); } else { LogService.getGlobal().logWarning("No definition of 'type_varchar' found for driver " + nameAttr.getValue() + ", using default (VARCHAR)..."); } String integerString = "INTEGER"; if (integerNameAttr != null) { integerString = integerNameAttr.getValue(); } else { LogService.getGlobal().logWarning("No definition of 'type_integer' found for driver " + nameAttr.getValue() + ", using default (INTEGER)..."); } String realString = "REAL"; if (realNameAttr != null) { realString = realNameAttr.getValue(); } else { LogService.getGlobal().logWarning("No definition of 'type_real' found for driver " + nameAttr.getValue() + ", using default (REAL)..."); } String quoteOpenString = "\""; if (quoteOpenAttr != null) { quoteOpenString = quoteOpenAttr.getValue(); } else { LogService.getGlobal().logWarning("No definition of 'identifier_quote_open' found for driver " + nameAttr.getValue() + ", using default (\")..."); } String quoteCloseString = "\""; if (quoteCloseAttr != null) { quoteCloseString = quoteCloseAttr.getValue(); } else { LogService.getGlobal().logWarning("No definition of 'identifier_quote_close' found for driver " + nameAttr.getValue() + ", using default (\")..."); } JDBCProperties properties = new JDBCProperties(nameAttr.getValue(), portAttr.getValue(), urlAttr.getValue(), dbNameAttr.getValue(), varcharString, integerString, realString, quoteOpenString, quoteCloseString); jdbcProperties.add(properties); } public static Enumeration<Driver> getAllDrivers() { return DriverManager.getDrivers(); } public static DriverInfo[] getAllDriverInfos() { List<DriverInfo> predefinedDriverList = new LinkedList<DriverInfo>(); for (JDBCProperties properties : getJDBCProperties()) { Enumeration<Driver> drivers = getAllDrivers(); boolean accepted = false; while (drivers.hasMoreElements()) { Driver driver = drivers.nextElement(); try { if (driver.acceptsURL(properties.getUrlPrefix())) { DriverInfo info = new DriverInfo(driver); info.setShortName(properties.getName()); predefinedDriverList.add(info); accepted = true; break; } } catch (SQLException e) { // do nothing } } if (!accepted) { predefinedDriverList.add(new DriverInfo(properties.getName())); } } List<DriverInfo> driverList = new LinkedList<DriverInfo>(); Enumeration<Driver> drivers = getAllDrivers(); while (drivers.hasMoreElements()) { Driver driver = drivers.nextElement(); boolean accepted = true; for (DriverInfo predefinedInfo : predefinedDriverList) { if ((predefinedInfo.getDriver() != null) && (predefinedInfo.getDriver().equals(driver))) { accepted = false; break; } } if (accepted) { DriverInfo info = new DriverInfo(driver); if ((!info.getShortName().startsWith("NonRegistering")) && (!info.getShortName().startsWith("Replication"))) { driverList.add(new DriverInfo(driver)); } } } driverList.addAll(predefinedDriverList); Collections.sort(driverList); DriverInfo[] driverArray = new DriverInfo[driverList.size()]; driverList.toArray(driverArray); return driverArray; } public static JDBCProperties getJDBCProperties(String name) { for (JDBCProperties p : jdbcProperties) { if (p.getName().equals(name)) { return p; } } return null; } public static List<JDBCProperties> getJDBCProperties() { return jdbcProperties; } public static String[] getDBSystemNames() { String[] names = new String[jdbcProperties.size()]; int counter = 0; Iterator<JDBCProperties> i = jdbcProperties.iterator(); while (i.hasNext()) { names[counter++] = i.next().getName(); } return names; } }