jdbc-dk-driver: first working version v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sun, 17 May 2015 00:27:56 +0200
branchv_0
changeset 192 a32bfcbdee51
parent 191 862d0a8747ac
child 193 5a18a6adf7f9
jdbc-dk-driver: first working version
java/jdbc-dk-driver/conf/META-INF/services/java.sql.Driver
java/jdbc-dk-driver/nbproject/build-impl.xml
java/jdbc-dk-driver/nbproject/genfiles.properties
java/jdbc-dk-driver/nbproject/project.properties
java/jdbc-dk-driver/nbproject/project.xml
java/jdbc-dk-driver/src/info/globalcode/sql/dk/jdbc/Driver.java
java/sql-dk/src/info/globalcode/sql/dk/DatabaseConnection.java
java/sql-dk/src/info/globalcode/sql/dk/configuration/Loader.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/jdbc-dk-driver/conf/META-INF/services/java.sql.Driver	Sun May 17 00:27:56 2015 +0200
@@ -0,0 +1,1 @@
+info.globalcode.sql.dk.jdbc.Driver
--- a/java/jdbc-dk-driver/nbproject/build-impl.xml	Sat May 16 23:58:06 2015 +0200
+++ b/java/jdbc-dk-driver/nbproject/build-impl.xml	Sun May 17 00:27:56 2015 +0200
@@ -127,6 +127,7 @@
         </condition>
         <condition property="have.sources">
             <or>
+                <available file="${src.conf.dir}"/>
                 <available file="${src.dir}"/>
                 <available file="${src.sql-dk.dir}"/>
             </or>
@@ -224,6 +225,7 @@
         <!-- You can override this target in the ../build.xml file. -->
     </target>
     <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
+        <fail unless="src.conf.dir">Must set src.conf.dir</fail>
         <fail unless="src.dir">Must set src.dir</fail>
         <fail unless="src.sql-dk.dir">Must set src.sql-dk.dir</fail>
         <fail unless="test.src.dir">Must set test.src.dir</fail>
@@ -247,7 +249,7 @@
     </target>
     <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-macrodef-javac-with-processors">
         <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
-            <attribute default="${src.dir}:${src.sql-dk.dir}" name="srcdir"/>
+            <attribute default="${src.conf.dir}:${src.dir}:${src.sql-dk.dir}" name="srcdir"/>
             <attribute default="${build.classes.dir}" name="destdir"/>
             <attribute default="${javac.classpath}" name="classpath"/>
             <attribute default="${javac.processorpath}" name="processorpath"/>
@@ -288,7 +290,7 @@
     </target>
     <target depends="-init-ap-cmdline-properties" name="-init-macrodef-javac-without-processors" unless="ap.supported.internal">
         <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
-            <attribute default="${src.dir}:${src.sql-dk.dir}" name="srcdir"/>
+            <attribute default="${src.conf.dir}:${src.dir}:${src.sql-dk.dir}" name="srcdir"/>
             <attribute default="${build.classes.dir}" name="destdir"/>
             <attribute default="${javac.classpath}" name="classpath"/>
             <attribute default="${javac.processorpath}" name="processorpath"/>
@@ -321,7 +323,7 @@
     </target>
     <target depends="-init-macrodef-javac-with-processors,-init-macrodef-javac-without-processors" name="-init-macrodef-javac">
         <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
-            <attribute default="${src.dir}:${src.sql-dk.dir}" name="srcdir"/>
+            <attribute default="${src.conf.dir}:${src.dir}:${src.sql-dk.dir}" name="srcdir"/>
             <attribute default="${build.classes.dir}" name="destdir"/>
             <attribute default="${javac.classpath}" name="classpath"/>
             <sequential>
@@ -919,11 +921,12 @@
                 <include name="*"/>
             </dirset>
         </pathconvert>
-        <j2seproject3:depend srcdir="${src.dir}:${src.sql-dk.dir}:${build.generated.subdirs}"/>
+        <j2seproject3:depend srcdir="${src.conf.dir}:${src.dir}:${src.sql-dk.dir}:${build.generated.subdirs}"/>
     </target>
     <target depends="init,deps-jar,-pre-pre-compile,-pre-compile, -copy-persistence-xml,-compile-depend" if="have.sources" name="-do-compile">
         <j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/>
         <copy todir="${build.classes.dir}">
+            <fileset dir="${src.conf.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
             <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
             <fileset dir="${src.sql-dk.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
         </copy>
@@ -946,7 +949,7 @@
     <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
         <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
         <j2seproject3:force-recompile/>
-        <j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.dir}:${src.sql-dk.dir}"/>
+        <j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.conf.dir}:${src.dir}:${src.sql-dk.dir}"/>
     </target>
     <target name="-post-compile-single">
         <!-- Empty placeholder for easier customization. -->
@@ -1212,6 +1215,9 @@
             <classpath>
                 <path path="${javac.classpath}"/>
             </classpath>
+            <fileset dir="${src.conf.dir}" excludes="${bug5101868workaround},${excludes}" includes="${includes}">
+                <filename name="**/*.java"/>
+            </fileset>
             <fileset dir="${src.dir}" excludes="${bug5101868workaround},${excludes}" includes="${includes}">
                 <filename name="**/*.java"/>
             </fileset>
@@ -1225,6 +1231,9 @@
             <arg line="${javadoc.endorsed.classpath.cmd.line.arg}"/>
         </javadoc>
         <copy todir="${dist.javadoc.dir}">
+            <fileset dir="${src.conf.dir}" excludes="${excludes}" includes="${includes}">
+                <filename name="**/doc-files/**"/>
+            </fileset>
             <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
                 <filename name="**/doc-files/**"/>
             </fileset>
--- a/java/jdbc-dk-driver/nbproject/genfiles.properties	Sat May 16 23:58:06 2015 +0200
+++ b/java/jdbc-dk-driver/nbproject/genfiles.properties	Sun May 17 00:27:56 2015 +0200
@@ -1,8 +1,8 @@
-build.xml.data.CRC32=64e20838
+build.xml.data.CRC32=50d83c90
 build.xml.script.CRC32=3b53b17c
 build.xml.stylesheet.CRC32=8064a381@1.75.2.48
 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
 # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
-nbproject/build-impl.xml.data.CRC32=64e20838
-nbproject/build-impl.xml.script.CRC32=01f7bc2f
+nbproject/build-impl.xml.data.CRC32=50d83c90
+nbproject/build-impl.xml.script.CRC32=0d479eb1
 nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48
--- a/java/jdbc-dk-driver/nbproject/project.properties	Sat May 16 23:58:06 2015 +0200
+++ b/java/jdbc-dk-driver/nbproject/project.properties	Sun May 17 00:27:56 2015 +0200
@@ -37,8 +37,8 @@
 javac.deprecation=false
 javac.processorpath=\
     ${javac.classpath}
-javac.source=1.8
-javac.target=1.8
+javac.source=1.7
+javac.target=1.7
 javac.test.classpath=\
     ${javac.classpath}:\
     ${build.classes.dir}
@@ -69,6 +69,7 @@
     ${javac.test.classpath}:\
     ${build.test.classes.dir}
 source.encoding=UTF-8
+src.conf.dir=conf
 src.dir=src
 src.sql-dk.dir=libs/sql-dk
 test.src.dir=test
--- a/java/jdbc-dk-driver/nbproject/project.xml	Sat May 16 23:58:06 2015 +0200
+++ b/java/jdbc-dk-driver/nbproject/project.xml	Sun May 17 00:27:56 2015 +0200
@@ -5,6 +5,7 @@
         <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
             <name>jdbc-dk-driver</name>
             <source-roots>
+                <root id="src.conf.dir" name="Config"/>
                 <root id="src.dir"/>
                 <root id="src.sql-dk.dir"/>
             </source-roots>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/jdbc-dk-driver/src/info/globalcode/sql/dk/jdbc/Driver.java	Sun May 17 00:27:56 2015 +0200
@@ -0,0 +1,156 @@
+/**
+ * SQL-DK
+ * Copyright © 2015 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.jdbc;
+
+import info.globalcode.sql.dk.Constants;
+import info.globalcode.sql.dk.configuration.Configuration;
+import info.globalcode.sql.dk.configuration.ConfigurationException;
+import info.globalcode.sql.dk.configuration.DatabaseDefinition;
+import info.globalcode.sql.dk.configuration.Loader;
+import info.globalcode.sql.dk.configuration.Properties;
+import info.globalcode.sql.dk.configuration.Property;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.DriverPropertyInfo;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>
+ * Meta JDBC driver that works as redirector/router. It loads SQL-DK's configuration file and
+ * instantiates a real JDBC connection (PostgreSQL, MariDB etc.) of given name.
+ * </p>
+ *
+ * <p>
+ * Raison d'être: user can define his/her database connections just once (in SQL-DK's configuration)
+ * and use them in many other programs – there is no need to define the connection (hostname,
+ * username, password, properties…) in each application again and again. User will simply connect to
+ * e.g. <code>jdbc:sql-dk://my-connection</code> and this driver reads parameters of
+ * <code>my-connection</code> from SQL-DK's configuration and returns real driver implementation.
+ * </p>
+ *
+ * <p>
+ * Of course, the real JDBC driver for given database is still needed on the class path.
+ * </p>
+ *
+ * <p>
+ * TODO: current version is quite heavy-weight, because it includes whole SQL-DK source tree. Some
+ * refactoring and separation is desired to provide more light-weight JDBC driver. Although the
+ * public interface and behavior of this driver should remain same.
+ * </p>
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class Driver implements java.sql.Driver {
+
+	private static final Logger log = Logger.getLogger(Driver.class.getName());
+
+	private final Loader loader = new Loader();
+
+	static {
+		try {
+			DriverManager.registerDriver(new Driver());
+		} catch (SQLException e) {
+			log.log(Level.SEVERE, "Unable to register JDBC driver", e);
+		}
+	}
+
+	@Override
+	public Connection connect(String url, java.util.Properties info) throws SQLException {
+		if (acceptsURL(url)) {
+			log.log(Level.FINER, "Loading SQL-DK configuration for URL: {0}", url);
+			String name = extractDatabaseName(url);
+			log.log(Level.FINE, "Loading SQL-DK configuration for name: {0}", name);
+
+			try {
+				return getConnection(name, info);
+			} catch (ConfigurationException e) {
+				log.log(Level.SEVERE, "Unable to load SQL-DK configuration for name: {0}. Is it defined in {1}?", new Object[]{name, Constants.CONFIG_FILE});
+				throw new SQLException("Unable to load SQL-DK configuration for name: " + name, e);
+			}
+		} else {
+			throw new SQLException("Unsupported URL: " + url);
+		}
+	}
+
+	private Connection getConnection(String connectionName, java.util.Properties info) throws SQLException, ConfigurationException {
+		Configuration c = loader.loadConfiguration();
+		DatabaseDefinition dd = c.getDatabase(connectionName);
+
+		if (acceptsURL(dd.getUrl())) {
+			log.log(Level.SEVERE, "SQL-DK meta JDBC driver loops to itself: {0} → {1} Please check {2}", new Object[]{connectionName, dd.getUrl(), Constants.CONFIG_FILE});
+			throw new ConfigurationException("SQL-DK meta JDBC driver loops to itself.");
+		} else {
+			return Loader.jdbcConnect(dd, translate(info));
+		}
+	}
+
+	private String extractDatabaseName(String url) {
+		return url.split("//", 2)[1];
+	}
+
+	/**
+	 * TODO: refactor/move, reuse
+	 *
+	 * @param info
+	 * @return
+	 */
+	private Properties translate(java.util.Properties info) {
+		Properties properties = new Properties();
+
+		for (String name : info.stringPropertyNames()) {
+			String value = info.getProperty(name);
+			properties.add(new Property(name, value));
+		}
+
+		return properties;
+	}
+
+	@Override
+	public boolean acceptsURL(String url) throws SQLException {
+		return url != null && url.startsWith("jdbc:sql-dk://");
+	}
+
+	@Override
+	public DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException {
+		return new DriverPropertyInfo[0];
+	}
+
+	@Override
+	public int getMajorVersion() {
+		return 0;
+	}
+
+	@Override
+	public int getMinorVersion() {
+		return 1;
+	}
+
+	@Override
+	public boolean jdbcCompliant() {
+		return false;
+	}
+
+	@Override
+	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+		throw new SQLFeatureNotSupportedException("Not supported yet.");
+	}
+
+}
--- a/java/sql-dk/src/info/globalcode/sql/dk/DatabaseConnection.java	Sat May 16 23:58:06 2015 +0200
+++ b/java/sql-dk/src/info/globalcode/sql/dk/DatabaseConnection.java	Sun May 17 00:27:56 2015 +0200
@@ -22,14 +22,13 @@
 import info.globalcode.sql.dk.batch.Batch;
 import info.globalcode.sql.dk.batch.BatchException;
 import info.globalcode.sql.dk.configuration.DatabaseDefinition;
+import info.globalcode.sql.dk.configuration.Loader;
 import info.globalcode.sql.dk.configuration.Properties;
-import info.globalcode.sql.dk.configuration.Property;
 import info.globalcode.sql.dk.formatting.ColumnsHeader;
 import info.globalcode.sql.dk.formatting.Formatter;
 import info.globalcode.sql.dk.jmx.ConnectionManagement;
 import info.globalcode.sql.dk.jmx.ConnectionManagement.COUNTER;
 import java.sql.Connection;
-import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -49,7 +48,7 @@
 public class DatabaseConnection implements AutoCloseable {
 
 	private static final Logger log = Logger.getLogger(DatabaseConnection.class.getName());
-	private static final String JDBC_PROPERTY_USER = "user";
+	public static final String JDBC_PROPERTY_USER = "user";
 	public static final String JDBC_PROPERTY_PASSWORD = "password";
 	private final DatabaseDefinition databaseDefinition;
 	private final Connection connection;
@@ -71,19 +70,7 @@
 		this.databaseDefinition = databaseDefinition;
 		this.properties = properties;
 		this.connectionMBean = connectionMBean;
-
-		if (properties.hasProperty(JDBC_PROPERTY_PASSWORD)) {
-			log.log(Level.WARNING, "Passing DB password as CLI parameter is insecure!");
-		}
-
-		Properties credentials = new Properties();
-		credentials.add(new Property(JDBC_PROPERTY_USER, databaseDefinition.getUserName()));
-		credentials.add(new Property(JDBC_PROPERTY_PASSWORD, databaseDefinition.getPassword()));
-		credentials.setDefaults(databaseDefinition.getProperties());
-		properties.setDefaults(credentials);
-		java.util.Properties javaProperties = properties.getJavaProperties();
-
-		connection = DriverManager.getConnection(databaseDefinition.getUrl(), javaProperties);
+		this.connection = Loader.jdbcConnect(databaseDefinition, properties);
 	}
 
 	public void executeQuery(SQLCommand sqlCommand, Formatter formatter) throws SQLException {
@@ -116,7 +103,7 @@
 	private void processCommand(SQLCommand sqlCommand, Formatter formatter) throws SQLException {
 		incrementCounter(connectionMBean, COUNTER.COMMAND);
 		resetCounter(connectionMBean, COUNTER.RECORD_CURRENT);
-		
+
 		try (PreparedStatement ps = sqlCommand.prepareStatement(connection)) {
 			log.log(Level.FINE, "Statement prepared");
 			sqlCommand.parametrize(ps);
@@ -157,7 +144,7 @@
 		while (rs.next()) {
 			incrementCounter(connectionMBean, COUNTER.RECORD_CURRENT);
 			incrementCounter(connectionMBean, COUNTER.RECORD_TOTAL);
-			
+
 			formatter.writeStartRow();
 
 			for (int i = 1; i <= columnCount; i++) {
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/Loader.java	Sat May 16 23:58:06 2015 +0200
+++ b/java/sql-dk/src/info/globalcode/sql/dk/configuration/Loader.java	Sun May 17 00:27:56 2015 +0200
@@ -18,6 +18,13 @@
 package info.globalcode.sql.dk.configuration;
 
 import info.globalcode.sql.dk.*;
+import static info.globalcode.sql.dk.DatabaseConnection.JDBC_PROPERTY_USER;
+import static info.globalcode.sql.dk.DatabaseConnection.JDBC_PROPERTY_PASSWORD;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.Unmarshaller;
 
@@ -28,6 +35,8 @@
  */
 public class Loader {
 
+	private static final Logger log = Logger.getLogger(Loader.class.getName());
+
 	public Configuration loadConfiguration() throws ConfigurationException {
 		try {
 			JAXBContext jaxb = JAXBContext.newInstance(Configuration.class);
@@ -38,4 +47,26 @@
 		}
 	}
 
+	/**
+	 * JDBC connection should not be used directly in SQL-DK.
+	 *
+	 * @see DatabaseDefinition#connect(info.globalcode.sql.dk.configuration.Properties)
+	 * @param properties
+	 * @param databaseDefinition
+	 * @return
+	 * @throws java.sql.SQLException
+	 */
+	public static Connection jdbcConnect(DatabaseDefinition databaseDefinition, Properties properties) throws SQLException {
+		if (properties.hasProperty(JDBC_PROPERTY_PASSWORD)) {
+			log.log(Level.WARNING, "Passing DB password as CLI parameter is insecure!");
+		}
+		Properties credentials = new Properties();
+		credentials.add(new Property(JDBC_PROPERTY_USER, databaseDefinition.getUserName()));
+		credentials.add(new Property(JDBC_PROPERTY_PASSWORD, databaseDefinition.getPassword()));
+		credentials.setDefaults(databaseDefinition.getProperties());
+		properties.setDefaults(credentials);
+		java.util.Properties javaProperties = properties.getJavaProperties();
+		return DriverManager.getConnection(databaseDefinition.getUrl(), javaProperties);
+	}
+
 }