parallelized DB connection testing v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sat, 06 Dec 2014 14:38:41 +0100
branchv_0
changeset 183 1bb5abfb0655
parent 182 4ff7a273f3e8
child 184 53fb05ce504c
parallelized DB connection testing
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<Object[]> data = new ArrayList<>();
+
+		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 (String dbName : options.getDatabaseNamesToTest()) {
-			data.add(testConnection(dbName));
+		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<Object[]> data, String sql, List<Parameter> parameters) throws ConfigurationException, FormatterException {
+	private void printTable(Formatter formatter, ColumnsHeader header, String sql, List<Parameter> parameters, List<Object[]> 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<Parameter> parameters) {
 		formatter.writeStartStatement();
-
 		if (sql != null) {
 			formatter.writeQuery(sql);
 			if (parameters != null) {
 				formatter.writeParameters(parameters);
 			}
 		}
-
 		formatter.writeStartResultSet(header);
+	}
 
-		for (Object[] row : data) {
-			formatter.writeStartRow();
-			for (Object cell : row) {
-				formatter.writeColumnValue(cell);
-			}
-			formatter.writeEndRow();
+	private void printRow(Formatter formatter, Object[] row) {
+		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;
 	}