Basic JMX management/reporting – counters for commands and records v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Thu, 25 Sep 2014 17:50:40 +0200
branchv_0
changeset 179236332caeb29
parent 178 5a5fc66f11b1
child 180 74a6d55da11c
Basic JMX management/reporting – counters for commands and records
distributions/debian/build.sh
java/sql-dk/src/info/globalcode/sql/dk/CLIStarter.java
java/sql-dk/src/info/globalcode/sql/dk/DatabaseConnection.java
java/sql-dk/src/info/globalcode/sql/dk/configuration/DatabaseDefinition.java
java/sql-dk/src/info/globalcode/sql/dk/jmx/ConnectionManagement.java
java/sql-dk/src/info/globalcode/sql/dk/jmx/ConnectionManagementMBean.java
java/sql-dk/src/info/globalcode/sql/dk/jmx/ManagementUtils.java
     1.1 --- a/distributions/debian/build.sh	Wed Sep 24 22:53:30 2014 +0200
     1.2 +++ b/distributions/debian/build.sh	Thu Sep 25 17:50:40 2014 +0200
     1.3 @@ -41,7 +41,7 @@
     1.4  CONTROL_FILE="equivs-control" &&
     1.5  COPYRIGHT_FILE="copyright" &&
     1.6  URL="https://sql-dk.globalcode.info/" &&
     1.7 -VERSION="0.9" &&
     1.8 +VERSION="0.10" &&
     1.9  
    1.10  echo "Section: database
    1.11  Priority: optional
     2.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/CLIStarter.java	Wed Sep 24 22:53:30 2014 +0200
     2.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/CLIStarter.java	Thu Sep 25 17:50:40 2014 +0200
     2.3 @@ -31,6 +31,8 @@
     2.4  import info.globalcode.sql.dk.formatting.Formatter;
     2.5  import info.globalcode.sql.dk.formatting.FormatterContext;
     2.6  import info.globalcode.sql.dk.formatting.FormatterException;
     2.7 +import info.globalcode.sql.dk.jmx.ConnectionManagement;
     2.8 +import info.globalcode.sql.dk.jmx.ManagementUtils;
     2.9  import java.io.File;
    2.10  import java.io.FileNotFoundException;
    2.11  import java.io.IOException;
    2.12 @@ -149,7 +151,9 @@
    2.13  	private void processQueryNow() throws ConfigurationException, SQLException, FormatterException {
    2.14  		DatabaseDefinition dd = getConfiguration().getDatabase(options.getDatabaseName());
    2.15  		FormatterDefinition fd = configuration.getFormatter(options.getFormatterName());
    2.16 -		try (DatabaseConnection c = dd.connect(options.getDatabaseProperties())) {
    2.17 +		ConnectionManagement jmxBean = ManagementUtils.registerMBean(dd.getName());
    2.18 +
    2.19 +		try (DatabaseConnection c = dd.connect(options.getDatabaseProperties(), jmxBean)) {
    2.20  			log.log(Level.FINE, "Database connected");
    2.21  			try (Formatter f = fd.getInstance(new FormatterContext(options.getOutputStream(), options.getFormatterProperties()))) {
    2.22  				c.executeQuery(options.getSQLCommand(), f);
    2.23 @@ -169,7 +173,9 @@
    2.24  
    2.25  		DatabaseDefinition dd = getConfiguration().getDatabase(options.getDatabaseName());
    2.26  		FormatterDefinition fd = configuration.getFormatter(options.getFormatterName());
    2.27 -		try (DatabaseConnection c = dd.connect(options.getDatabaseProperties())) {
    2.28 +		ConnectionManagement jmxBean = ManagementUtils.registerMBean(dd.getName());
    2.29 +
    2.30 +		try (DatabaseConnection c = dd.connect(options.getDatabaseProperties(), jmxBean)) {
    2.31  			log.log(Level.FINE, "Database connected");
    2.32  			try (Formatter f = fd.getInstance(new FormatterContext(options.getOutputStream(), options.getFormatterProperties()))) {
    2.33  				c.executeBatch(b, f);
     3.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/DatabaseConnection.java	Wed Sep 24 22:53:30 2014 +0200
     3.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/DatabaseConnection.java	Thu Sep 25 17:50:40 2014 +0200
     3.3 @@ -17,6 +17,8 @@
     3.4   */
     3.5  package info.globalcode.sql.dk;
     3.6  
     3.7 +import static info.globalcode.sql.dk.jmx.ConnectionManagement.incrementCounter;
     3.8 +import static info.globalcode.sql.dk.jmx.ConnectionManagement.resetCounter;
     3.9  import info.globalcode.sql.dk.batch.Batch;
    3.10  import info.globalcode.sql.dk.batch.BatchException;
    3.11  import info.globalcode.sql.dk.configuration.DatabaseDefinition;
    3.12 @@ -24,6 +26,8 @@
    3.13  import info.globalcode.sql.dk.configuration.Property;
    3.14  import info.globalcode.sql.dk.formatting.ColumnsHeader;
    3.15  import info.globalcode.sql.dk.formatting.Formatter;
    3.16 +import info.globalcode.sql.dk.jmx.ConnectionManagement;
    3.17 +import info.globalcode.sql.dk.jmx.ConnectionManagement.COUNTER;
    3.18  import java.sql.Connection;
    3.19  import java.sql.DriverManager;
    3.20  import java.sql.PreparedStatement;
    3.21 @@ -50,10 +54,23 @@
    3.22  	private final DatabaseDefinition databaseDefinition;
    3.23  	private final Connection connection;
    3.24  	private final Properties properties;
    3.25 +	/**
    3.26 +	 * Could be null = JMX is disabled → must check, see functions in
    3.27 +	 * {@linkplain ConnectionManagement}
    3.28 +	 */
    3.29 +	private final ConnectionManagement connectionMBean;
    3.30  
    3.31 -	public DatabaseConnection(DatabaseDefinition databaseDefinition, Properties properties) throws SQLException {
    3.32 +	/**
    3.33 +	 *
    3.34 +	 * @param databaseDefinition DB url, name, password etc.
    3.35 +	 * @param properties additional properties from CLI
    3.36 +	 * @param connectionMBean JMX management bean | null = disabled JMX reporting
    3.37 +	 * @throws SQLException
    3.38 +	 */
    3.39 +	public DatabaseConnection(DatabaseDefinition databaseDefinition, Properties properties, ConnectionManagement connectionMBean) throws SQLException {
    3.40  		this.databaseDefinition = databaseDefinition;
    3.41  		this.properties = properties;
    3.42 +		this.connectionMBean = connectionMBean;
    3.43  
    3.44  		if (properties.hasProperty(JDBC_PROPERTY_PASSWORD)) {
    3.45  			log.log(Level.WARNING, "Passing DB password as CLI parameter is insecure!");
    3.46 @@ -97,6 +114,9 @@
    3.47  	}
    3.48  
    3.49  	private void processCommand(SQLCommand sqlCommand, Formatter formatter) throws SQLException {
    3.50 +		incrementCounter(connectionMBean, COUNTER.COMMAND);
    3.51 +		resetCounter(connectionMBean, COUNTER.RECORD_CURRENT);
    3.52 +		
    3.53  		try (PreparedStatement ps = sqlCommand.prepareStatement(connection)) {
    3.54  			log.log(Level.FINE, "Statement prepared");
    3.55  			sqlCommand.parametrize(ps);
    3.56 @@ -135,6 +155,9 @@
    3.57  		int columnCount = rs.getMetaData().getColumnCount();
    3.58  
    3.59  		while (rs.next()) {
    3.60 +			incrementCounter(connectionMBean, COUNTER.RECORD_CURRENT);
    3.61 +			incrementCounter(connectionMBean, COUNTER.RECORD_TOTAL);
    3.62 +			
    3.63  			formatter.writeStartRow();
    3.64  
    3.65  			for (int i = 1; i <= columnCount; i++) {
     4.1 --- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/DatabaseDefinition.java	Wed Sep 24 22:53:30 2014 +0200
     4.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/configuration/DatabaseDefinition.java	Thu Sep 25 17:50:40 2014 +0200
     4.3 @@ -19,7 +19,14 @@
     4.4  
     4.5  import static info.globalcode.sql.dk.Xmlns.CONFIGURATION;
     4.6  import info.globalcode.sql.dk.DatabaseConnection;
     4.7 +import info.globalcode.sql.dk.jmx.ConnectionManagement;
     4.8 +import java.lang.management.ManagementFactory;
     4.9  import java.sql.SQLException;
    4.10 +import java.util.Hashtable;
    4.11 +import java.util.logging.Level;
    4.12 +import java.util.logging.Logger;
    4.13 +import javax.management.MBeanServer;
    4.14 +import javax.management.ObjectName;
    4.15  import javax.xml.bind.annotation.XmlElement;
    4.16  
    4.17  /**
    4.18 @@ -29,6 +36,7 @@
    4.19   */
    4.20  public class DatabaseDefinition implements NameIdentified {
    4.21  
    4.22 +	private static final Logger log = Logger.getLogger(DatabaseDefinition.class.getName());
    4.23  	private String name;
    4.24  	private String url;
    4.25  	private String userName;
    4.26 @@ -83,8 +91,17 @@
    4.27  
    4.28  	/**
    4.29  	 * @param properties ad-hoc properties from CLI options (for the JDBC driver)
    4.30 +	 * @param jmxBean JMX management bean for progress reporting | null = disable JMX
    4.31 +	 */
    4.32 +	public DatabaseConnection connect(Properties properties, ConnectionManagement jmxBean) throws SQLException {
    4.33 +		return new DatabaseConnection(this, properties, jmxBean);
    4.34 +	}
    4.35 +
    4.36 +	/**
    4.37 +	 * @see #connect(info.globalcode.sql.dk.configuration.Properties, java.lang.String)
    4.38 +	 * With disabled JMX reporting.
    4.39  	 */
    4.40  	public DatabaseConnection connect(Properties properties) throws SQLException {
    4.41 -		return new DatabaseConnection(this, properties);
    4.42 +		return new DatabaseConnection(this, properties, null);
    4.43  	}
    4.44  }
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/jmx/ConnectionManagement.java	Thu Sep 25 17:50:40 2014 +0200
     5.3 @@ -0,0 +1,97 @@
     5.4 +/**
     5.5 + * SQL-DK
     5.6 + * Copyright © 2014 František Kučera (frantovo.cz)
     5.7 + *
     5.8 + * This program is free software: you can redistribute it and/or modify
     5.9 + * it under the terms of the GNU General Public License as published by
    5.10 + * the Free Software Foundation, either version 3 of the License, or
    5.11 + * (at your option) any later version.
    5.12 + *
    5.13 + * This program is distributed in the hope that it will be useful,
    5.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    5.16 + * GNU General Public License for more details.
    5.17 + *
    5.18 + * You should have received a copy of the GNU General Public License
    5.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
    5.20 + */
    5.21 +package info.globalcode.sql.dk.jmx;
    5.22 +
    5.23 +import java.util.EnumMap;
    5.24 +import java.util.Map;
    5.25 +
    5.26 +/**
    5.27 + * JMX management bean for progress reporting.
    5.28 + *
    5.29 + * @author Ing. František Kučera (frantovo.cz)
    5.30 + */
    5.31 +public class ConnectionManagement implements ConnectionManagementMBean {
    5.32 +
    5.33 +	private final String databaseName;
    5.34 +	private final Map<COUNTER, Integer> counters = new EnumMap(COUNTER.class);
    5.35 +
    5.36 +	public ConnectionManagement(String databaseName) {
    5.37 +		this.databaseName = databaseName;
    5.38 +		for (COUNTER c : COUNTER.values()) {
    5.39 +			counters.put(c, 0);
    5.40 +		}
    5.41 +	}
    5.42 +
    5.43 +	public enum COUNTER {
    5.44 +
    5.45 +		COMMAND,
    5.46 +		RECORD_CURRENT,
    5.47 +		RECORD_TOTAL
    5.48 +	};
    5.49 +
    5.50 +	public void incrementCounter(COUNTER counter) {
    5.51 +		synchronized (counters) {
    5.52 +			int old = counters.get(counter);
    5.53 +			counters.put(counter, old + 1);
    5.54 +		}
    5.55 +	}
    5.56 +
    5.57 +	public void resetCounter(COUNTER counter) {
    5.58 +		synchronized (counters) {
    5.59 +			counters.put(counter, 0);
    5.60 +		}
    5.61 +	}
    5.62 +
    5.63 +	public static void incrementCounter(ConnectionManagement mbean, COUNTER counter) {
    5.64 +		if (mbean != null) {
    5.65 +			mbean.incrementCounter(counter);
    5.66 +		}
    5.67 +	}
    5.68 +
    5.69 +	public static void resetCounter(ConnectionManagement mbean, COUNTER counter) {
    5.70 +		if (mbean != null) {
    5.71 +			mbean.resetCounter(counter);
    5.72 +		}
    5.73 +	}
    5.74 +
    5.75 +	@Override
    5.76 +	public String getDatabaseName() {
    5.77 +		return databaseName;
    5.78 +	}
    5.79 +
    5.80 +	@Override
    5.81 +	public int getCommandCount() {
    5.82 +		synchronized (counters) {
    5.83 +			return counters.get(COUNTER.COMMAND);
    5.84 +		}
    5.85 +	}
    5.86 +
    5.87 +	@Override
    5.88 +	public int getCurrentRecordCount() {
    5.89 +		synchronized (counters) {
    5.90 +			return counters.get(COUNTER.RECORD_CURRENT);
    5.91 +		}
    5.92 +	}
    5.93 +	
    5.94 +	@Override
    5.95 +	public int getTotalRecordCount() {
    5.96 +		synchronized (counters) {
    5.97 +			return counters.get(COUNTER.RECORD_TOTAL);
    5.98 +		}
    5.99 +	}
   5.100 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/jmx/ConnectionManagementMBean.java	Thu Sep 25 17:50:40 2014 +0200
     6.3 @@ -0,0 +1,34 @@
     6.4 +/**
     6.5 + * SQL-DK
     6.6 + * Copyright © 2014 František Kučera (frantovo.cz)
     6.7 + *
     6.8 + * This program is free software: you can redistribute it and/or modify
     6.9 + * it under the terms of the GNU General Public License as published by
    6.10 + * the Free Software Foundation, either version 3 of the License, or
    6.11 + * (at your option) any later version.
    6.12 + *
    6.13 + * This program is distributed in the hope that it will be useful,
    6.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    6.16 + * GNU General Public License for more details.
    6.17 + *
    6.18 + * You should have received a copy of the GNU General Public License
    6.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
    6.20 + */
    6.21 +package info.globalcode.sql.dk.jmx;
    6.22 +
    6.23 +/**
    6.24 + *
    6.25 + * @author Ing. František Kučera (frantovo.cz)
    6.26 + */
    6.27 +public interface ConnectionManagementMBean {
    6.28 +
    6.29 +	public String getDatabaseName();
    6.30 +
    6.31 +	public int getCommandCount();
    6.32 +
    6.33 +	public int getCurrentRecordCount();
    6.34 +
    6.35 +	public int getTotalRecordCount();
    6.36 +
    6.37 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/java/sql-dk/src/info/globalcode/sql/dk/jmx/ManagementUtils.java	Thu Sep 25 17:50:40 2014 +0200
     7.3 @@ -0,0 +1,68 @@
     7.4 +/**
     7.5 + * SQL-DK
     7.6 + * Copyright © 2014 František Kučera (frantovo.cz)
     7.7 + *
     7.8 + * This program is free software: you can redistribute it and/or modify
     7.9 + * it under the terms of the GNU General Public License as published by
    7.10 + * the Free Software Foundation, either version 3 of the License, or
    7.11 + * (at your option) any later version.
    7.12 + *
    7.13 + * This program is distributed in the hope that it will be useful,
    7.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    7.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    7.16 + * GNU General Public License for more details.
    7.17 + *
    7.18 + * You should have received a copy of the GNU General Public License
    7.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
    7.20 + */
    7.21 +package info.globalcode.sql.dk.jmx;
    7.22 +
    7.23 +import java.lang.management.ManagementFactory;
    7.24 +import java.util.Hashtable;
    7.25 +import java.util.logging.Level;
    7.26 +import java.util.logging.Logger;
    7.27 +import javax.management.MBeanServer;
    7.28 +import javax.management.ObjectName;
    7.29 +
    7.30 +/**
    7.31 + *
    7.32 + * @author Ing. František Kučera (frantovo.cz)
    7.33 + */
    7.34 +public class ManagementUtils {
    7.35 +
    7.36 +	private static final Logger log = Logger.getLogger(ManagementUtils.class.getName());
    7.37 +	public static final String DEFAULT_CONNECTION_JMX_NAME = "main";
    7.38 +
    7.39 +	/**
    7.40 +	 * @see #registerMBean(java.lang.String, java.lang.String) with default JMX name
    7.41 +	 */
    7.42 +	public static ConnectionManagement registerMBean(String dbName) {
    7.43 +		return registerMBean(dbName, DEFAULT_CONNECTION_JMX_NAME);
    7.44 +	}
    7.45 +
    7.46 +	/**
    7.47 +	 *
    7.48 +	 * @param dbName database name
    7.49 +	 * @param jmxName name of JMX bean
    7.50 +	 * @return registered JMX bean | or null if registration fails (should not)
    7.51 +	 */
    7.52 +	public static ConnectionManagement registerMBean(String dbName, String jmxName) {
    7.53 +		try {
    7.54 +			ConnectionManagement mbean = new ConnectionManagement(dbName);
    7.55 +			MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
    7.56 +			Hashtable<String, String> objectProperties = new Hashtable<>();
    7.57 +			objectProperties.put("type", "Connection");
    7.58 +			objectProperties.put("name", jmxName);
    7.59 +			ObjectName objectName = new ObjectName("info.globalcode.sql.dk", objectProperties);
    7.60 +			mbs.registerMBean(mbean, objectName);
    7.61 +			log.log(Level.FINE, "JMX MBean was registered as: {0}", objectName);
    7.62 +			return mbean;
    7.63 +		} catch (Exception e) {
    7.64 +			log.log(Level.WARNING, "Unable to register JMX MBean", e);
    7.65 +			return null;
    7.66 +		}
    7.67 +	}
    7.68 +
    7.69 +	private ManagementUtils() {
    7.70 +	}
    7.71 +}