Webアプリを運用するときに、ストップレスで設定を変更したい事があると思いますが、そのためにDBを使うのは面倒ですし、パフォーマンスの影響もあります。オンメモリでキャッシュするという運用もありますが、どのみち設定用の管理画面が必要になります。これを作るのは面倒になりますよね。
JavaのJDK5からはJMX(Java Management eXtension)が標準で装備されています。これはシステム管理を目的としているAPIで、MBean(Management Bean)と呼ばれる管理情報を作成する為のAPIと、そのMBeanを管理するAPIで構成されています。
JMXはApache GeronimoやJBossのサーバ管理機能で使われています。身近なところではTomcatの管理にも使われています。Java本体でもJVMの管理の為に提供されていますが、このMBeanはjava.lang.managementパッケージで提供されています。
前置きが長くなりましたが、MBeanが登録されたMBeanServerを操作するために、SunのJDKではJConsoleと呼ばれるツールが付属しています。Webアプリの設定情報もMBeanで管理するようにすれば、JConsoleで設定変更する事ができるのです。これなら設定を管理する画面を作る必要がありません。
そこでJavaのプロパティを示すPropertiesクラスがありますが、それのMBean化と、利用例が以下のコードになります。
(前のブログでも書いたのですが、放置されているのでこちらにコードを持ってきます。。。)
まずMBean化されたPropertiesです。Propertiesを継承します。
import java.util.Properties;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.ReflectionException;
public class PropertiesMBean extends Properties implements DynamicMBean {
public PropertiesMBean() {
super();
}
public PropertiesMBean(Properties properties) {
super(properties);
}
public Object getAttribute(String attribute)
throws AttributeNotFoundException, MBeanException,
ReflectionException {
return this.get(attribute);
}
public AttributeList getAttributes(String[] attributes) {
AttributeList list = new AttributeList();
for (int i = 0; i < attributes.length; i++) {
Object value = null;
try {
value = getAttribute(attributes[i]);
} catch (Exception e) {
}
list.add(new Attribute(attributes[i], value));
}
return list;
}
public MBeanInfo getMBeanInfo() {
MBeanNotificationInfo[] notificationInfos = new MBeanNotificationInfo[0];
MBeanOperationInfo[] operationInfos = new MBeanOperationInfo[0];
MBeanConstructorInfo[] constructorInfos = new MBeanConstructorInfo[0];
MBeanAttributeInfo[] attributeInfos = new MBeanAttributeInfo[this
.size()];
int i = 0;
for (Object key : this.keySet()) {
attributeInfos[i++] = new MBeanAttributeInfo(key.toString(),
"java.lang.String", key.toString(), true, true, false);
}
return new MBeanInfo("PropertiesMBean", "Properties", attributeInfos,
constructorInfos, operationInfos, notificationInfos);
}
public Object invoke(String actionName, Object[] params, String[] signature)
throws MBeanException, ReflectionException {
return null;
}
public void setAttribute(Attribute attribute)
throws AttributeNotFoundException, InvalidAttributeValueException,
MBeanException, ReflectionException {
this.put(attribute.getName(), attribute.getValue());
}
public AttributeList setAttributes(AttributeList attributes) {
AttributeList list = new AttributeList(attributes);
for (int i = 0; i < attributes.size(); i++) {
Attribute attribute = (Attribute) attributes.get(i);
try {
setAttribute(attribute);
} catch (Exception e) {
list.remove(attribute);
}
}
return list;
}
}
そして利用例です。
import java.lang.management.ManagementFactory;
import java.util.Properties;
import javax.management.MBeanServer;
import javax.management.ObjectName;
public class Test {
public static void main(String[] args) throws Exception {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
Properties properties = new PropertiesMBean();
properties.setProperty("foo", "bar");
ObjectName name = new ObjectName("test:type=properties");
server.registerMBean(properties, name);
while(true){
System.out.println(properties.get("foo"));
Thread.sleep(50);
}
}
}
上記サンプルではリモートAPIを利用していないので、JDK5の場合は起動オプションに、-Dcom.sun.management.jmxremoteをつける必要があります。それではTestクラスを起動してからJConsoleを起動してみます。JConsoleはJDKのインストールディレクトリの以下のbinの中に入っています。
Testクラスを起動した状態です。
JConsoleを起動します。この画面はJDK6のJconsoleです。
こんな画面が立ち上がりますので、Testを選択してください。タブがあるのでMBeanを選択してツリーを展開すると、以下のようになります。
プロパティの状態が表示されていますね。これを変更すると、以下のように変わります。
設定がリモート経由でリニアに変更されているのが確認できると思います。これを応用すると、Webアプリでも利用する事が可能です。今回はリモートAPIを使っていないのでローカルホストからしか接続できませんが、リモートAPIを使う事によってネットワーク経由で操作することも可能です。