CLOVER🍀

That was when it all began.

MXBeanを作成してみる

今回で、JMX関連は1度終わりにしようかなぁと思います。最後は、MXBeanです。

お題はやはり、以下のサイトのサンプルをScalaで書き直し。
http://www.javainthebox.net/laboratory/JavaSE6/userdefinedmxbeans/mxbean.html
まあ、MXBeanのAttributeとかOperationはこちらで元々使っていたサンプルがベースになっていますけどね。

では、まずは普通のMXBean。
MyMXBean.scala

import scala.reflect.BeanProperty

trait MyMXBean {
  @BeanProperty
  var count: Int

  def execute(): Unit
}

class MyMXBeanImpl(val name: String) extends MyMXBean {
  @BeanProperty
  var count: Int = _

  def execute(): Unit = {
    count += 1
    printf("Execute[%s], count[%d]\n", name, count)
  }
}

パッと見、名前が「MXBean」で終わっていることを覗いてほぼStandardMBeanと同じです。まあ、クラス名に対する縛りはStandardMBeanよりも緩そうな感じですが。

続いて、CompositeDataを利用するMXBean。最初は、MXBeanの定義です。
MyCompositeMXBean.scala

import scala.reflect.BeanProperty

trait MyCompositeMXBean {
  @BeanProperty var count: Int
  @BeanProperty val myName: MyName
  def execute(): Unit
}

class MyCompositeMXBeanImpl(val first: String, val last: String) extends MyCompositeMXBean {
  @BeanProperty var count: Int = _
  @BeanProperty val myName: MyName = new MyName(first, last)

  def execute(): Unit = {
    count += 1
    printf("Execute[%s:%s], count[%d]\n", first, last, count)
  }
}

MXBeanが使用するCompositeDataの定義。
MyCompositeData.scala

import scala.reflect.BeanProperty

import javax.management.openmbean.{CompositeData, CompositeDataSupport, CompositeType}

import sun.management.{LazyCompositeData, MappedMXBeanType}

object MyName {
  def apply(cd: CompositeData): MyName = {
    MyNameCompositeData.validateCompositeData(cd)
    new MyName(MyNameCompositeData.first(cd), MyNameCompositeData.last(cd))
  }

  def from(cd: CompositeData): MyName = cd match {
    case null => null
    case name: MyNameCompositeData => name.myName
    case _ => apply(cd)
  }
}

class MyName(@BeanProperty val first: String, @BeanProperty val last: String)

object MyNameCompositeData {
  private val NAME_FIRST: String = "first"
  private val NAME_LAST: String = "last"

  val MY_NAME_COMPOSITE_TYPE: CompositeType =
    MappedMXBeanType.toOpenType(classOf[MyName]).asInstanceOf[CompositeType]
  val MY_NAME_ITEM_NAMES: Array[String] = Array(NAME_FIRST, NAME_LAST)

  def toCompositeData(myName: MyName): CompositeData =
    new MyNameCompositeData(myName).getCompositeData

  def first(cd: CompositeData): String = cd.get(NAME_FIRST).asInstanceOf[String]
  def last(cd: CompositeData): String = cd.get(NAME_LAST).asInstanceOf[String]

  def validateCompositeData(cd: CompositeData): Unit = {
    require(cd != null)
    //require(!LazyCompositeData.isTypeMatched(MY_NAME_COMPOSITE_TYPE, cd.getCompositeType))
  }
}

class MyNameCompositeData(@BeanProperty val myName: MyName) extends LazyCompositeData {
  import MyNameCompositeData._

  protected def getCompositeData: CompositeData =
    new CompositeDataSupport(MY_NAME_COMPOSITE_TYPE, MY_NAME_ITEM_NAMES, Array(myName.first,myName.last))
}

なんかMyとか付いてますが、基本的にはサイトのサンプルと同じです。ただ、LazyCompositeData#isTypeMatchedはprotected staticなメソッドのため、Scalaからは呼び出すことができないので断念しました。

最後、@MXBeanアノテーションを使用したMXBeanの作成。MXBeanの定義。
MyEasyMXBean.scala

import scala.reflect.BeanProperty

import javax.management.MXBean

@MXBean
trait MyEasyMXBean {
  @BeanProperty var count: Int
  @BeanProperty val easyName: EasyName

  def execute(): Unit
}

class MyEasyMXBeanImpl(val name: String) extends MyEasyMXBean {
  @BeanProperty var count: Int = _
  @BeanProperty val easyName: EasyName = new EasyName(name)

  def execute(): Unit = {
    count += 1
    printf("Execute[%s], count[%d]\n", name, count)
  }
}

続いて、複合型の定義です。
EasyName.scala

import scala.reflect.BeanProperty

import java.beans.ConstructorProperties

class EasyName @ConstructorProperties(Array("name"))(@BeanProperty val name: String)

Scalaでコンストラクタにアノテーションを付ける??と一瞬戸惑いましたが、アクセス修飾子とかの指定方法から冷静に考えると、上記の位置でよさそうですよね。

いずれも、MBeanServerに普通に登録すればJConsoleなどで確認することができます。

登録したMXBeanをJConsoleで見たところ。Scalaのフィールドに対応するメソッドが見えてしまっているのはご愛嬌。