InfoLister: list configured and configurable JDBC driver properties – option: --list-jdbc-properties v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Wed, 15 Jan 2014 21:06:12 +0100
branchv_0
changeset 159 9632b23df30c
parent 158 770b5009ec42
child 160 84ea4a819fb2
InfoLister: list configured and configurable JDBC driver properties – option: --list-jdbc-properties
java/sql-dk/src/info/globalcode/sql/dk/CLIOptions.java
java/sql-dk/src/info/globalcode/sql/dk/CLIParser.java
java/sql-dk/src/info/globalcode/sql/dk/InfoLister.java
java/sql-dk/src/info/globalcode/sql/dk/formatting/FakeSqlArray.java
scripts/bash_completion.pl
--- a/java/sql-dk/src/info/globalcode/sql/dk/CLIOptions.java	Wed Jan 15 18:15:55 2014 +0100
+++ b/java/sql-dk/src/info/globalcode/sql/dk/CLIOptions.java	Wed Jan 15 21:06:12 2014 +0100
@@ -46,6 +46,7 @@
 	private String sql;
 	private String databaseName;
 	private Set<String> databaseNamesToTest = new HashSet<>();
+	private Set<String> databaseNamesToListProperties = new HashSet<>();
 	private String namePrefix = DEFAULT_NAME_PREFIX;
 	private String nameSuffix = DEFAULT_NAME_SUFFIX;
 	private String formatterName;
@@ -95,6 +96,9 @@
 			if (showInfo.contains(InfoType.CONNECTION) && databaseNamesToTest.isEmpty()) {
 				e.addProblem(new InvalidOptionsException.OptionProblem("Please specify which database should be tested."));
 			}
+			if (showInfo.contains(InfoType.JDBC_PROPERTIES) && databaseNamesToListProperties.isEmpty()) {
+				e.addProblem(new InvalidOptionsException.OptionProblem("Please specify for which database the properties should be listed."));
+			}
 		}
 
 		if (!namedParameters.isEmpty() && !numberedParameters.isEmpty()) {
@@ -238,10 +242,18 @@
 		return databaseNamesToTest;
 	}
 
-	public void addDatabaseNamesToTest(String databaseNameToTest) {
-		this.databaseNamesToTest.add(databaseNameToTest);
+	public void addDatabaseNameToTest(String name) {
+		databaseNamesToTest.add(name);
 	}
 
+	public Set<String> getDatabaseNamesToListProperties() {
+		return databaseNamesToListProperties;
+	}
+
+	public void addDatabaseNameToListProperties(String name) {
+		databaseNamesToListProperties.add(name);
+	}
+	
 	public SQLCommand getSQLCommand() {
 		if (namedParameters.isEmpty()) {
 			return new SQLCommandNumbered(sql, numberedParameters);
--- a/java/sql-dk/src/info/globalcode/sql/dk/CLIParser.java	Wed Jan 15 18:15:55 2014 +0100
+++ b/java/sql-dk/src/info/globalcode/sql/dk/CLIParser.java	Wed Jan 15 21:06:12 2014 +0100
@@ -128,12 +128,16 @@
 				case Tokens.INFO_JDBC_DRIVERS:
 					options.addShowInfo(InfoType.JDBC_DRIVERS);
 					break;
+				case Tokens.INFO_JDBC_PROPERTIES:
+					options.addShowInfo(InfoType.JDBC_PROPERTIES);
+					options.addDatabaseNameToListProperties(fetchNext(args, ++i));
+					break;
 				case Tokens.INFO_DATABASES:
 					options.addShowInfo(InfoType.DATABASES);
 					break;
 				case Tokens.INFO_CONNECTION:
 					options.addShowInfo(InfoType.CONNECTION);
-					options.addDatabaseNamesToTest(fetchNext(args, ++i));
+					options.addDatabaseNameToTest(fetchNext(args, ++i));
 					break;
 				default:
 					throw new CLIParserException("Unknown option: " + arg);
@@ -170,6 +174,7 @@
 		public static final String INFO_FORMATTERS = "--list-formatters"; // bash-completion:option // help: print list of available formatters
 		public static final String INFO_TYPES = "--list-types"; // bash-completion:option // help: print list of available data types
 		public static final String INFO_JDBC_DRIVERS = "--list-jdbc-drivers"; // bash-completion:option // help: list of available JDBC drivers
+		public static final String INFO_JDBC_PROPERTIES = "--list-jdbc-properties"; // bash-completion:option // help: list of available JDBC properties for given database
 		public static final String INFO_DATABASES = "--list-databases"; // bash-completion:option // help: print list of configured databases
 		public static final String INFO_CONNECTION = "--test-connection"; // bash-completion:option // help: test connection to particular database
 
--- a/java/sql-dk/src/info/globalcode/sql/dk/InfoLister.java	Wed Jan 15 18:15:55 2014 +0100
+++ b/java/sql-dk/src/info/globalcode/sql/dk/InfoLister.java	Wed Jan 15 21:06:12 2014 +0100
@@ -22,19 +22,25 @@
 import info.globalcode.sql.dk.configuration.ConfigurationProvider;
 import info.globalcode.sql.dk.configuration.DatabaseDefinition;
 import info.globalcode.sql.dk.configuration.FormatterDefinition;
+import info.globalcode.sql.dk.configuration.Property;
 import info.globalcode.sql.dk.formatting.ColumnsHeader;
+import info.globalcode.sql.dk.formatting.FakeSqlArray;
 import info.globalcode.sql.dk.formatting.Formatter;
 import info.globalcode.sql.dk.formatting.FormatterContext;
 import info.globalcode.sql.dk.formatting.FormatterException;
 import java.io.BufferedReader;
 import java.io.InputStreamReader;
 import java.io.PrintStream;
+import java.sql.Array;
 import java.sql.Driver;
+import java.sql.DriverPropertyInfo;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.EnumSet;
+import java.util.HashSet;
 import java.util.List;
 import java.util.ServiceLoader;
+import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.sql.rowset.RowSetMetaDataImpl;
@@ -47,6 +53,10 @@
 public class InfoLister {
 
 	private static final Logger log = Logger.getLogger(InfoLister.class.getName());
+	/**
+	 * Fake database name for output formatting
+	 */
+	public static final String CONFIG_DB_NAME = "sqldk_configuration";
 	private PrintStream out;
 	private ConfigurationProvider configurationProvider;
 	private CLIOptions options;
@@ -67,6 +77,7 @@
 			switch (infoType) {
 				case CONNECTION:
 				case JDBC_DRIVERS:
+				case JDBC_PROPERTIES:
 				case DATABASES:
 				case FORMATTERS:
 				case TYPES:
@@ -79,10 +90,10 @@
 			try (Formatter f = getFormatter()) {
 				formatter = f;
 				formatter.writeStartBatch();
-				formatter.writeStartDatabase(new DatabaseDefinition());
-				formatter.writeStartStatement();
+				DatabaseDefinition dd = new DatabaseDefinition();
+				dd.setName(CONFIG_DB_NAME);
+				formatter.writeStartDatabase(dd);
 				showInfos(commands);
-				formatter.writeEndStatement();
 				formatter.writeEndDatabase();
 				formatter.writeEndBatch();
 				formatter.close();
@@ -117,7 +128,7 @@
 			data.add(new Object[]{fd.getName(), false, defaultFormatter.equals(fd.getName()), fd.getClassName()});
 		}
 
-		printTable(formatter, header, data);
+		printTable(formatter, header, data, "-- configured and built-in output formatters", null);
 
 
 	}
@@ -128,7 +139,7 @@
 		for (SQLType sqlType : SQLType.values()) {
 			data.add(new Object[]{sqlType.name(), sqlType.getCode()});
 		}
-		printTable(formatter, header, data);
+		printTable(formatter, header, data, "-- data types", null);
 		log.log(Level.INFO, "Type names in --types option are case insensitive");
 	}
 
@@ -148,7 +159,7 @@
 			}
 		}
 
-		printTable(formatter, header, data);
+		printTable(formatter, header, data, "-- configured databases", null);
 	}
 
 	public void listJdbcDrivers() throws FormatterException, ConfigurationException {
@@ -167,11 +178,90 @@
 				d.getMajorVersion() + "." + d.getMinorVersion(),
 				d.getMajorVersion(),
 				d.getMinorVersion(),
-				d.jdbcCompliant()});
+				d.jdbcCompliant()
+			});
 		}
 
-		printTable(formatter, header, data);
+		printTable(formatter, header, data, "-- discovered JDBC drivers (available on the CLASSPATH)", null);
+	}
+
+	public void listJdbcProperties() throws FormatterException, ConfigurationException {
+		for (String dbName : options.getDatabaseNamesToListProperties()) {
+			ColumnsHeader header = constructHeader(
+					new HeaderField("property_name", SQLType.VARCHAR),
+					new HeaderField("required", SQLType.BOOLEAN),
+					new HeaderField("choices", SQLType.ARRAY),
+					new HeaderField("configured_value", SQLType.VARCHAR),
+					new HeaderField("description", SQLType.VARCHAR));
+			List<Object[]> data = new ArrayList<>();
+
+			DatabaseDefinition dd = configurationProvider.getConfiguration().getDatabase(dbName);
+
+			Driver driver = findDriver(dd);
+
+			if (driver == null) {
+				log.log(Level.WARNING, "No JDBC driver was found for DB: {0} with URL: {1}", new Object[]{dd.getName(), dd.getUrl()});
+			} else {
+				log.log(Level.INFO, "For DB: {0} was found JDBC driver: {1}", new Object[]{dd.getName(), driver.getClass().getName()});
+
+				try {
+					DriverPropertyInfo[] propertyInfos = driver.getPropertyInfo(dd.getUrl(), dd.getProperties().getJavaProperties());
+
+					Set<String> standardProperties = new HashSet<>();
+
+					for (DriverPropertyInfo pi : propertyInfos) {
+						Array choices = new FakeSqlArray(pi.choices, SQLType.VARCHAR);
+						data.add(new Object[]{
+							pi.name,
+							pi.required,
+							choices.getArray() == null ? "" : choices,
+							pi.value == null ? "" : pi.value,
+							pi.description
+						});
+						standardProperties.add(pi.name);
+					}
 
+					for (Property p : dd.getProperties()) {
+						if (!standardProperties.contains(p.getName())) {
+							data.add(new Object[]{
+								p.getName(),
+								"",
+								"",
+								p.getValue(),
+								""
+							});
+							log.log(Level.WARNING, "Your configuration contains property „{0}“ not declared by the JDBC driver.", p.getName());
+						}
+					}
+
+				} catch (SQLException e) {
+					log.log(Level.WARNING, "Error during getting property infos.", e);
+				}
+
+				List<Parameter> parameters = new ArrayList<>();
+				parameters.add(new NamedParameter("databgase", dbName, SQLType.VARCHAR));
+				parameters.add(new NamedParameter("driver_class", driver.getClass().getName(), SQLType.VARCHAR));
+				parameters.add(new NamedParameter("driver_major_version", driver.getMajorVersion(), SQLType.INTEGER));
+				parameters.add(new NamedParameter("driver_minor_version", driver.getMinorVersion(), SQLType.INTEGER));
+
+				printTable(formatter, header, data, "-- configured and configurable JDBC driver properties", parameters);
+			}
+		}
+
+	}
+
+	private Driver findDriver(DatabaseDefinition dd) {
+		final ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
+		for (Driver d : drivers) {
+			try {
+				if (d.acceptsURL(dd.getUrl())) {
+					return d;
+				}
+			} catch (SQLException e) {
+				log.log(Level.WARNING, "Error during finding JDBC driver for: " + dd.getName(), e);
+			}
+		}
+		return null;
 	}
 
 	public void testConnection() throws FormatterException, ConfigurationException {
@@ -185,7 +275,7 @@
 			data.add(testConnection(dbName));
 		}
 
-		printTable(formatter, header, data);
+		printTable(formatter, header, data, "-- database configuration and connectivity test", null);
 	}
 
 	public Object[] testConnection(String dbName) {
@@ -228,7 +318,16 @@
 		out.println(line);
 	}
 
-	private void printTable(Formatter formatter, ColumnsHeader header, List<Object[]> data) throws ConfigurationException, FormatterException {
+	private void printTable(Formatter formatter, ColumnsHeader header, List<Object[]> data, String sql, List<Parameter> parameters) throws ConfigurationException, FormatterException {
+		formatter.writeStartStatement();
+
+		if (sql != null) {
+			formatter.writeQuery(sql);
+			if (parameters != null) {
+				formatter.writeParameters(parameters);
+			}
+		}
+
 		formatter.writeStartResultSet(header);
 
 		for (Object[] row : data) {
@@ -240,6 +339,7 @@
 		}
 
 		formatter.writeEndResultSet();
+		formatter.writeEndStatement();
 	}
 
 	private Formatter getFormatter() throws ConfigurationException, FormatterException {
@@ -319,6 +419,12 @@
 				infoLister.listJdbcDrivers();
 			}
 		},
+		JDBC_PROPERTIES {
+			@Override
+			public void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException {
+				infoLister.listJdbcProperties();
+			}
+		},
 		DATABASES {
 			@Override
 			public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/info/globalcode/sql/dk/formatting/FakeSqlArray.java	Wed Jan 15 21:06:12 2014 +0100
@@ -0,0 +1,96 @@
+/**
+ * SQL-DK
+ * Copyright © 2014 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.formatting;
+
+import info.globalcode.sql.dk.SQLType;
+import java.sql.Array;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+/**
+ * Fake SQL array, for formatting purposes only
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class FakeSqlArray implements Array {
+
+	private static final UnsupportedOperationException exception = new UnsupportedOperationException("This is just a fake SQL array.");
+	private final Object[] data;
+	private final SQLType baseType;
+
+	public FakeSqlArray(Object[] data, SQLType baseType) {
+		this.data = data;
+		this.baseType = baseType;
+	}
+
+	@Override
+	public String getBaseTypeName() throws SQLException {
+		return baseType.name();
+	}
+
+	@Override
+	public int getBaseType() throws SQLException {
+		return baseType.getCode();
+	}
+
+	@Override
+	public Object getArray() throws SQLException {
+		return data;
+	}
+
+	@Override
+	public Object getArray(Map<String, Class<?>> map) throws SQLException {
+		throw exception;
+	}
+
+	@Override
+	public Object getArray(long index, int count) throws SQLException {
+		throw exception;
+	}
+
+	@Override
+	public Object getArray(long index, int count, Map<String, Class<?>> map) throws SQLException {
+		throw exception;
+	}
+
+	@Override
+	public ResultSet getResultSet() throws SQLException {
+		throw exception;
+	}
+
+	@Override
+	public ResultSet getResultSet(Map<String, Class<?>> map) throws SQLException {
+		throw exception;
+	}
+
+	@Override
+	public ResultSet getResultSet(long index, int count) throws SQLException {
+		throw exception;
+	}
+
+	@Override
+	public ResultSet getResultSet(long index, int count, Map<String, Class<?>> map) throws SQLException {
+		throw exception;
+	}
+
+	@Override
+	public void free() throws SQLException {
+		throw exception;
+	}
+}
--- a/scripts/bash_completion.pl	Wed Jan 15 18:15:55 2014 +0100
+++ b/scripts/bash_completion.pl	Wed Jan 15 21:06:12 2014 +0100
@@ -50,7 +50,7 @@
 	prev=${COMP_WORDS[COMP_CWORD-1]}
 
 	case "$prev" in
-	--db | --test-connection)
+	--db | --test-connection | --list-jdbc-properties)
 		if [ -f '.$databasesFile.' ]; then
 			COMPREPLY=( $( compgen -W " $( cat '.$databasesFile.' ) " -- $cur ) )
 			return 0