tokuhirom's Blog

JMX にアクセスして値を取得したい

Java で何かを実装したい場合、Tomcat ちゃんやらなんやらの remote のプロセスからデータを取得したいという欲求を覚えることでしょう。

そういう時の取得方法についてサンプルプログラムを以下に貼ります。

以下のような感じで書くと、オブジェクトのリストを得ることができます。 remote.queryMBeans(null, null) と呼ぶとオブジェクトリストがとれるんですね。このへんはググれば出てきます。

import javax.management.remote.{JMXConnectorFactory, JMXServiceURL}

import scala.collection.JavaConverters._

object JMXListItems {
    def main(argv: Array[String]) = {
        if (argv.length != 1) {
            println("Options: jmx_url")
            System.exit(0)
        }

        val url = argv(0)
        val target = new JMXServiceURL(url)
        val connector = JMXConnectorFactory.connect(target)
        val remote = connector.getMBeanServerConnection
        val objectNames = remote.queryMBeans(null, null)
        objectNames.asScala foreach { it =>
            printf("%s %s\n", it.getClassName, it.getObjectName.getCanonicalName)
        }
    }
}

ほんで、実際の値の取得はこんな感じです。 MBeanServerConnection#getAttributes() というメソッドがあるのでこれを利用すればよいように見えるが、一部の MBean は Serialize 不能な値を返してくる……。そういう場合には getAttributes メソッドが例外を上げてくるので、まったく値を取れない。 そこで、現実的には一個一個例外をキャッチしつつ getAttribute するしかないのかな、と思っている。 が、そんなバカバカしい状態が Java SE でずっと放置されているわけもないと思うので、実はもっとちゃんとした方法があるのかもしれない!

import javax.management.ObjectName
import javax.management.remote.{JMXConnectorFactory, JMXServiceURL}

object JMXGetAttributes {
    def main(argv: Array[String]): Unit = {
        if (argv.length != 2) {
            println("Options: jmx_url object_canonical_name")
            System.exit(0)
        }

        val url = argv(0)
        val objectCanonicalName = argv(1)
        val target = new JMXServiceURL(url)
        val connector = JMXConnectorFactory.connect(target)
        try {
            val remote = connector.getMBeanServerConnection
            val name = ObjectName.getInstance(objectCanonicalName)
            remote.getMBeanInfo(name).getAttributes foreach { attribute =>
                val value = try {
                    remote.getAttribute(name, attribute.getName).toString
                } catch {
                    case e:Exception => "Unavailable"
                }
                printf("%s %s\n", attribute.getName, value)
            }
        } finally {
            connector.close()
        }
    }
}

ちなみに MBeanServerConnection は内部でスレッドを上げてくるので、ちゃんと close しないとすぐに Out Of Memory になるので注意が必要。

あと、サンプルコードは scala です。