Playing with JMX and Scala : MBean Creation & "Remote" access
** Update (2011-12-07) : scala JMX API project just created... I've started refactoring an older JMX scala API, and I'll release the new API as an open source project. The project link : janalyse-jmx : scala JMX API
An example (download corresponding sbt project) of how to create a MBean in Scala. We define the MBean interface using scala trait, and make our class implements the trait capabilities. No need to define the getter thanks to the "@BeanProperty" annotation, scala does the job for us. The MBean registration is directly done within Supervisor class instanciation, the jmxRegister method and with an implicit conversion to automatically convert a String to an ObjectName.
And now the test case, showing how to test within the same JVM our Supervisor MBean. The test case creates an internal MBean server, which will be used by Supervison internal MBean registration. So we take benefits of local and remote-like access from jmx for our test case.
CONTEXT : Linux Gentoo / Scala 2.9.0 / Java 1.6.0_26 / SBT 0.10 / ScalaTest 1.6.1
An example (download corresponding sbt project) of how to create a MBean in Scala. We define the MBean interface using scala trait, and make our class implements the trait capabilities. No need to define the getter thanks to the "@BeanProperty" annotation, scala does the job for us. The MBean registration is directly done within Supervisor class instanciation, the jmxRegister method and with an implicit conversion to automatically convert a String to an ObjectName.
package jmxsandbox
import scala.actors.Actor
import scala.reflect.BeanProperty
import java.lang.management.ManagementFactory
import javax.management.ObjectName
// Some definitions to simplify the code
private object JMXHelpers {
implicit def string2objectName(name:String):ObjectName = new ObjectName(name)
def jmxRegister(ob:Object, obname:ObjectName) =
ManagementFactory.getPlatformMBeanServer.registerMBean(ob, obname)
}
import JMXHelpers._
// Some messages managed by the Supervisor actor
sealed abstract class Message
case object MAlive extends Message
case object MDead extends Message
case object MExit extends Message
case object MGet extends Message
// The defined MBean Interface
trait SupervisorMBean {
def getAlive():Int
}
// The class with JMX MBean
class Supervisor extends Actor with SupervisorMBean {
jmxRegister(this, "JMXSandbox:name=Supervisor")
@BeanProperty
var alive=0
def act() {
loop {
react {
case MAlive => alive+=1
case MDead => alive-=1
case MExit => exit
case MGet => reply(alive)
}
}
}
}
And now the test case, showing how to test within the same JVM our Supervisor MBean. The test case creates an internal MBean server, which will be used by Supervison internal MBean registration. So we take benefits of local and remote-like access from jmx for our test case.
package jmxsandbox
import java.rmi.registry.LocateRegistry
import java.lang.management.ManagementFactory
import javax.management.remote.JMXConnectorServerFactory
import javax.management.remote.JMXConnectorFactory
import javax.management.remote.JMXServiceURL
import org.scalatest.FunSuite
import org.scalatest.matchers.ShouldMatchers
import JMXHelpers._
class SelfTest extends FunSuite with ShouldMatchers {
// Let's create and start a local JMX service
LocateRegistry.createRegistry(4500)
val url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:4500/jmxapitestrmi")
val mbs = ManagementFactory.getPlatformMBeanServer()
val cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs)
cs.start
test("Checking supervisor state with standard access and from JMX interface") {
// Create and start the supervisor actor
val supervisor = new Supervisor {start}
// Send a MAlive message to the supervisor
supervisor ! MAlive
// get the current alive value from the actor
val stateDirect = (supervisor !? MGet) match {
case e:Int => e
case _ => -1
}
// The check
stateDirect should equal (1)
// Do the same but using Supervisor MBean to read the alive value
val mbserver = JMXConnectorFactory.connect(url).getMBeanServerConnection
val stateViaJMX = mbserver.getAttribute("JMXSandbox:name=Supervisor","Alive").asInstanceOf[Int]
// The check
stateViaJMX should equal (stateDirect)
// Ask the actor to exit
supervisor ! MExit
}
}
CONTEXT : Linux Gentoo / Scala 2.9.0 / Java 1.6.0_26 / SBT 0.10 / ScalaTest 1.6.1