/* * RapidMiner * * Copyright (C) 2001-2011 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.connection; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.nio.charset.Charset; import java.security.Key; import java.sql.SQLException; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import com.rapidminer.io.Base64; import com.rapidminer.io.process.XMLTools; import com.rapidminer.tools.FileSystemService; import com.rapidminer.tools.LogService; import com.rapidminer.tools.Tools; import com.rapidminer.tools.XMLException; import com.rapidminer.tools.cipher.CipherException; import com.rapidminer.tools.cipher.CipherTools; import com.rapidminer.tools.cipher.KeyGeneratorTool; import com.rapidminer.tools.jdbc.DatabaseHandler; import com.rapidminer.tools.jdbc.DatabaseService; /** * The central service for registering DatabaseConnections. They are used for * connection selection on all database related operators as well as for the import * wizards. * * @author Tobias Malbrecht, Sebastian Land */ public class DatabaseConnectionService { public static final String PROPERTY_CONNECTIONS_FILE = "connections"; public static final String PROPERTY_CONNECTIONS_FILE_XML = "connections.xml"; private static List<FieldConnectionEntry> connections = new LinkedList<FieldConnectionEntry>(); private static DatabaseHandler handler = null; public static void init() { File connectionsFile = getOldConnectionsFile(); File xmlConnectionsFile = getXMLConnectionsFile(); if (!xmlConnectionsFile.exists() && !connectionsFile.exists()) { // both files do not exist, create the new xml format file try { xmlConnectionsFile.createNewFile(); writeXMLConnectionsEntries(getConnectionEntries(), xmlConnectionsFile); } catch (IOException ex) { // do nothing } } else if (!xmlConnectionsFile.exists() && connectionsFile.exists()) { // only the old text format exists, read it and save as new xml format so next time only the new xml format exists connections = readConnectionEntries(connectionsFile); writeXMLConnectionsEntries(getConnectionEntries(), xmlConnectionsFile); connectionsFile.delete(); } else { try { if (!"".equals(Tools.readTextFile(xmlConnectionsFile))) { Document document = XMLTools.parse(xmlConnectionsFile); Element jdbcElement = document.getDocumentElement(); connections = new LinkedList<FieldConnectionEntry>(parseEntries(jdbcElement)); } } catch (Exception e) { LogService.getRoot().log(Level.WARNING, "Failed to read database connections file: "+e, e); } } } private static File getOldConnectionsFile() { return FileSystemService.getUserConfigFile(PROPERTY_CONNECTIONS_FILE); } private static File getXMLConnectionsFile() { return FileSystemService.getUserConfigFile(PROPERTY_CONNECTIONS_FILE_XML); } public static Collection<FieldConnectionEntry> getConnectionEntries() { return connections; } public static ConnectionEntry getConnectionEntry(String name) { for (ConnectionEntry entry : connections) { if (entry.getName().equals(name)) { return entry; } } return null; } public static void addConnectionEntry(FieldConnectionEntry entry) { connections.add(entry); Collections.sort(connections, ConnectionEntry.COMPARATOR); writeConnectionEntries(connections); } public static void deleteConnectionEntry(ConnectionEntry entry) { connections.remove(entry); if (entry != null) { writeConnectionEntries(connections); } } public static void setConnectionEntries(List<FieldConnectionEntry> entries) { connections = entries; Collections.sort(connections, ConnectionEntry.COMPARATOR); } // public static void renameConnectionEntry(ConnectionEntry entry, String name) { // if (entry != null) { // entry.setName(name); // writeConnectionEntries(connections, connectionsFile); // } // } @Deprecated public static List<FieldConnectionEntry> readConnectionEntries(File connectionEntriesFile) { LinkedList<FieldConnectionEntry> connectionEntries = new LinkedList<FieldConnectionEntry>(); BufferedReader in = null; try { in = new BufferedReader(new FileReader(connectionEntriesFile)); String line = in.readLine(); if (line != null) { int numberOfEntries = Integer.parseInt(line); for (int i = 0; i < numberOfEntries; i++) { String name = in.readLine(); String system = in.readLine(); String host = in.readLine(); String port = in.readLine(); String database = in.readLine(); String user = in.readLine(); String password = CipherTools.decrypt(in.readLine()); if (name != null && system != null) { connectionEntries.add(new FieldConnectionEntry(name, DatabaseService.getJDBCProperties(system), host, port, database, user, password.toCharArray())); } } } in.close(); Collections.sort(connectionEntries, ConnectionEntry.COMPARATOR); } catch (Exception e) { connectionEntries.clear(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { // should not happen } } } return connectionEntries; } public static void writeConnectionEntries(Collection<FieldConnectionEntry> connectionEntries) { File connectionEntriesFile = getXMLConnectionsFile(); writeXMLConnectionsEntries(connectionEntries, connectionEntriesFile); } public static void writeXMLConnectionsEntries(Collection<FieldConnectionEntry> connectionEntries, File connectionEntriesFile) { Key key; try { key = KeyGeneratorTool.getUserKey(); } catch (IOException e) { LogService.getRoot().log(Level.WARNING, "Cannot retrieve key, probably no one was created: "+e, e); return; } try { XMLTools.stream(toXML(connectionEntries, key, null, false), connectionEntriesFile, Charset.forName("UTF-8")); } catch (Exception e) { LogService.getRoot().log(Level.WARNING, "Failed to write database connections file: "+e, e); } } /** * @param replacementForLocalhost The hostname "localhost" will be replaced by this string. Useful if * this is exported to another machine where "localhost" has a different meaning. null=don't replace anything. * */ public static Document toXML(Collection<FieldConnectionEntry> connectionEntries, Key key, String replacementForLocalhost) throws ParserConfigurationException, DOMException, CipherException { return toXML(connectionEntries, key, replacementForLocalhost, true); } public static Document toXML(Collection<FieldConnectionEntry> connectionEntries, Key key, String replacementForLocalhost, boolean includeDynamic) throws ParserConfigurationException, DOMException, CipherException { Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); Element root = doc.createElement("jdbc-entries"); String base64key = Base64.encodeBytes(key.getEncoded()); root.setAttribute("key", base64key); doc.appendChild(root); for (FieldConnectionEntry entry : connectionEntries) { if (!includeDynamic && entry.getRepository() != null) { // do nothing in this case } else { root.appendChild(entry.toXML(doc, key, replacementForLocalhost)); } } return doc; } public static Collection<FieldConnectionEntry> parseEntries(Element entries) throws XMLException, CipherException, IOException { if (!entries.getTagName().equals("jdbc-entries")) { throw new XMLException("Outer tag must be <jdbc-entries>"); } String base64Key = entries.getAttribute("key"); if (base64Key == null) { throw new XMLException("Cipher key attribute missing."); } Key key = KeyGeneratorTool.makeKey(Base64.decode(base64Key)); Collection<FieldConnectionEntry> result = new LinkedList<FieldConnectionEntry>(); NodeList children = entries.getElementsByTagName(FieldConnectionEntry.XML_TAG_NAME); for (int i = 0; i < children.getLength(); i++) { result.add(new FieldConnectionEntry((Element) children.item(i), key)); } return result; } public static boolean testConnection(ConnectionEntry entry) throws SQLException { if (entry != null) { if (handler != null) { handler.disconnect(); } handler = DatabaseHandler.getConnectedDatabaseHandler(entry); if (handler != null) { handler.disconnect(); } return true; } return false; } }