提交 2a362b6b authored 作者: Thomas Mueller's avatar Thomas Mueller

JMX (the Java management extension) is now supported. Issue 253.

上级 aec48950
......@@ -123,8 +123,8 @@ import org.h2.table.IndexColumn;
import org.h2.table.RangeTable;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.table.TableView;
import org.h2.table.TableFilter.TableFilterVisitor;
import org.h2.table.TableView;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.StatementBuilder;
......@@ -1089,7 +1089,7 @@ public class Parser {
String objectName;
if (column) {
// can't use readIdentifierWithSchema() because
// it wouldn't read schema.table.column correctly
// it would not read schema.table.column correctly
// if the db name is equal to the schema name
ArrayList<String> list = New.arrayList();
do {
......@@ -4371,6 +4371,10 @@ public class Parser {
readIfEqualOrTo();
read();
return new NoOperation(session);
} else if (readIf("JMX")) {
readIfEqualOrTo();
read();
return new NoOperation(session);
} else if (readIf("PAGE_SIZE")) {
readIfEqualOrTo();
read();
......
......@@ -200,12 +200,7 @@ public class Set extends Prepared {
break;
case SetTypes.LOG: {
int value = getIntValue();
if (value < 0 || value > 2) {
throw DbException.getInvalidValueException("LOG", getIntValue());
}
if (value == 0) {
session.getUser().checkAdmin();
}
session.getUser().checkAdmin();
database.setLogMode(value);
break;
}
......
......@@ -84,7 +84,7 @@ public class ConnectionInfo implements Cloneable {
String[] connectionTime = { "ACCESS_MODE_DATA", "AUTOCOMMIT", "CIPHER",
"CREATE", "CACHE_TYPE", "FILE_LOCK", "IGNORE_UNKNOWN_SETTINGS", "IFEXISTS",
"INIT", "PASSWORD", "RECOVER", "USER", "AUTO_SERVER",
"AUTO_RECONNECT", "OPEN_NEW", "PAGE_SIZE", "PASSWORD_HASH" };
"AUTO_RECONNECT", "OPEN_NEW", "PAGE_SIZE", "PASSWORD_HASH", "JMX" };
for (String key : connectionTime) {
if (SysProperties.CHECK && set.contains(key)) {
DbException.throwInternalError(key);
......@@ -338,7 +338,7 @@ public class ConnectionInfo implements Cloneable {
*
* @return the database name
*/
String getName() {
public String getName() {
if (persistent) {
if (nameNormalized == null) {
String suffix = Constants.SUFFIX_PAGE_FILE;
......
......@@ -2255,6 +2255,9 @@ public class Database implements DataHandler {
}
public void setLogMode(int log) {
if (log < 0 || log > 2) {
throw DbException.getInvalidValueException("LOG", log);
}
if (pageStore != null) {
if (log != PageStore.LOG_MODE_SYNC ||
pageStore.getLogMode() != PageStore.LOG_MODE_SYNC) {
......
......@@ -18,6 +18,7 @@ import org.h2.store.FileLock;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
/**
* The engine contains a map of all open databases.
......@@ -30,6 +31,7 @@ public class Engine implements SessionFactory {
private final HashMap<String, Database> databases = New.hashMap();
private volatile long wrongPasswordDelay = SysProperties.DELAY_WRONG_PASSWORD_MIN;
private boolean jmx;
public static Engine getInstance() {
return INSTANCE;
......@@ -95,6 +97,15 @@ public class Engine implements SessionFactory {
}
checkClustering(ci, database);
Session session = database.createSession(user);
if (ci.getProperty("JMX", false)) {
try {
Utils.callStaticMethod("org.h2.jmx.DatabaseInfo.registerMBean", ci, database);
} catch (Exception e) {
database.removeSession(session);
throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1, e, "JMX");
}
jmx = true;
}
return session;
}
}
......@@ -218,6 +229,13 @@ public class Engine implements SessionFactory {
* @param name the database name
*/
public void close(String name) {
if (jmx) {
try {
Utils.callStaticMethod("org.h2.jmx.DatabaseInfo.unregisterMBean", name);
} catch (Exception e) {
throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1, e, "JMX");
}
}
databases.remove(name);
}
......
......@@ -1069,11 +1069,19 @@ public class Session extends SessionWithState {
}
public Table[] getLocks() {
synchronized (database) {
Table[] list = new Table[locks.size()];
locks.toArray(list);
return list;
// copy the data without synchronizing
ArrayList<Table> copy = New.arrayList();
for (int i = 0; i < locks.size(); i++) {
try {
copy.add(locks.get(i));
} catch (Exception e) {
// ignore
break;
}
}
Table[] list = new Table[copy.size()];
copy.toArray(list);
return list;
}
/**
......
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.jmx;
import java.lang.management.ManagementFactory;
import java.sql.Timestamp;
import java.util.Map;
import java.util.TreeMap;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.h2.command.Command;
import org.h2.engine.ConnectionInfo;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.table.Table;
import org.h2.util.New;
/**
* The MBean implementation.
*
* @author <a href="mailto:edong@adconion.com">Eric Dong</a>
* @author Thomas Mueller
*/
public class DatabaseInfo implements DatabaseInfoMBean {
private static final Map<String, ObjectName> MBEANS = New.hashMap();
/** Database. */
private final Database database;
private DatabaseInfo(Database database) {
if (database == null) {
throw new IllegalArgumentException("Argument 'database' must not be null");
}
this.database = database;
}
/**
* Returns a JMX new ObjectName instance.
*
* @param name name of the MBean
* @return a new ObjectName instance
* @throws JMException if the ObjectName could not be created
*/
private static ObjectName getObjectName(String name) throws JMException {
name = name.replace(':', '_');
return new ObjectName("org.h2", "name", name);
}
/**
* Registers an MBean for the database.
*
* @param connectionInfo connection info
* @param database database
*/
public static void registerMBean(ConnectionInfo connectionInfo, Database database) throws JMException {
String name = connectionInfo.getName();
if (!MBEANS.containsKey(name)) {
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
String mbeanName = database.getShortName() + " (" + name + ")";
ObjectName mbeanObjectName = getObjectName(mbeanName);
MBEANS.put(name, mbeanObjectName);
DatabaseInfo info = new DatabaseInfo(database);
Object mbean = new DocumentedMBean(info, DatabaseInfoMBean.class);
mbeanServer.registerMBean(mbean, mbeanObjectName);
}
}
/**
* Unregisters the MBean for the database if one is registered.
*
* @param name database name
*/
public static void unregisterMBean(String name) throws Exception {
ObjectName mbeanObjectName = MBEANS.remove(name);
if (mbeanObjectName != null) {
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
mbeanServer.unregisterMBean(mbeanObjectName);
}
}
public boolean isExclusive() {
return database.getExclusiveSession() != null;
}
public boolean isReadOnly() {
return database.isReadOnly();
}
public String getMode() {
return database.getMode().getName();
}
public boolean isMultiThreaded() {
return database.isMultiThreaded();
}
public boolean isMvcc() {
return database.isMultiVersion();
}
public int getLogMode() {
return database.getLogMode();
}
public void setLogMode(int value) {
database.setLogMode(value);
}
public int getTraceLevel() {
return database.getTraceSystem().getLevelFile();
}
public void setTraceLevel(int level) {
database.getTraceSystem().setLevelFile(level);
}
public long getFileWriteCountTotal() {
return database.isPersistent() ? database.getPageStore().getWriteCountTotal() : 0L;
}
public long getFileWriteCount() {
return database.isPersistent() ? database.getPageStore().getWriteCount() : 0L;
}
public long getFileReadCount() {
return database.isPersistent() ? database.getPageStore().getReadCount() : 0L;
}
public long getFileSize() {
return database.isPersistent() ? (database.getPageStore().getPageCount() * database.getPageStore().getPageSize() / 1024) : 0;
}
public int getCacheSizeMax() {
return database.isPersistent() ? database.getPageStore().getCache().getMaxMemory() : 0;
}
public void setCacheSizeMax(int kb) {
if (database.isPersistent()) {
database.getPageStore().getCache().setMaxMemory(kb);
}
}
public int getCacheSize() {
return database.isPersistent() ? database.getPageStore().getCache().getMemory() : 0;
}
public String getVersion() {
return Constants.getFullVersion();
}
public String listSettings() {
StringBuilder buff = new StringBuilder();
for (Map.Entry<String, String> e : new TreeMap<String, String>(database.getSettings().getSettings()).entrySet()) {
buff.append(e.getKey()).append(" = ").append(e.getValue()).append('\n');
}
return buff.toString();
}
public String listSessions() {
StringBuilder buff = new StringBuilder();
for (Session session : database.getSessions(false)) {
buff.append("session id: ").append(session.getId());
buff.append(" user: ").append(session.getUser().getName()).append('\n');
buff.append("connected: ").append(new Timestamp(session.getSessionStart())).append('\n');
Command command = session.getCurrentCommand();
if (command != null) {
buff.append("statement: ").append(session.getCurrentCommand()).append('\n');
long commandStart = session.getCurrentCommandStart();
if (commandStart != 0) {
buff.append("started: ").append(new Timestamp(commandStart)).append('\n');
}
}
Table[] t = session.getLocks();
if (t.length > 0) {
for (Table table : session.getLocks()) {
if (table.isLockedExclusivelyBy(session)) {
buff.append("write lock on ");
} else {
buff.append("read lock on ");
}
buff.append(table.getSchema().getName()).append('.').append(table.getName()).append('\n');
}
}
buff.append('\n');
}
return buff.toString();
}
}
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.jmx;
/**
* Information and management operations for the given database.
* @h2.resource
*
* @author <a href="mailto:edong@adconion.com">Eric Dong</a>
* @author Thomas Mueller
*/
public interface DatabaseInfoMBean {
/**
* Is the database open in exclusive mode?
* @h2.resource
*
* @return true if the database is open in exclusive mode, false otherwise
*/
boolean isExclusive();
/**
* Is the database read-only?
* @h2.resource
*
* @return true if the database is read-only, false otherwise
*/
boolean isReadOnly();
/**
* The database compatibility mode (REGULAR if no compatibility mode is
* used).
* @h2.resource
*
* @return the database mode
*/
String getMode();
/**
* Is multi-threading enabled?
* @h2.resource
*
* @return true if multi-threading is enabled, false otherwise
*/
boolean isMultiThreaded();
/**
* Is MVCC (multi version concurrency) enabled?
* @h2.resource
*
* @return true if MVCC is enabled, false otherwise
*/
boolean isMvcc();
/**
* The transaction log mode (0 disabled, 1 without sync, 2 enabled).
* @h2.resource
*
* @return the transaction log mode
*/
int getLogMode();
/**
* Set the transaction log mode.
*
* @param value the new log mode
*/
void setLogMode(int value);
/**
* The number of write operations since the database was created.
* @h2.resource
*
* @return the total write count
*/
long getFileWriteCountTotal();
/**
* The number of write operations since the database was opened.
* @h2.resource
*
* @return the write count
*/
long getFileWriteCount();
/**
* The file read count since the database was opened.
* @h2.resource
*
* @return the read count
*/
long getFileReadCount();
/**
* The database file size in KB.
* @h2.resource
*
* @return the number of pages
*/
long getFileSize();
/**
* The maximum cache size in KB.
* @h2.resource
*
* @return the maximum size
*/
int getCacheSizeMax();
/**
* Change the maximum size.
*
* @param kb the cache size in KB.
*/
void setCacheSizeMax(int kb);
/**
* The current cache size in KB.
* @h2.resource
*
* @return the current size
*/
int getCacheSize();
/**
* The database version.
* @h2.resource
*
* @return the version
*/
String getVersion();
/**
* The trace level (0 disabled, 1 error, 2 info, 3 debug).
* @h2.resource
*
* @return the level
*/
int getTraceLevel();
/**
* Set the trace level.
*
* @param level the new value
*/
void setTraceLevel(int level);
/**
* List the database settings.
* @h2.resource
*
* @return the database settings
*/
String listSettings();
/**
* List sessions, including the queries that are in
* progress, and locked tables.
* @h2.resource
*
* @return information about the sessions
*/
String listSessions();
}
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.jmx;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Properties;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.NotCompliantMBeanException;
import javax.management.StandardMBean;
import org.h2.util.Utils;
/**
* An MBean that reads the documentation from a resource file.
*/
public class DocumentedMBean extends StandardMBean {
private final String interfaceName;
private Properties resources;
public <T> DocumentedMBean(T impl, Class<T> mbeanInterface) throws NotCompliantMBeanException {
super(impl, mbeanInterface);
this.interfaceName = impl.getClass().getName() + "MBean";
}
private Properties getResources() {
if (resources == null) {
resources = new Properties();
String resourceName = "/org/h2/res/javadoc.properties";
try {
byte[] buff = Utils.getResource(resourceName);
if (buff != null) {
resources.load(new ByteArrayInputStream(buff));
}
} catch (IOException e) {
// ignore
}
}
return resources;
}
@Override
protected String getDescription(MBeanInfo info) {
String s = getResources().getProperty(interfaceName);
return s == null ? super.getDescription(info) : s;
}
@Override
protected String getDescription(MBeanOperationInfo op) {
String s = getResources().getProperty(interfaceName + "." + op.getName());
return s == null ? super.getDescription(op) : s;
}
@Override
protected String getDescription(MBeanAttributeInfo info) {
String prefix = info.isIs() ? "is" : "get";
String s = getResources().getProperty(interfaceName + "." + prefix + info.getName());
return s == null ? super.getDescription(info) : s;
}
@Override
protected int getImpact(MBeanOperationInfo info) {
if (info.getName().startsWith("list")) {
return MBeanOperationInfo.INFO;
} else {
return MBeanOperationInfo.ACTION;
}
}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License, Version 1.0,
and under the Eclipse Public License, Version 1.0
(http://h2database.com/html/license.html).
Initial Developer: H2 Group
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" /><title>
Javadoc package documentation
</title></head><body style="font: 9pt/130% Tahoma, Arial, Helvetica, sans-serif; font-weight: normal;"><p>
Implementation of the Java Management Extension (JMX) features.
</p></body></html>
\ No newline at end of file
......@@ -126,6 +126,7 @@ import org.h2.test.unit.TestFileSystem;
import org.h2.test.unit.TestFtp;
import org.h2.test.unit.TestIntArray;
import org.h2.test.unit.TestIntIntHashMap;
import org.h2.test.unit.TestJmx;
import org.h2.test.unit.TestMathUtils;
import org.h2.test.unit.TestNetUtils;
import org.h2.test.unit.TestOldVersion;
......@@ -353,7 +354,7 @@ create table test(id identity, name varchar(100) default space(100));
delete from test;
documentation: review package and class level javadocs
documentation: rolling review at jaqu.html
documentation: rolling review at main.html
-------------
......@@ -647,6 +648,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestFileSystem().runTest(this);
new TestIntArray().runTest(this);
new TestIntIntHashMap().runTest(this);
new TestJmx().runTest(this);
new TestMathUtils().runTest(this);
new TestOldVersion().runTest(this);
new TestNetUtils().runTest(this);
......
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.unit;
import java.lang.management.ManagementFactory;
import java.sql.Connection;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Set;
import javax.management.Attribute;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.h2.test.TestBase;
import org.h2.util.New;
/**
* Tests the JMX feature.
*/
public class TestJmx extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
public void test() throws Exception {
HashMap<String, MBeanAttributeInfo> attrMap;
HashMap<String, MBeanOperationInfo> opMap;
String result;
MBeanInfo info;
ObjectName name;
Connection conn;
Statement stat;
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
conn = getConnection("mem:jmx;jmx=true");
stat = conn.createStatement();
name = new ObjectName("org.h2", "name", "JMX (mem_jmx)");
info = mbeanServer.getMBeanInfo(name);
assertEquals("0", mbeanServer.getAttribute(name, "CacheSizeMax").toString());
// cache size is ignored for in-memory databases
mbeanServer.setAttribute(name, new Attribute("CacheSizeMax", 1));
assertEquals("0", mbeanServer.getAttribute(name, "CacheSizeMax").toString());
assertEquals("0", mbeanServer.getAttribute(name, "CacheSize").toString());
assertEquals("false", mbeanServer.getAttribute(name, "Exclusive").toString());
assertEquals("0", mbeanServer.getAttribute(name, "FileSize").toString());
assertEquals("0", mbeanServer.getAttribute(name, "FileReadCount").toString());
assertEquals("0", mbeanServer.getAttribute(name, "FileWriteCount").toString());
assertEquals("0", mbeanServer.getAttribute(name, "FileWriteCountTotal").toString());
assertEquals("0", mbeanServer.getAttribute(name, "LogMode").toString());
// ignored for in-memory databases
mbeanServer.setAttribute(name, new Attribute("LogMode", 1));
assertEquals("0", mbeanServer.getAttribute(name, "LogMode").toString());
assertEquals("REGULAR", mbeanServer.getAttribute(name, "Mode").toString());
assertEquals("false", mbeanServer.getAttribute(name, "MultiThreaded").toString());
assertEquals("false", mbeanServer.getAttribute(name, "Mvcc").toString());
assertEquals("false", mbeanServer.getAttribute(name, "ReadOnly").toString());
assertEquals("1", mbeanServer.getAttribute(name, "TraceLevel").toString());
mbeanServer.setAttribute(name, new Attribute("TraceLevel", 0));
assertEquals("0", mbeanServer.getAttribute(name, "TraceLevel").toString());
assertTrue(mbeanServer.getAttribute(name, "Version").toString().startsWith("1."));
assertEquals(14, info.getAttributes().length);
result = mbeanServer.invoke(name, "listSettings", null, null).toString();
assertTrue(result.indexOf("ANALYZE_AUTO") >= 0);
conn.setAutoCommit(false);
stat.execute("create table test(id int)");
stat.execute("insert into test values(1)");
result = mbeanServer.invoke(name, "listSessions", null, null).toString();
assertTrue(result.indexOf("session id") >= 0);
assertTrue(result.indexOf("write lock") >= 0);
assertEquals(2, info.getOperations().length);
assertTrue(info.getDescription().indexOf("database") >= 0);
attrMap = New.hashMap();
for (MBeanAttributeInfo a : info.getAttributes()) {
attrMap.put(a.getName(), a);
}
assertTrue(attrMap.get("CacheSize").getDescription().indexOf("KB") >= 0);
opMap = New.hashMap();
for (MBeanOperationInfo o : info.getOperations()) {
opMap.put(o.getName(), o);
}
assertTrue(opMap.get("listSessions").getDescription().indexOf("lock") >= 0);
assertEquals(MBeanOperationInfo.INFO, opMap.get("listSessions").getImpact());
conn.close();
conn = getConnection("jmx;jmx=true");
stat = conn.createStatement();
name = new ObjectName("org.h2", "name", "JMX*");
Set<ObjectName> set = mbeanServer.queryNames(name, null);
name = set.iterator().next();
assertEquals("16384", mbeanServer.getAttribute(name, "CacheSizeMax").toString());
mbeanServer.setAttribute(name, new Attribute("CacheSizeMax", 1));
assertEquals("1", mbeanServer.getAttribute(name, "CacheSizeMax").toString());
assertTrue(0 < (Integer) mbeanServer.getAttribute(name, "CacheSize"));
assertTrue(0 < (Long) mbeanServer.getAttribute(name, "FileSize"));
assertTrue(0 < (Long) mbeanServer.getAttribute(name, "FileReadCount"));
assertTrue(0 < (Long) mbeanServer.getAttribute(name, "FileWriteCount"));
assertTrue(0 < (Long) mbeanServer.getAttribute(name, "FileWriteCountTotal"));
mbeanServer.setAttribute(name, new Attribute("LogMode", 0));
assertEquals("0", mbeanServer.getAttribute(name, "LogMode").toString());
conn.close();
}
}
......@@ -380,6 +380,7 @@ public class Build extends BuildBase {
exclude("temp/org/h2/java/*").
exclude("temp/org/h2/jdbcx/*").
exclude("temp/org/h2/jcr/*").
exclude("temp/org/h2/jmx/*").
exclude("temp/org/h2/mode/*").
exclude("temp/org/h2/samples/*").
exclude("temp/org/h2/server/*").
......@@ -546,7 +547,7 @@ public class Build extends BuildBase {
private void resources(boolean clientOnly, boolean basicOnly) {
if (!clientOnly) {
java("org.h2.build.doc.GenerateHelp", null);
javadoc("-sourcepath", "src/main", "org.h2.tools",
javadoc("-sourcepath", "src/main", "org.h2.tools", "org.h2.jmx",
"-docletpath", "bin" + File.pathSeparator + "temp",
"-doclet", "org.h2.build.doclet.ResourceDoclet");
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论