# HG changeset patch # User František Kučera # Date 1417873121 -3600 # Node ID 1bb5abfb0655c2ba6e3c4e5e0f8a8da6f7149341 # Parent 4ff7a273f3e8a8688103a453afcaf1112fffb875 parallelized DB connection testing diff -r 4ff7a273f3e8 -r 1bb5abfb0655 java/sql-dk/src/info/globalcode/sql/dk/InfoLister.java --- a/java/sql-dk/src/info/globalcode/sql/dk/InfoLister.java Sat Dec 06 14:12:59 2014 +0100 +++ b/java/sql-dk/src/info/globalcode/sql/dk/InfoLister.java Sat Dec 06 14:38:41 2014 +0100 @@ -43,6 +43,9 @@ import java.util.List; import java.util.ServiceLoader; import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import javax.sql.rowset.RowSetMetaDataImpl; @@ -59,9 +62,9 @@ * Fake database name for output formatting */ public static final String CONFIG_DB_NAME = "sqldk_configuration"; - private PrintStream out; - private ConfigurationProvider configurationProvider; - private CLIOptions options; + private final PrintStream out; + private final ConfigurationProvider configurationProvider; + private final CLIOptions options; private Formatter formatter; public InfoLister(PrintStream out, ConfigurationProvider configurationProvider, CLIOptions options) { @@ -131,7 +134,7 @@ data.add(new Object[]{fd.getName(), false, defaultFormatter.equals(fd.getName()), fd.getClassName(), isInstantiable(fd)}); } - printTable(formatter, header, data, "-- configured and built-in output formatters", null); + printTable(formatter, header, "-- configured and built-in output formatters", null, data); } private boolean isInstantiable(FormatterDefinition fd) { @@ -152,7 +155,7 @@ for (SQLType sqlType : SQLType.values()) { data.add(new Object[]{sqlType.name(), sqlType.getCode()}); } - printTable(formatter, header, data, "-- data types", null); + printTable(formatter, header, "-- data types", null, data); log.log(Level.INFO, "Type names in --types option are case insensitive"); } @@ -172,7 +175,7 @@ } } - printTable(formatter, header, data, "-- configured databases", null); + printTable(formatter, header, "-- configured databases", null, data); } public void listJdbcDrivers() throws FormatterException, ConfigurationException { @@ -195,7 +198,7 @@ }); } - printTable(formatter, header, data, "-- discovered JDBC drivers (available on the CLASSPATH)", null); + printTable(formatter, header, "-- discovered JDBC drivers (available on the CLASSPATH)", null, data); } public void listJdbcProperties() throws FormatterException, ConfigurationException { @@ -257,7 +260,7 @@ 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); + printTable(formatter, header, "-- configured and configurable JDBC driver properties", parameters, data); } } @@ -277,18 +280,59 @@ return null; } - public void testConnection() throws FormatterException, ConfigurationException { + /** + * Parallelism for connection testing – maximum concurrent database connections. + */ + private static final int TESTING_THREAD_COUNT = 64; + /** + * Time limit for all connection testing threads – particular timeouts per connection will be + * much smaller. + */ + private static final long TESTING_AWAIT_LIMIT = 1; + private static final TimeUnit TESTING_AWAIT_UNIT = TimeUnit.DAYS; + + public void testConnections() throws FormatterException, ConfigurationException { ColumnsHeader header = constructHeader( new HeaderField("database_name", SQLType.VARCHAR), new HeaderField("configured", SQLType.BOOLEAN), new HeaderField("connected", SQLType.BOOLEAN)); - List data = new ArrayList<>(); - for (String dbName : options.getDatabaseNamesToTest()) { - data.add(testConnection(dbName)); + log.log(Level.FINE, "Testing DB connections in {0} threads", TESTING_THREAD_COUNT); + + ExecutorService es = Executors.newFixedThreadPool(TESTING_THREAD_COUNT); + + final Formatter currentFormatter = formatter; + + printHeader(currentFormatter, header, "-- database configuration and connectivity test", null); + + for (final String dbName : options.getDatabaseNamesToTest()) { + es.submit(new Runnable() { + // TODO: Java 8 – lambda + @Override + public void run() { + final Object[] row = testConnection(dbName); + synchronized (currentFormatter) { + printRow(currentFormatter, row); + } + } + }); } - printTable(formatter, header, data, "-- database configuration and connectivity test", null); + es.shutdown(); + + try { + log.log(Level.FINEST, "Waiting for test results: {0} {1}", new Object[]{TESTING_AWAIT_LIMIT, TESTING_AWAIT_UNIT.name()}); + boolean finished = es.awaitTermination(TESTING_AWAIT_LIMIT, TESTING_AWAIT_UNIT); + if (finished) { + log.log(Level.FINEST, "All testing threads finished in time limit."); + } else { + throw new FormatterException("Exceeded total time limit for test threads – this should never happen"); + } + } catch (InterruptedException e) { + throw new FormatterException("Interrupted while waiting for test results", e); + } + + printFooter(currentFormatter); } public Object[] testConnection(String dbName) { @@ -306,7 +350,7 @@ } log.log(Level.FINE, "Database connection test was successful"); } catch (ConfigurationException | SQLException e) { - log.log(Level.SEVERE, "Error during testing connection", e); + log.log(Level.SEVERE, "Error during testing connection " + dbName, e); } return new Object[]{dbName, succesfullyConfigured, succesfullyConnected}; @@ -331,26 +375,36 @@ out.println(line); } - private void printTable(Formatter formatter, ColumnsHeader header, List data, String sql, List parameters) throws ConfigurationException, FormatterException { + private void printTable(Formatter formatter, ColumnsHeader header, String sql, List parameters, List data) throws ConfigurationException, FormatterException { + printHeader(formatter, header, sql, parameters); + + for (Object[] row : data) { + printRow(formatter, row); + } + + printFooter(formatter); + } + + private void printHeader(Formatter formatter, ColumnsHeader header, String sql, List parameters) { formatter.writeStartStatement(); - if (sql != null) { formatter.writeQuery(sql); if (parameters != null) { formatter.writeParameters(parameters); } } + formatter.writeStartResultSet(header); + } - formatter.writeStartResultSet(header); + private void printRow(Formatter formatter, Object[] row) { + formatter.writeStartRow(); + for (Object cell : row) { + formatter.writeColumnValue(cell); + } + formatter.writeEndRow(); + } - for (Object[] row : data) { - formatter.writeStartRow(); - for (Object cell : row) { - formatter.writeColumnValue(cell); - } - formatter.writeEndRow(); - } - + private void printFooter(Formatter formatter) { formatter.writeEndResultSet(); formatter.writeEndStatement(); } @@ -397,59 +451,59 @@ public enum InfoType { HELP { - @Override - public void showInfo(InfoLister infoLister) { - infoLister.printResource(Constants.HELP_FILE); - } - }, + @Override + public void showInfo(InfoLister infoLister) { + infoLister.printResource(Constants.HELP_FILE); + } + }, VERSION { - @Override - public void showInfo(InfoLister infoLister) { - infoLister.printResource(Constants.VERSION_FILE); - } - }, + @Override + public void showInfo(InfoLister infoLister) { + infoLister.printResource(Constants.VERSION_FILE); + } + }, LICENSE { - @Override - public void showInfo(InfoLister infoLister) { - infoLister.printResource(Constants.LICENSE_FILE); - } - }, + @Override + public void showInfo(InfoLister infoLister) { + infoLister.printResource(Constants.LICENSE_FILE); + } + }, FORMATTERS { - @Override - public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException { - infoLister.listFormatters(); - } - }, + @Override + public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException { + infoLister.listFormatters(); + } + }, TYPES { - @Override - public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException { - infoLister.listTypes(); - } - }, + @Override + public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException { + infoLister.listTypes(); + } + }, JDBC_DRIVERS { - @Override - public void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException { - infoLister.listJdbcDrivers(); - } - }, + @Override + public void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException { + infoLister.listJdbcDrivers(); + } + }, JDBC_PROPERTIES { - @Override - public void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException { - infoLister.listJdbcProperties(); - } - }, + @Override + public void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException { + infoLister.listJdbcProperties(); + } + }, DATABASES { - @Override - public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException { - infoLister.listDatabases(); - } - }, + @Override + public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException { + infoLister.listDatabases(); + } + }, CONNECTION { - @Override - public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException { - infoLister.testConnection(); - } - }; + @Override + public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException { + infoLister.testConnections(); + } + }; public abstract void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException; }