# HG changeset patch # User František Kučera # Date 1411660240 -7200 # Node ID 236332caeb296ae23217e1d7af9800361690e969 # Parent 5a5fc66f11b140ba12d2d2c2ef72356335baf79a Basic JMX management/reporting – counters for commands and records diff -r 5a5fc66f11b1 -r 236332caeb29 distributions/debian/build.sh --- a/distributions/debian/build.sh Wed Sep 24 22:53:30 2014 +0200 +++ b/distributions/debian/build.sh Thu Sep 25 17:50:40 2014 +0200 @@ -41,7 +41,7 @@ CONTROL_FILE="equivs-control" && COPYRIGHT_FILE="copyright" && URL="https://sql-dk.globalcode.info/" && -VERSION="0.9" && +VERSION="0.10" && echo "Section: database Priority: optional diff -r 5a5fc66f11b1 -r 236332caeb29 java/sql-dk/src/info/globalcode/sql/dk/CLIStarter.java --- a/java/sql-dk/src/info/globalcode/sql/dk/CLIStarter.java Wed Sep 24 22:53:30 2014 +0200 +++ b/java/sql-dk/src/info/globalcode/sql/dk/CLIStarter.java Thu Sep 25 17:50:40 2014 +0200 @@ -31,6 +31,8 @@ import info.globalcode.sql.dk.formatting.Formatter; import info.globalcode.sql.dk.formatting.FormatterContext; import info.globalcode.sql.dk.formatting.FormatterException; +import info.globalcode.sql.dk.jmx.ConnectionManagement; +import info.globalcode.sql.dk.jmx.ManagementUtils; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -149,7 +151,9 @@ private void processQueryNow() throws ConfigurationException, SQLException, FormatterException { DatabaseDefinition dd = getConfiguration().getDatabase(options.getDatabaseName()); FormatterDefinition fd = configuration.getFormatter(options.getFormatterName()); - try (DatabaseConnection c = dd.connect(options.getDatabaseProperties())) { + ConnectionManagement jmxBean = ManagementUtils.registerMBean(dd.getName()); + + try (DatabaseConnection c = dd.connect(options.getDatabaseProperties(), jmxBean)) { log.log(Level.FINE, "Database connected"); try (Formatter f = fd.getInstance(new FormatterContext(options.getOutputStream(), options.getFormatterProperties()))) { c.executeQuery(options.getSQLCommand(), f); @@ -169,7 +173,9 @@ DatabaseDefinition dd = getConfiguration().getDatabase(options.getDatabaseName()); FormatterDefinition fd = configuration.getFormatter(options.getFormatterName()); - try (DatabaseConnection c = dd.connect(options.getDatabaseProperties())) { + ConnectionManagement jmxBean = ManagementUtils.registerMBean(dd.getName()); + + try (DatabaseConnection c = dd.connect(options.getDatabaseProperties(), jmxBean)) { log.log(Level.FINE, "Database connected"); try (Formatter f = fd.getInstance(new FormatterContext(options.getOutputStream(), options.getFormatterProperties()))) { c.executeBatch(b, f); diff -r 5a5fc66f11b1 -r 236332caeb29 java/sql-dk/src/info/globalcode/sql/dk/DatabaseConnection.java --- a/java/sql-dk/src/info/globalcode/sql/dk/DatabaseConnection.java Wed Sep 24 22:53:30 2014 +0200 +++ b/java/sql-dk/src/info/globalcode/sql/dk/DatabaseConnection.java Thu Sep 25 17:50:40 2014 +0200 @@ -17,6 +17,8 @@ */ package info.globalcode.sql.dk; +import static info.globalcode.sql.dk.jmx.ConnectionManagement.incrementCounter; +import static info.globalcode.sql.dk.jmx.ConnectionManagement.resetCounter; import info.globalcode.sql.dk.batch.Batch; import info.globalcode.sql.dk.batch.BatchException; import info.globalcode.sql.dk.configuration.DatabaseDefinition; @@ -24,6 +26,8 @@ import info.globalcode.sql.dk.configuration.Property; import info.globalcode.sql.dk.formatting.ColumnsHeader; import info.globalcode.sql.dk.formatting.Formatter; +import info.globalcode.sql.dk.jmx.ConnectionManagement; +import info.globalcode.sql.dk.jmx.ConnectionManagement.COUNTER; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; @@ -50,10 +54,23 @@ private final DatabaseDefinition databaseDefinition; private final Connection connection; private final Properties properties; + /** + * Could be null = JMX is disabled → must check, see functions in + * {@linkplain ConnectionManagement} + */ + private final ConnectionManagement connectionMBean; - public DatabaseConnection(DatabaseDefinition databaseDefinition, Properties properties) throws SQLException { + /** + * + * @param databaseDefinition DB url, name, password etc. + * @param properties additional properties from CLI + * @param connectionMBean JMX management bean | null = disabled JMX reporting + * @throws SQLException + */ + public DatabaseConnection(DatabaseDefinition databaseDefinition, Properties properties, ConnectionManagement connectionMBean) throws SQLException { this.databaseDefinition = databaseDefinition; this.properties = properties; + this.connectionMBean = connectionMBean; if (properties.hasProperty(JDBC_PROPERTY_PASSWORD)) { log.log(Level.WARNING, "Passing DB password as CLI parameter is insecure!"); @@ -97,6 +114,9 @@ } private void processCommand(SQLCommand sqlCommand, Formatter formatter) throws SQLException { + incrementCounter(connectionMBean, COUNTER.COMMAND); + resetCounter(connectionMBean, COUNTER.RECORD_CURRENT); + try (PreparedStatement ps = sqlCommand.prepareStatement(connection)) { log.log(Level.FINE, "Statement prepared"); sqlCommand.parametrize(ps); @@ -135,6 +155,9 @@ int columnCount = rs.getMetaData().getColumnCount(); while (rs.next()) { + incrementCounter(connectionMBean, COUNTER.RECORD_CURRENT); + incrementCounter(connectionMBean, COUNTER.RECORD_TOTAL); + formatter.writeStartRow(); for (int i = 1; i <= columnCount; i++) { diff -r 5a5fc66f11b1 -r 236332caeb29 java/sql-dk/src/info/globalcode/sql/dk/configuration/DatabaseDefinition.java --- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/DatabaseDefinition.java Wed Sep 24 22:53:30 2014 +0200 +++ b/java/sql-dk/src/info/globalcode/sql/dk/configuration/DatabaseDefinition.java Thu Sep 25 17:50:40 2014 +0200 @@ -19,7 +19,14 @@ import static info.globalcode.sql.dk.Xmlns.CONFIGURATION; import info.globalcode.sql.dk.DatabaseConnection; +import info.globalcode.sql.dk.jmx.ConnectionManagement; +import java.lang.management.ManagementFactory; import java.sql.SQLException; +import java.util.Hashtable; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.management.MBeanServer; +import javax.management.ObjectName; import javax.xml.bind.annotation.XmlElement; /** @@ -29,6 +36,7 @@ */ public class DatabaseDefinition implements NameIdentified { + private static final Logger log = Logger.getLogger(DatabaseDefinition.class.getName()); private String name; private String url; private String userName; @@ -83,8 +91,17 @@ /** * @param properties ad-hoc properties from CLI options (for the JDBC driver) + * @param jmxBean JMX management bean for progress reporting | null = disable JMX + */ + public DatabaseConnection connect(Properties properties, ConnectionManagement jmxBean) throws SQLException { + return new DatabaseConnection(this, properties, jmxBean); + } + + /** + * @see #connect(info.globalcode.sql.dk.configuration.Properties, java.lang.String) + * With disabled JMX reporting. */ public DatabaseConnection connect(Properties properties) throws SQLException { - return new DatabaseConnection(this, properties); + return new DatabaseConnection(this, properties, null); } } diff -r 5a5fc66f11b1 -r 236332caeb29 java/sql-dk/src/info/globalcode/sql/dk/jmx/ConnectionManagement.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/sql-dk/src/info/globalcode/sql/dk/jmx/ConnectionManagement.java Thu Sep 25 17:50:40 2014 +0200 @@ -0,0 +1,97 @@ +/** + * 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 . + */ +package info.globalcode.sql.dk.jmx; + +import java.util.EnumMap; +import java.util.Map; + +/** + * JMX management bean for progress reporting. + * + * @author Ing. František Kučera (frantovo.cz) + */ +public class ConnectionManagement implements ConnectionManagementMBean { + + private final String databaseName; + private final Map counters = new EnumMap(COUNTER.class); + + public ConnectionManagement(String databaseName) { + this.databaseName = databaseName; + for (COUNTER c : COUNTER.values()) { + counters.put(c, 0); + } + } + + public enum COUNTER { + + COMMAND, + RECORD_CURRENT, + RECORD_TOTAL + }; + + public void incrementCounter(COUNTER counter) { + synchronized (counters) { + int old = counters.get(counter); + counters.put(counter, old + 1); + } + } + + public void resetCounter(COUNTER counter) { + synchronized (counters) { + counters.put(counter, 0); + } + } + + public static void incrementCounter(ConnectionManagement mbean, COUNTER counter) { + if (mbean != null) { + mbean.incrementCounter(counter); + } + } + + public static void resetCounter(ConnectionManagement mbean, COUNTER counter) { + if (mbean != null) { + mbean.resetCounter(counter); + } + } + + @Override + public String getDatabaseName() { + return databaseName; + } + + @Override + public int getCommandCount() { + synchronized (counters) { + return counters.get(COUNTER.COMMAND); + } + } + + @Override + public int getCurrentRecordCount() { + synchronized (counters) { + return counters.get(COUNTER.RECORD_CURRENT); + } + } + + @Override + public int getTotalRecordCount() { + synchronized (counters) { + return counters.get(COUNTER.RECORD_TOTAL); + } + } +} diff -r 5a5fc66f11b1 -r 236332caeb29 java/sql-dk/src/info/globalcode/sql/dk/jmx/ConnectionManagementMBean.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/sql-dk/src/info/globalcode/sql/dk/jmx/ConnectionManagementMBean.java Thu Sep 25 17:50:40 2014 +0200 @@ -0,0 +1,34 @@ +/** + * 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 . + */ +package info.globalcode.sql.dk.jmx; + +/** + * + * @author Ing. František Kučera (frantovo.cz) + */ +public interface ConnectionManagementMBean { + + public String getDatabaseName(); + + public int getCommandCount(); + + public int getCurrentRecordCount(); + + public int getTotalRecordCount(); + +} diff -r 5a5fc66f11b1 -r 236332caeb29 java/sql-dk/src/info/globalcode/sql/dk/jmx/ManagementUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/sql-dk/src/info/globalcode/sql/dk/jmx/ManagementUtils.java Thu Sep 25 17:50:40 2014 +0200 @@ -0,0 +1,68 @@ +/** + * 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 . + */ +package info.globalcode.sql.dk.jmx; + +import java.lang.management.ManagementFactory; +import java.util.Hashtable; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +/** + * + * @author Ing. František Kučera (frantovo.cz) + */ +public class ManagementUtils { + + private static final Logger log = Logger.getLogger(ManagementUtils.class.getName()); + public static final String DEFAULT_CONNECTION_JMX_NAME = "main"; + + /** + * @see #registerMBean(java.lang.String, java.lang.String) with default JMX name + */ + public static ConnectionManagement registerMBean(String dbName) { + return registerMBean(dbName, DEFAULT_CONNECTION_JMX_NAME); + } + + /** + * + * @param dbName database name + * @param jmxName name of JMX bean + * @return registered JMX bean | or null if registration fails (should not) + */ + public static ConnectionManagement registerMBean(String dbName, String jmxName) { + try { + ConnectionManagement mbean = new ConnectionManagement(dbName); + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + Hashtable objectProperties = new Hashtable<>(); + objectProperties.put("type", "Connection"); + objectProperties.put("name", jmxName); + ObjectName objectName = new ObjectName("info.globalcode.sql.dk", objectProperties); + mbs.registerMBean(mbean, objectName); + log.log(Level.FINE, "JMX MBean was registered as: {0}", objectName); + return mbean; + } catch (Exception e) { + log.log(Level.WARNING, "Unable to register JMX MBean", e); + return null; + } + } + + private ManagementUtils() { + } +}