1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/InfoLister.java Mon Mar 04 20:15:24 2019 +0100
1.3 @@ -0,0 +1,673 @@
1.4 +/**
1.5 + * SQL-DK
1.6 + * Copyright © 2013 František Kučera (frantovo.cz)
1.7 + *
1.8 + * This program is free software: you can redistribute it and/or modify
1.9 + * it under the terms of the GNU General Public License as published by
1.10 + * the Free Software Foundation, either version 3 of the License, or
1.11 + * (at your option) any later version.
1.12 + *
1.13 + * This program is distributed in the hope that it will be useful,
1.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.16 + * GNU General Public License for more details.
1.17 + *
1.18 + * You should have received a copy of the GNU General Public License
1.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
1.20 + */
1.21 +package info.globalcode.sql.dk;
1.22 +
1.23 +import info.globalcode.sql.dk.configuration.CommandArgument;
1.24 +import info.globalcode.sql.dk.configuration.Configuration;
1.25 +import info.globalcode.sql.dk.configuration.ConfigurationException;
1.26 +import info.globalcode.sql.dk.configuration.ConfigurationProvider;
1.27 +import info.globalcode.sql.dk.configuration.DatabaseDefinition;
1.28 +import info.globalcode.sql.dk.configuration.FormatterDefinition;
1.29 +import info.globalcode.sql.dk.configuration.Properties;
1.30 +import info.globalcode.sql.dk.configuration.Property;
1.31 +import info.globalcode.sql.dk.configuration.PropertyDeclaration;
1.32 +import info.globalcode.sql.dk.configuration.TunnelDefinition;
1.33 +import info.globalcode.sql.dk.formatting.ColumnsHeader;
1.34 +import info.globalcode.sql.dk.formatting.CommonProperties;
1.35 +import info.globalcode.sql.dk.formatting.FakeSqlArray;
1.36 +import info.globalcode.sql.dk.formatting.Formatter;
1.37 +import info.globalcode.sql.dk.formatting.FormatterContext;
1.38 +import info.globalcode.sql.dk.formatting.FormatterException;
1.39 +import java.io.BufferedReader;
1.40 +import java.io.ByteArrayOutputStream;
1.41 +import java.io.InputStreamReader;
1.42 +import java.io.PrintStream;
1.43 +import java.sql.Array;
1.44 +import java.sql.Driver;
1.45 +import java.sql.DriverManager;
1.46 +import java.sql.DriverPropertyInfo;
1.47 +import java.sql.SQLException;
1.48 +import java.util.ArrayList;
1.49 +import java.util.Collections;
1.50 +import java.util.Comparator;
1.51 +import java.util.EnumSet;
1.52 +import java.util.HashMap;
1.53 +import java.util.HashSet;
1.54 +import java.util.List;
1.55 +import java.util.Map;
1.56 +import java.util.Map.Entry;
1.57 +import java.util.ServiceLoader;
1.58 +import java.util.Set;
1.59 +import java.util.concurrent.ExecutorService;
1.60 +import java.util.concurrent.Executors;
1.61 +import java.util.concurrent.TimeUnit;
1.62 +import java.util.logging.Level;
1.63 +import java.util.logging.LogRecord;
1.64 +import java.util.logging.Logger;
1.65 +import javax.sql.rowset.RowSetMetaDataImpl;
1.66 +
1.67 +/**
1.68 + * Displays info like help, version etc.
1.69 + *
1.70 + * @author Ing. František Kučera (frantovo.cz)
1.71 + */
1.72 +public class InfoLister {
1.73 +
1.74 + private static final Logger log = Logger.getLogger(InfoLister.class.getName());
1.75 + /**
1.76 + * Fake database name for output formatting
1.77 + */
1.78 + public static final String CONFIG_DB_NAME = "sqldk_configuration";
1.79 + private final PrintStream out;
1.80 + private final ConfigurationProvider configurationProvider;
1.81 + private final CLIOptions options;
1.82 + private Formatter formatter;
1.83 +
1.84 + public InfoLister(PrintStream out, ConfigurationProvider configurationProvider, CLIOptions options) {
1.85 + this.out = out;
1.86 + this.configurationProvider = configurationProvider;
1.87 + this.options = options;
1.88 + }
1.89 +
1.90 + public void showInfo() throws ConfigurationException, FormatterException {
1.91 + EnumSet<InfoType> commands = options.getShowInfo();
1.92 +
1.93 + boolean formattinNeeded = false;
1.94 +
1.95 + for (InfoType infoType : commands) {
1.96 + switch (infoType) {
1.97 + case CONNECTION:
1.98 + case JDBC_DRIVERS:
1.99 + case JDBC_PROPERTIES:
1.100 + case DATABASES:
1.101 + case FORMATTERS:
1.102 + case FORMATTER_PROPERTIES:
1.103 + case TYPES:
1.104 + case JAVA_PROPERTIES:
1.105 + case ENVIRONMENT_VARIABLES:
1.106 + formattinNeeded = true;
1.107 + break;
1.108 + }
1.109 + }
1.110 +
1.111 + if (formattinNeeded) {
1.112 + try (Formatter f = getFormatter()) {
1.113 + formatter = f;
1.114 + formatter.writeStartBatch();
1.115 + DatabaseDefinition dd = new DatabaseDefinition();
1.116 + dd.setName(CONFIG_DB_NAME);
1.117 + formatter.writeStartDatabase(dd);
1.118 + showInfos(commands);
1.119 + formatter.writeEndDatabase();
1.120 + formatter.writeEndBatch();
1.121 + formatter.close();
1.122 + }
1.123 + } else {
1.124 + showInfos(commands);
1.125 + }
1.126 + }
1.127 +
1.128 + private void showInfos(EnumSet<InfoType> commands) throws ConfigurationException, FormatterException {
1.129 + for (InfoType infoType : commands) {
1.130 + infoType.showInfo(this);
1.131 + }
1.132 + }
1.133 +
1.134 + private void listJavaProperties() throws FormatterException, ConfigurationException {
1.135 + ColumnsHeader header = constructHeader(new HeaderField("name", SQLType.VARCHAR), new HeaderField("value", SQLType.VARCHAR));
1.136 + List<Object[]> data = new ArrayList<>();
1.137 + for (Entry<Object, Object> e : System.getProperties().entrySet()) {
1.138 + data.add(new Object[]{e.getKey(), e.getValue()});
1.139 + }
1.140 + printTable(formatter, header, "-- Java system properties", null, data, 0);
1.141 + }
1.142 +
1.143 + private void listEnvironmentVariables() throws FormatterException, ConfigurationException {
1.144 + ColumnsHeader header = constructHeader(new HeaderField("name", SQLType.VARCHAR), new HeaderField("value", SQLType.VARCHAR));
1.145 + List<Object[]> data = new ArrayList<>();
1.146 + for (Entry<String, String> e : System.getenv().entrySet()) {
1.147 + data.add(new Object[]{e.getKey(), e.getValue()});
1.148 + }
1.149 + printTable(formatter, header, "-- environment variables", null, data, 0);
1.150 + }
1.151 +
1.152 + private void listFormatters() throws ConfigurationException, FormatterException {
1.153 + ColumnsHeader header = constructHeader(
1.154 + new HeaderField("name", SQLType.VARCHAR),
1.155 + new HeaderField("built_in", SQLType.BOOLEAN),
1.156 + new HeaderField("default", SQLType.BOOLEAN),
1.157 + new HeaderField("class_name", SQLType.VARCHAR),
1.158 + new HeaderField("valid", SQLType.BOOLEAN));
1.159 + List<Object[]> data = new ArrayList<>();
1.160 +
1.161 + String defaultFormatter = configurationProvider.getConfiguration().getDefaultFormatter();
1.162 + defaultFormatter = defaultFormatter == null ? Configuration.DEFAULT_FORMATTER : defaultFormatter;
1.163 +
1.164 + for (FormatterDefinition fd : configurationProvider.getConfiguration().getBuildInFormatters()) {
1.165 + data.add(new Object[]{fd.getName(), true, defaultFormatter.equals(fd.getName()), fd.getClassName(), isInstantiable(fd)});
1.166 + }
1.167 +
1.168 + for (FormatterDefinition fd : configurationProvider.getConfiguration().getFormatters()) {
1.169 + data.add(new Object[]{fd.getName(), false, defaultFormatter.equals(fd.getName()), fd.getClassName(), isInstantiable(fd)});
1.170 + }
1.171 +
1.172 + printTable(formatter, header, "-- configured and built-in output formatters", null, data);
1.173 + }
1.174 +
1.175 + private boolean isInstantiable(FormatterDefinition fd) {
1.176 + try {
1.177 + try (ByteArrayOutputStream testStream = new ByteArrayOutputStream()) {
1.178 + fd.getInstance(new FormatterContext(testStream, new Properties(0)));
1.179 + return true;
1.180 + }
1.181 + } catch (Exception e) {
1.182 + log.log(Level.SEVERE, "Unable to create an instance of formatter: " + fd.getName(), e);
1.183 + return false;
1.184 + }
1.185 + }
1.186 +
1.187 + private void listFormatterProperties() throws FormatterException, ConfigurationException {
1.188 + for (String formatterName : options.getFormatterNamesToListProperties()) {
1.189 + listFormatterProperties(formatterName);
1.190 + }
1.191 + }
1.192 +
1.193 + private void listFormatterProperties(String formatterName) throws FormatterException, ConfigurationException {
1.194 + FormatterDefinition fd = configurationProvider.getConfiguration().getFormatter(formatterName);
1.195 + try {
1.196 +
1.197 + // currently only for debugging purposes
1.198 + // TODO: introduce --info-lister-property or generic filtering capability in printTable() ?
1.199 + boolean printDeclaredIn = options.getFormatterProperties().getBoolean("InfoLister:print:declared_in", false);
1.200 +
1.201 + List<HeaderField> headerFields = new ArrayList<>();
1.202 + headerFields.add(new HeaderField("name", SQLType.VARCHAR));
1.203 + headerFields.add(new HeaderField("type", SQLType.VARCHAR));
1.204 + headerFields.add(new HeaderField("default", SQLType.VARCHAR));
1.205 + headerFields.add(new HeaderField("description", SQLType.VARCHAR));
1.206 + if (printDeclaredIn) {
1.207 + headerFields.add(new HeaderField("declared_in", SQLType.VARCHAR));
1.208 + }
1.209 +
1.210 + ColumnsHeader header = constructHeader(headerFields.toArray(new HeaderField[0]));
1.211 +
1.212 + Map<String, Object[]> data = new HashMap<>();
1.213 + Class<Formatter> formatterClass = (Class<Formatter>) Class.forName(fd.getClassName());
1.214 + List<Class<? extends Formatter>> hierarchy = Functions.getClassHierarchy(formatterClass, Formatter.class);
1.215 + Collections.reverse(hierarchy);
1.216 + hierarchy.stream().forEach((c) -> {
1.217 + for (PropertyDeclaration p : Functions.getPropertyDeclarations(c)) {
1.218 + data.put(p.name(), propertyDeclarationToRow(p, c, printDeclaredIn));
1.219 + }
1.220 + });
1.221 +
1.222 + List<Parameter> parameters = new ArrayList<>();
1.223 + parameters.add(new NamedParameter("formatter", formatterName, SQLType.VARCHAR));
1.224 +
1.225 + printTable(formatter, header, "-- formatter properties", parameters, new ArrayList<>(data.values()));
1.226 + } catch (ClassNotFoundException e) {
1.227 + throw new ConfigurationException("Unable to find class " + fd.getClassName() + " of formatter" + fd.getName(), e);
1.228 + }
1.229 + }
1.230 +
1.231 + private static Object[] propertyDeclarationToRow(PropertyDeclaration p, Class formatterClass, boolean printDeclaredIn) {
1.232 + List list = new ArrayList();
1.233 +
1.234 + list.add(p.name());
1.235 + list.add(CommonProperties.getSimpleTypeName(p.type()));
1.236 + list.add(p.defaultValue());
1.237 + list.add(p.description());
1.238 + if (printDeclaredIn) {
1.239 + list.add(formatterClass.getName());
1.240 + }
1.241 +
1.242 + return list.toArray();
1.243 + }
1.244 +
1.245 + private void listTypes() throws FormatterException, ConfigurationException {
1.246 + ColumnsHeader header = constructHeader(new HeaderField("name", SQLType.VARCHAR), new HeaderField("code", SQLType.INTEGER));
1.247 + List<Object[]> data = new ArrayList<>();
1.248 + for (SQLType sqlType : SQLType.values()) {
1.249 + data.add(new Object[]{sqlType.name(), sqlType.getCode()});
1.250 + }
1.251 + printTable(formatter, header, "-- data types", null, data);
1.252 + log.log(Level.INFO, "Type names in --types option are case insensitive");
1.253 + }
1.254 +
1.255 + private void listDatabases() throws ConfigurationException, FormatterException {
1.256 + ColumnsHeader header = constructHeader(
1.257 + new HeaderField("database_name", SQLType.VARCHAR),
1.258 + new HeaderField("user_name", SQLType.VARCHAR),
1.259 + new HeaderField("database_url", SQLType.VARCHAR));
1.260 + List<Object[]> data = new ArrayList<>();
1.261 +
1.262 + final List<DatabaseDefinition> configuredDatabases = configurationProvider.getConfiguration().getDatabases();
1.263 + if (configuredDatabases.isEmpty()) {
1.264 + log.log(Level.WARNING, "No databases are configured.");
1.265 + } else {
1.266 + for (DatabaseDefinition dd : configuredDatabases) {
1.267 + data.add(new Object[]{dd.getName(), dd.getUserName(), dd.getUrl()});
1.268 +
1.269 + final TunnelDefinition tunnel = dd.getTunnel();
1.270 + if (tunnel != null) {
1.271 + log.log(Level.INFO, "Tunnel command: {0}", tunnel.getCommand());
1.272 + for (CommandArgument ca : Functions.notNull(tunnel.getArguments())) {
1.273 + log.log(Level.INFO, "\targument: {0}/{1}", new Object[]{ca.getType(), ca.getValue()});
1.274 + }
1.275 + }
1.276 +
1.277 + }
1.278 + }
1.279 +
1.280 + printTable(formatter, header, "-- configured databases", null, data);
1.281 + }
1.282 +
1.283 + private void listJdbcDrivers() throws FormatterException, ConfigurationException {
1.284 + ColumnsHeader header = constructHeader(
1.285 + new HeaderField("class", SQLType.VARCHAR),
1.286 + new HeaderField("version", SQLType.VARCHAR),
1.287 + new HeaderField("major", SQLType.INTEGER),
1.288 + new HeaderField("minor", SQLType.INTEGER),
1.289 + new HeaderField("jdbc_compliant", SQLType.BOOLEAN));
1.290 + List<Object[]> data = new ArrayList<>();
1.291 +
1.292 + final ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
1.293 + for (Driver d : drivers) {
1.294 + data.add(new Object[]{
1.295 + d.getClass().getName(),
1.296 + d.getMajorVersion() + "." + d.getMinorVersion(),
1.297 + d.getMajorVersion(),
1.298 + d.getMinorVersion(),
1.299 + d.jdbcCompliant()
1.300 + });
1.301 + }
1.302 +
1.303 + printTable(formatter, header, "-- discovered JDBC drivers (available on the CLASSPATH)", null, data);
1.304 + }
1.305 +
1.306 + private void listJdbcProperties() throws FormatterException, ConfigurationException {
1.307 + for (String dbName : options.getDatabaseNamesToListProperties()) {
1.308 + ColumnsHeader header = constructHeader(
1.309 + new HeaderField("property_name", SQLType.VARCHAR),
1.310 + new HeaderField("required", SQLType.BOOLEAN),
1.311 + new HeaderField("choices", SQLType.ARRAY),
1.312 + new HeaderField("configured_value", SQLType.VARCHAR),
1.313 + new HeaderField("description", SQLType.VARCHAR));
1.314 + List<Object[]> data = new ArrayList<>();
1.315 +
1.316 + DatabaseDefinition dd = configurationProvider.getConfiguration().getDatabase(dbName);
1.317 +
1.318 + Driver driver = findDriver(dd);
1.319 +
1.320 + if (driver == null) {
1.321 + log.log(Level.WARNING, "No JDBC driver was found for DB: {0} with URL: {1}", new Object[]{dd.getName(), dd.getUrl()});
1.322 + } else {
1.323 + log.log(Level.INFO, "For DB: {0} was found JDBC driver: {1}", new Object[]{dd.getName(), driver.getClass().getName()});
1.324 +
1.325 + try {
1.326 + DriverPropertyInfo[] propertyInfos = driver.getPropertyInfo(dd.getUrl(), dd.getProperties().getJavaProperties());
1.327 +
1.328 + Set<String> standardProperties = new HashSet<>();
1.329 +
1.330 + for (DriverPropertyInfo pi : propertyInfos) {
1.331 + Array choices = new FakeSqlArray(pi.choices, SQLType.VARCHAR);
1.332 + data.add(new Object[]{
1.333 + pi.name,
1.334 + pi.required,
1.335 + choices.getArray() == null ? "" : choices,
1.336 + pi.value == null ? "" : pi.value,
1.337 + pi.description
1.338 + });
1.339 + standardProperties.add(pi.name);
1.340 + }
1.341 +
1.342 + for (Property p : dd.getProperties()) {
1.343 + if (!standardProperties.contains(p.getName())) {
1.344 + data.add(new Object[]{
1.345 + p.getName(),
1.346 + "",
1.347 + "",
1.348 + p.getValue(),
1.349 + ""
1.350 + });
1.351 + log.log(Level.WARNING, "Your configuration contains property „{0}“ not declared by the JDBC driver.", p.getName());
1.352 + }
1.353 + }
1.354 +
1.355 + } catch (SQLException e) {
1.356 + log.log(Level.WARNING, "Error during getting property infos.", e);
1.357 + }
1.358 +
1.359 + List<Parameter> parameters = new ArrayList<>();
1.360 + parameters.add(new NamedParameter("database", dbName, SQLType.VARCHAR));
1.361 + parameters.add(new NamedParameter("driver_class", driver.getClass().getName(), SQLType.VARCHAR));
1.362 + parameters.add(new NamedParameter("driver_major_version", driver.getMajorVersion(), SQLType.INTEGER));
1.363 + parameters.add(new NamedParameter("driver_minor_version", driver.getMinorVersion(), SQLType.INTEGER));
1.364 +
1.365 + printTable(formatter, header, "-- configured and configurable JDBC driver properties", parameters, data);
1.366 + }
1.367 + }
1.368 +
1.369 + }
1.370 +
1.371 + private Driver findDriver(DatabaseDefinition dd) {
1.372 + final ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
1.373 + for (Driver d : drivers) {
1.374 + try {
1.375 + if (d.acceptsURL(dd.getUrl())) {
1.376 + return d;
1.377 + }
1.378 + } catch (SQLException e) {
1.379 + log.log(Level.WARNING, "Error during finding JDBC driver for: " + dd.getName(), e);
1.380 + }
1.381 + }
1.382 + return null;
1.383 + }
1.384 +
1.385 + /**
1.386 + * Parallelism for connection testing – maximum concurrent database connections.
1.387 + */
1.388 + private static final int TESTING_THREAD_COUNT = 64;
1.389 + /**
1.390 + * Time limit for all connection testing threads – particular timeouts per connection will be
1.391 + * much smaller.
1.392 + */
1.393 + private static final long TESTING_AWAIT_LIMIT = 1;
1.394 + private static final TimeUnit TESTING_AWAIT_UNIT = TimeUnit.DAYS;
1.395 +
1.396 + private void testConnections() throws FormatterException, ConfigurationException {
1.397 + ColumnsHeader header = constructHeader(
1.398 + new HeaderField("database_name", SQLType.VARCHAR),
1.399 + new HeaderField("configured", SQLType.BOOLEAN),
1.400 + new HeaderField("connected", SQLType.BOOLEAN),
1.401 + new HeaderField("product_name", SQLType.VARCHAR),
1.402 + new HeaderField("product_version", SQLType.VARCHAR));
1.403 +
1.404 + log.log(Level.FINE, "Testing DB connections in {0} threads", TESTING_THREAD_COUNT);
1.405 +
1.406 + ExecutorService es = Executors.newFixedThreadPool(TESTING_THREAD_COUNT);
1.407 +
1.408 + final Formatter currentFormatter = formatter;
1.409 +
1.410 + printHeader(currentFormatter, header, "-- database configuration and connectivity test", null);
1.411 +
1.412 + for (final String dbName : options.getDatabaseNamesToTest()) {
1.413 + preloadDriver(dbName);
1.414 + }
1.415 +
1.416 + for (final String dbName : options.getDatabaseNamesToTest()) {
1.417 + es.submit(() -> {
1.418 + final Object[] row = testConnection(dbName);
1.419 + synchronized (currentFormatter) {
1.420 + printRow(currentFormatter, row);
1.421 + }
1.422 + }
1.423 + );
1.424 + }
1.425 +
1.426 + es.shutdown();
1.427 +
1.428 + try {
1.429 + log.log(Level.FINEST, "Waiting for test results: {0} {1}", new Object[]{TESTING_AWAIT_LIMIT, TESTING_AWAIT_UNIT.name()});
1.430 + boolean finished = es.awaitTermination(TESTING_AWAIT_LIMIT, TESTING_AWAIT_UNIT);
1.431 + if (finished) {
1.432 + log.log(Level.FINEST, "All testing threads finished in time limit.");
1.433 + } else {
1.434 + throw new FormatterException("Exceeded total time limit for test threads – this should never happen");
1.435 + }
1.436 + } catch (InterruptedException e) {
1.437 + throw new FormatterException("Interrupted while waiting for test results", e);
1.438 + }
1.439 +
1.440 + printFooter(currentFormatter);
1.441 + }
1.442 +
1.443 + /**
1.444 + * JDBC driver classes should be preloaded in single thread to avoid deadlocks while doing
1.445 + * {@linkplain DriverManager#registerDriver(java.sql.Driver)} during parallel connections.
1.446 + *
1.447 + * @param dbName
1.448 + */
1.449 + private void preloadDriver(String dbName) {
1.450 + try {
1.451 + DatabaseDefinition dd = configurationProvider.getConfiguration().getDatabase(dbName);
1.452 + Driver driver = findDriver(dd);
1.453 + if (driver == null) {
1.454 + log.log(Level.WARNING, "No Driver found for DB: {0}", dbName);
1.455 + } else {
1.456 + log.log(Level.FINEST, "Driver preloading for DB: {0} was successfull", dbName);
1.457 + }
1.458 + } catch (Exception e) {
1.459 + LogRecord r = new LogRecord(Level.WARNING, "Failed to preload the Driver for DB: {0}");
1.460 + r.setParameters(new Object[]{dbName});
1.461 + r.setThrown(e);
1.462 + log.log(r);
1.463 + }
1.464 + }
1.465 +
1.466 + private Object[] testConnection(String dbName) {
1.467 + log.log(Level.FINE, "Testing connection to database: {0}", dbName);
1.468 +
1.469 + boolean succesfullyConnected = false;
1.470 + boolean succesfullyConfigured = false;
1.471 + String productName = null;
1.472 + String productVersion = null;
1.473 +
1.474 + try {
1.475 + DatabaseDefinition dd = configurationProvider.getConfiguration().getDatabase(dbName);
1.476 + log.log(Level.FINE, "Database definition was loaded from configuration");
1.477 + succesfullyConfigured = true;
1.478 + try (DatabaseConnection dc = dd.connect(options.getDatabaseProperties())) {
1.479 + succesfullyConnected = dc.test();
1.480 + productName = dc.getProductName();
1.481 + productVersion = dc.getProductVersion();
1.482 + }
1.483 + log.log(Level.FINE, "Database connection test was successful");
1.484 + } catch (ConfigurationException | SQLException | RuntimeException e) {
1.485 + log.log(Level.SEVERE, "Error during testing connection " + dbName, e);
1.486 + }
1.487 +
1.488 + return new Object[]{dbName, succesfullyConfigured, succesfullyConnected, productName, productVersion};
1.489 + }
1.490 +
1.491 + private void printResource(String fileName) {
1.492 + try (BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream(fileName)))) {
1.493 + while (true) {
1.494 + String line = reader.readLine();
1.495 + if (line == null) {
1.496 + break;
1.497 + } else {
1.498 + println(line);
1.499 + }
1.500 + }
1.501 + } catch (Exception e) {
1.502 + log.log(Level.SEVERE, "Unable to print this info. Please see our website for it: " + Constants.WEBSITE, e);
1.503 + }
1.504 + }
1.505 +
1.506 + private void println(String line) {
1.507 + out.println(line);
1.508 + }
1.509 +
1.510 + private void printTable(Formatter formatter, ColumnsHeader header, String sql, List<Parameter> parameters, List<Object[]> data) throws ConfigurationException, FormatterException {
1.511 + printTable(formatter, header, sql, parameters, data, null);
1.512 + }
1.513 +
1.514 + private void printTable(Formatter formatter, ColumnsHeader header, String sql, List<Parameter> parameters, List<Object[]> data, final Integer sortByColumn) throws ConfigurationException, FormatterException {
1.515 + printHeader(formatter, header, sql, parameters);
1.516 +
1.517 + if (sortByColumn != null) {
1.518 + Collections.sort(data, new Comparator<Object[]>() {
1.519 +
1.520 + @Override
1.521 + public int compare(Object[] o1, Object[] o2) {
1.522 + String s1 = String.valueOf(o1[sortByColumn]);
1.523 + String s2 = String.valueOf(o2[sortByColumn]);
1.524 + return s1.compareTo(s2);
1.525 + }
1.526 + });
1.527 + }
1.528 +
1.529 + for (Object[] row : data) {
1.530 + printRow(formatter, row);
1.531 + }
1.532 +
1.533 + printFooter(formatter);
1.534 + }
1.535 +
1.536 + private void printHeader(Formatter formatter, ColumnsHeader header, String sql, List<Parameter> parameters) {
1.537 + formatter.writeStartStatement();
1.538 + if (sql != null) {
1.539 + formatter.writeQuery(sql);
1.540 + if (parameters != null) {
1.541 + formatter.writeParameters(parameters);
1.542 + }
1.543 + }
1.544 + formatter.writeStartResultSet(header);
1.545 + }
1.546 +
1.547 + private void printRow(Formatter formatter, Object[] row) {
1.548 + formatter.writeStartRow();
1.549 + for (Object cell : row) {
1.550 + formatter.writeColumnValue(cell);
1.551 + }
1.552 + formatter.writeEndRow();
1.553 + }
1.554 +
1.555 + private void printFooter(Formatter formatter) {
1.556 + formatter.writeEndResultSet();
1.557 + formatter.writeEndStatement();
1.558 + }
1.559 +
1.560 + private Formatter getFormatter() throws ConfigurationException, FormatterException {
1.561 + String formatterName = options.getFormatterName();
1.562 + formatterName = formatterName == null ? Configuration.DEFAULT_FORMATTER_PREFETCHING : formatterName;
1.563 + FormatterDefinition fd = configurationProvider.getConfiguration().getFormatter(formatterName);
1.564 + FormatterContext context = new FormatterContext(out, options.getFormatterProperties());
1.565 + return fd.getInstance(context);
1.566 + }
1.567 +
1.568 + private ColumnsHeader constructHeader(HeaderField... fields) throws FormatterException {
1.569 + try {
1.570 + RowSetMetaDataImpl metaData = new RowSetMetaDataImpl();
1.571 + metaData.setColumnCount(fields.length);
1.572 +
1.573 + for (int i = 0; i < fields.length; i++) {
1.574 + HeaderField hf = fields[i];
1.575 + int sqlIndex = i + 1;
1.576 + metaData.setColumnName(sqlIndex, hf.name);
1.577 + metaData.setColumnLabel(sqlIndex, hf.name);
1.578 + metaData.setColumnType(sqlIndex, hf.type.getCode());
1.579 + metaData.setColumnTypeName(sqlIndex, hf.type.name());
1.580 + }
1.581 +
1.582 + return new ColumnsHeader(metaData);
1.583 + } catch (SQLException e) {
1.584 + throw new FormatterException("Error while constructing table headers", e);
1.585 + }
1.586 + }
1.587 +
1.588 + private static class HeaderField {
1.589 +
1.590 + String name;
1.591 + SQLType type;
1.592 +
1.593 + public HeaderField(String name, SQLType type) {
1.594 + this.name = name;
1.595 + this.type = type;
1.596 + }
1.597 + }
1.598 +
1.599 + public enum InfoType {
1.600 +
1.601 + HELP {
1.602 + @Override
1.603 + public void showInfo(InfoLister infoLister) {
1.604 + infoLister.printResource(Constants.HELP_FILE);
1.605 + }
1.606 + },
1.607 + VERSION {
1.608 + @Override
1.609 + public void showInfo(InfoLister infoLister) {
1.610 + infoLister.printResource(Constants.VERSION_FILE);
1.611 + }
1.612 + },
1.613 + LICENSE {
1.614 + @Override
1.615 + public void showInfo(InfoLister infoLister) {
1.616 + infoLister.printResource(Constants.LICENSE_FILE);
1.617 + }
1.618 + },
1.619 + JAVA_PROPERTIES {
1.620 + @Override
1.621 + public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
1.622 + infoLister.listJavaProperties();
1.623 + }
1.624 + },
1.625 + ENVIRONMENT_VARIABLES {
1.626 + @Override
1.627 + public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
1.628 + infoLister.listEnvironmentVariables();
1.629 + }
1.630 + },
1.631 + FORMATTERS {
1.632 + @Override
1.633 + public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
1.634 + infoLister.listFormatters();
1.635 + }
1.636 + },
1.637 + FORMATTER_PROPERTIES {
1.638 + @Override
1.639 + public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
1.640 + infoLister.listFormatterProperties();
1.641 + }
1.642 + },
1.643 + TYPES {
1.644 + @Override
1.645 + public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
1.646 + infoLister.listTypes();
1.647 + }
1.648 + },
1.649 + JDBC_DRIVERS {
1.650 + @Override
1.651 + public void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException {
1.652 + infoLister.listJdbcDrivers();
1.653 + }
1.654 + },
1.655 + JDBC_PROPERTIES {
1.656 + @Override
1.657 + public void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException {
1.658 + infoLister.listJdbcProperties();
1.659 + }
1.660 + },
1.661 + DATABASES {
1.662 + @Override
1.663 + public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
1.664 + infoLister.listDatabases();
1.665 + }
1.666 + },
1.667 + CONNECTION {
1.668 + @Override
1.669 + public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
1.670 + infoLister.testConnections();
1.671 + }
1.672 + };
1.673 +
1.674 + public abstract void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException;
1.675 + }
1.676 +}