InfoLister: list configured and configurable JDBC driver properties – option: --list-jdbc-properties
1.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/CLIOptions.java Wed Jan 15 18:15:55 2014 +0100
1.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/CLIOptions.java Wed Jan 15 21:06:12 2014 +0100
1.3 @@ -46,6 +46,7 @@
1.4 private String sql;
1.5 private String databaseName;
1.6 private Set<String> databaseNamesToTest = new HashSet<>();
1.7 + private Set<String> databaseNamesToListProperties = new HashSet<>();
1.8 private String namePrefix = DEFAULT_NAME_PREFIX;
1.9 private String nameSuffix = DEFAULT_NAME_SUFFIX;
1.10 private String formatterName;
1.11 @@ -95,6 +96,9 @@
1.12 if (showInfo.contains(InfoType.CONNECTION) && databaseNamesToTest.isEmpty()) {
1.13 e.addProblem(new InvalidOptionsException.OptionProblem("Please specify which database should be tested."));
1.14 }
1.15 + if (showInfo.contains(InfoType.JDBC_PROPERTIES) && databaseNamesToListProperties.isEmpty()) {
1.16 + e.addProblem(new InvalidOptionsException.OptionProblem("Please specify for which database the properties should be listed."));
1.17 + }
1.18 }
1.19
1.20 if (!namedParameters.isEmpty() && !numberedParameters.isEmpty()) {
1.21 @@ -238,10 +242,18 @@
1.22 return databaseNamesToTest;
1.23 }
1.24
1.25 - public void addDatabaseNamesToTest(String databaseNameToTest) {
1.26 - this.databaseNamesToTest.add(databaseNameToTest);
1.27 + public void addDatabaseNameToTest(String name) {
1.28 + databaseNamesToTest.add(name);
1.29 }
1.30
1.31 + public Set<String> getDatabaseNamesToListProperties() {
1.32 + return databaseNamesToListProperties;
1.33 + }
1.34 +
1.35 + public void addDatabaseNameToListProperties(String name) {
1.36 + databaseNamesToListProperties.add(name);
1.37 + }
1.38 +
1.39 public SQLCommand getSQLCommand() {
1.40 if (namedParameters.isEmpty()) {
1.41 return new SQLCommandNumbered(sql, numberedParameters);
2.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/CLIParser.java Wed Jan 15 18:15:55 2014 +0100
2.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/CLIParser.java Wed Jan 15 21:06:12 2014 +0100
2.3 @@ -128,12 +128,16 @@
2.4 case Tokens.INFO_JDBC_DRIVERS:
2.5 options.addShowInfo(InfoType.JDBC_DRIVERS);
2.6 break;
2.7 + case Tokens.INFO_JDBC_PROPERTIES:
2.8 + options.addShowInfo(InfoType.JDBC_PROPERTIES);
2.9 + options.addDatabaseNameToListProperties(fetchNext(args, ++i));
2.10 + break;
2.11 case Tokens.INFO_DATABASES:
2.12 options.addShowInfo(InfoType.DATABASES);
2.13 break;
2.14 case Tokens.INFO_CONNECTION:
2.15 options.addShowInfo(InfoType.CONNECTION);
2.16 - options.addDatabaseNamesToTest(fetchNext(args, ++i));
2.17 + options.addDatabaseNameToTest(fetchNext(args, ++i));
2.18 break;
2.19 default:
2.20 throw new CLIParserException("Unknown option: " + arg);
2.21 @@ -170,6 +174,7 @@
2.22 public static final String INFO_FORMATTERS = "--list-formatters"; // bash-completion:option // help: print list of available formatters
2.23 public static final String INFO_TYPES = "--list-types"; // bash-completion:option // help: print list of available data types
2.24 public static final String INFO_JDBC_DRIVERS = "--list-jdbc-drivers"; // bash-completion:option // help: list of available JDBC drivers
2.25 + public static final String INFO_JDBC_PROPERTIES = "--list-jdbc-properties"; // bash-completion:option // help: list of available JDBC properties for given database
2.26 public static final String INFO_DATABASES = "--list-databases"; // bash-completion:option // help: print list of configured databases
2.27 public static final String INFO_CONNECTION = "--test-connection"; // bash-completion:option // help: test connection to particular database
2.28
3.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/InfoLister.java Wed Jan 15 18:15:55 2014 +0100
3.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/InfoLister.java Wed Jan 15 21:06:12 2014 +0100
3.3 @@ -22,19 +22,25 @@
3.4 import info.globalcode.sql.dk.configuration.ConfigurationProvider;
3.5 import info.globalcode.sql.dk.configuration.DatabaseDefinition;
3.6 import info.globalcode.sql.dk.configuration.FormatterDefinition;
3.7 +import info.globalcode.sql.dk.configuration.Property;
3.8 import info.globalcode.sql.dk.formatting.ColumnsHeader;
3.9 +import info.globalcode.sql.dk.formatting.FakeSqlArray;
3.10 import info.globalcode.sql.dk.formatting.Formatter;
3.11 import info.globalcode.sql.dk.formatting.FormatterContext;
3.12 import info.globalcode.sql.dk.formatting.FormatterException;
3.13 import java.io.BufferedReader;
3.14 import java.io.InputStreamReader;
3.15 import java.io.PrintStream;
3.16 +import java.sql.Array;
3.17 import java.sql.Driver;
3.18 +import java.sql.DriverPropertyInfo;
3.19 import java.sql.SQLException;
3.20 import java.util.ArrayList;
3.21 import java.util.EnumSet;
3.22 +import java.util.HashSet;
3.23 import java.util.List;
3.24 import java.util.ServiceLoader;
3.25 +import java.util.Set;
3.26 import java.util.logging.Level;
3.27 import java.util.logging.Logger;
3.28 import javax.sql.rowset.RowSetMetaDataImpl;
3.29 @@ -47,6 +53,10 @@
3.30 public class InfoLister {
3.31
3.32 private static final Logger log = Logger.getLogger(InfoLister.class.getName());
3.33 + /**
3.34 + * Fake database name for output formatting
3.35 + */
3.36 + public static final String CONFIG_DB_NAME = "sqldk_configuration";
3.37 private PrintStream out;
3.38 private ConfigurationProvider configurationProvider;
3.39 private CLIOptions options;
3.40 @@ -67,6 +77,7 @@
3.41 switch (infoType) {
3.42 case CONNECTION:
3.43 case JDBC_DRIVERS:
3.44 + case JDBC_PROPERTIES:
3.45 case DATABASES:
3.46 case FORMATTERS:
3.47 case TYPES:
3.48 @@ -79,10 +90,10 @@
3.49 try (Formatter f = getFormatter()) {
3.50 formatter = f;
3.51 formatter.writeStartBatch();
3.52 - formatter.writeStartDatabase(new DatabaseDefinition());
3.53 - formatter.writeStartStatement();
3.54 + DatabaseDefinition dd = new DatabaseDefinition();
3.55 + dd.setName(CONFIG_DB_NAME);
3.56 + formatter.writeStartDatabase(dd);
3.57 showInfos(commands);
3.58 - formatter.writeEndStatement();
3.59 formatter.writeEndDatabase();
3.60 formatter.writeEndBatch();
3.61 formatter.close();
3.62 @@ -117,7 +128,7 @@
3.63 data.add(new Object[]{fd.getName(), false, defaultFormatter.equals(fd.getName()), fd.getClassName()});
3.64 }
3.65
3.66 - printTable(formatter, header, data);
3.67 + printTable(formatter, header, data, "-- configured and built-in output formatters", null);
3.68
3.69
3.70 }
3.71 @@ -128,7 +139,7 @@
3.72 for (SQLType sqlType : SQLType.values()) {
3.73 data.add(new Object[]{sqlType.name(), sqlType.getCode()});
3.74 }
3.75 - printTable(formatter, header, data);
3.76 + printTable(formatter, header, data, "-- data types", null);
3.77 log.log(Level.INFO, "Type names in --types option are case insensitive");
3.78 }
3.79
3.80 @@ -148,7 +159,7 @@
3.81 }
3.82 }
3.83
3.84 - printTable(formatter, header, data);
3.85 + printTable(formatter, header, data, "-- configured databases", null);
3.86 }
3.87
3.88 public void listJdbcDrivers() throws FormatterException, ConfigurationException {
3.89 @@ -167,11 +178,90 @@
3.90 d.getMajorVersion() + "." + d.getMinorVersion(),
3.91 d.getMajorVersion(),
3.92 d.getMinorVersion(),
3.93 - d.jdbcCompliant()});
3.94 + d.jdbcCompliant()
3.95 + });
3.96 }
3.97
3.98 - printTable(formatter, header, data);
3.99 + printTable(formatter, header, data, "-- discovered JDBC drivers (available on the CLASSPATH)", null);
3.100 + }
3.101
3.102 + public void listJdbcProperties() throws FormatterException, ConfigurationException {
3.103 + for (String dbName : options.getDatabaseNamesToListProperties()) {
3.104 + ColumnsHeader header = constructHeader(
3.105 + new HeaderField("property_name", SQLType.VARCHAR),
3.106 + new HeaderField("required", SQLType.BOOLEAN),
3.107 + new HeaderField("choices", SQLType.ARRAY),
3.108 + new HeaderField("configured_value", SQLType.VARCHAR),
3.109 + new HeaderField("description", SQLType.VARCHAR));
3.110 + List<Object[]> data = new ArrayList<>();
3.111 +
3.112 + DatabaseDefinition dd = configurationProvider.getConfiguration().getDatabase(dbName);
3.113 +
3.114 + Driver driver = findDriver(dd);
3.115 +
3.116 + if (driver == null) {
3.117 + log.log(Level.WARNING, "No JDBC driver was found for DB: {0} with URL: {1}", new Object[]{dd.getName(), dd.getUrl()});
3.118 + } else {
3.119 + log.log(Level.INFO, "For DB: {0} was found JDBC driver: {1}", new Object[]{dd.getName(), driver.getClass().getName()});
3.120 +
3.121 + try {
3.122 + DriverPropertyInfo[] propertyInfos = driver.getPropertyInfo(dd.getUrl(), dd.getProperties().getJavaProperties());
3.123 +
3.124 + Set<String> standardProperties = new HashSet<>();
3.125 +
3.126 + for (DriverPropertyInfo pi : propertyInfos) {
3.127 + Array choices = new FakeSqlArray(pi.choices, SQLType.VARCHAR);
3.128 + data.add(new Object[]{
3.129 + pi.name,
3.130 + pi.required,
3.131 + choices.getArray() == null ? "" : choices,
3.132 + pi.value == null ? "" : pi.value,
3.133 + pi.description
3.134 + });
3.135 + standardProperties.add(pi.name);
3.136 + }
3.137 +
3.138 + for (Property p : dd.getProperties()) {
3.139 + if (!standardProperties.contains(p.getName())) {
3.140 + data.add(new Object[]{
3.141 + p.getName(),
3.142 + "",
3.143 + "",
3.144 + p.getValue(),
3.145 + ""
3.146 + });
3.147 + log.log(Level.WARNING, "Your configuration contains property „{0}“ not declared by the JDBC driver.", p.getName());
3.148 + }
3.149 + }
3.150 +
3.151 + } catch (SQLException e) {
3.152 + log.log(Level.WARNING, "Error during getting property infos.", e);
3.153 + }
3.154 +
3.155 + List<Parameter> parameters = new ArrayList<>();
3.156 + parameters.add(new NamedParameter("databgase", dbName, SQLType.VARCHAR));
3.157 + parameters.add(new NamedParameter("driver_class", driver.getClass().getName(), SQLType.VARCHAR));
3.158 + parameters.add(new NamedParameter("driver_major_version", driver.getMajorVersion(), SQLType.INTEGER));
3.159 + parameters.add(new NamedParameter("driver_minor_version", driver.getMinorVersion(), SQLType.INTEGER));
3.160 +
3.161 + printTable(formatter, header, data, "-- configured and configurable JDBC driver properties", parameters);
3.162 + }
3.163 + }
3.164 +
3.165 + }
3.166 +
3.167 + private Driver findDriver(DatabaseDefinition dd) {
3.168 + final ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
3.169 + for (Driver d : drivers) {
3.170 + try {
3.171 + if (d.acceptsURL(dd.getUrl())) {
3.172 + return d;
3.173 + }
3.174 + } catch (SQLException e) {
3.175 + log.log(Level.WARNING, "Error during finding JDBC driver for: " + dd.getName(), e);
3.176 + }
3.177 + }
3.178 + return null;
3.179 }
3.180
3.181 public void testConnection() throws FormatterException, ConfigurationException {
3.182 @@ -185,7 +275,7 @@
3.183 data.add(testConnection(dbName));
3.184 }
3.185
3.186 - printTable(formatter, header, data);
3.187 + printTable(formatter, header, data, "-- database configuration and connectivity test", null);
3.188 }
3.189
3.190 public Object[] testConnection(String dbName) {
3.191 @@ -228,7 +318,16 @@
3.192 out.println(line);
3.193 }
3.194
3.195 - private void printTable(Formatter formatter, ColumnsHeader header, List<Object[]> data) throws ConfigurationException, FormatterException {
3.196 + private void printTable(Formatter formatter, ColumnsHeader header, List<Object[]> data, String sql, List<Parameter> parameters) throws ConfigurationException, FormatterException {
3.197 + formatter.writeStartStatement();
3.198 +
3.199 + if (sql != null) {
3.200 + formatter.writeQuery(sql);
3.201 + if (parameters != null) {
3.202 + formatter.writeParameters(parameters);
3.203 + }
3.204 + }
3.205 +
3.206 formatter.writeStartResultSet(header);
3.207
3.208 for (Object[] row : data) {
3.209 @@ -240,6 +339,7 @@
3.210 }
3.211
3.212 formatter.writeEndResultSet();
3.213 + formatter.writeEndStatement();
3.214 }
3.215
3.216 private Formatter getFormatter() throws ConfigurationException, FormatterException {
3.217 @@ -319,6 +419,12 @@
3.218 infoLister.listJdbcDrivers();
3.219 }
3.220 },
3.221 + JDBC_PROPERTIES {
3.222 + @Override
3.223 + public void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException {
3.224 + infoLister.listJdbcProperties();
3.225 + }
3.226 + },
3.227 DATABASES {
3.228 @Override
3.229 public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/formatting/FakeSqlArray.java Wed Jan 15 21:06:12 2014 +0100
4.3 @@ -0,0 +1,96 @@
4.4 +/**
4.5 + * SQL-DK
4.6 + * Copyright © 2014 František Kučera (frantovo.cz)
4.7 + *
4.8 + * This program is free software: you can redistribute it and/or modify
4.9 + * it under the terms of the GNU General Public License as published by
4.10 + * the Free Software Foundation, either version 3 of the License, or
4.11 + * (at your option) any later version.
4.12 + *
4.13 + * This program is distributed in the hope that it will be useful,
4.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
4.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4.16 + * GNU General Public License for more details.
4.17 + *
4.18 + * You should have received a copy of the GNU General Public License
4.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
4.20 + */
4.21 +package info.globalcode.sql.dk.formatting;
4.22 +
4.23 +import info.globalcode.sql.dk.SQLType;
4.24 +import java.sql.Array;
4.25 +import java.sql.ResultSet;
4.26 +import java.sql.SQLException;
4.27 +import java.util.Map;
4.28 +
4.29 +/**
4.30 + * Fake SQL array, for formatting purposes only
4.31 + *
4.32 + * @author Ing. František Kučera (frantovo.cz)
4.33 + */
4.34 +public class FakeSqlArray implements Array {
4.35 +
4.36 + private static final UnsupportedOperationException exception = new UnsupportedOperationException("This is just a fake SQL array.");
4.37 + private final Object[] data;
4.38 + private final SQLType baseType;
4.39 +
4.40 + public FakeSqlArray(Object[] data, SQLType baseType) {
4.41 + this.data = data;
4.42 + this.baseType = baseType;
4.43 + }
4.44 +
4.45 + @Override
4.46 + public String getBaseTypeName() throws SQLException {
4.47 + return baseType.name();
4.48 + }
4.49 +
4.50 + @Override
4.51 + public int getBaseType() throws SQLException {
4.52 + return baseType.getCode();
4.53 + }
4.54 +
4.55 + @Override
4.56 + public Object getArray() throws SQLException {
4.57 + return data;
4.58 + }
4.59 +
4.60 + @Override
4.61 + public Object getArray(Map<String, Class<?>> map) throws SQLException {
4.62 + throw exception;
4.63 + }
4.64 +
4.65 + @Override
4.66 + public Object getArray(long index, int count) throws SQLException {
4.67 + throw exception;
4.68 + }
4.69 +
4.70 + @Override
4.71 + public Object getArray(long index, int count, Map<String, Class<?>> map) throws SQLException {
4.72 + throw exception;
4.73 + }
4.74 +
4.75 + @Override
4.76 + public ResultSet getResultSet() throws SQLException {
4.77 + throw exception;
4.78 + }
4.79 +
4.80 + @Override
4.81 + public ResultSet getResultSet(Map<String, Class<?>> map) throws SQLException {
4.82 + throw exception;
4.83 + }
4.84 +
4.85 + @Override
4.86 + public ResultSet getResultSet(long index, int count) throws SQLException {
4.87 + throw exception;
4.88 + }
4.89 +
4.90 + @Override
4.91 + public ResultSet getResultSet(long index, int count, Map<String, Class<?>> map) throws SQLException {
4.92 + throw exception;
4.93 + }
4.94 +
4.95 + @Override
4.96 + public void free() throws SQLException {
4.97 + throw exception;
4.98 + }
4.99 +}
5.1 --- a/scripts/bash_completion.pl Wed Jan 15 18:15:55 2014 +0100
5.2 +++ b/scripts/bash_completion.pl Wed Jan 15 21:06:12 2014 +0100
5.3 @@ -50,7 +50,7 @@
5.4 prev=${COMP_WORDS[COMP_CWORD-1]}
5.5
5.6 case "$prev" in
5.7 - --db | --test-connection)
5.8 + --db | --test-connection | --list-jdbc-properties)
5.9 if [ -f '.$databasesFile.' ]; then
5.10 COMPREPLY=( $( compgen -W " $( cat '.$databasesFile.' ) " -- $cur ) )
5.11 return 0