My favorites | Sign in
Project Home Downloads Wiki Issues Source
Project Information
Members
Featured
Downloads
Links

JAJMX : High level scala JMX API

The goal is to simplify jmx operations on remote JVM. Work on this library is still in progress... but implemented features work well.

A standalone packaging, "jajmx.jar", is provided, for quick console or scripting usage. It embeds all dependencies, and comes with all scala libraries. Otherwise for library usage, just add the dependency and the repository to your sbt configuration file (build.sbt). Just run "java -jar jajmx.jar -usejavacp" to start the console.

JAnalyse software maven repository : http://www.janalyse.fr/repository/

Scala docs : http://www.janalyse.fr/scaladocs/janalyse-jmx (work in progress...)

Current release : 0.3.1 (for scala 2.9.1, 2.9.2)

Declare dependency in SBT as follow : libraryDependencies += "fr.janalyse" %% "janalyse-jmx" % "0.3.1" % "compile"

Add JAnalyse repository in SBT as follow : resolvers += "JAnalyse Repository" at "http://www.janalyse.fr/repository/"


gcforce script : Only provide to the script, host and port of a remote JVM with JMX enabled, and this script will force the JVM to Garbage Collect major operation.

#!/bin/sh
exec java -jar jajmx.jar -nocompdaemon -usejavacp -savecompiled "$0" "$@"
!#

if (args.size != 2) {
  println("Usage : gcforce host port")
  System.exit(1)
}
val host=args(0)
val port=args(1).toInt

jajmx.JMX.once(host, port) { jmx =>
  jmx("java.lang:type=Memory").call("gc")
  println("Explicit GC invoked on host %s port %d".format(host,port))
}

And the one scala line force gc script :

#!/bin/sh
exec java -jar jajmx.jar -nocompdaemon -usejavacp -savecompiled "$0" "$@"
!#

jajmx.JMX.once("127.0.0.1", 1099) { _.memory map {_ call "gc"} }

jmxgrep script : This scala script search matching mbean name, attribute name, or value satisfying the given set of regular exception. This jmxgrep script can be tested against itself : "jmxgrep localhost 9999 vendor version"

#!/bin/sh
JAVA_OPTS=""
JAVA_OPTS=$JAVA_OPTS" -Dcom.sun.management.jmxremote"
JAVA_OPTS=$JAVA_OPTS" -Dcom.sun.management.jmxremote.port=9999"
JAVA_OPTS=$JAVA_OPTS" -Dcom.sun.management.jmxremote.authenticate=false"
JAVA_OPTS=$JAVA_OPTS" -Dcom.sun.management.jmxremote.ssl=false"
exec java $JAVA_OPTS -jar jajmx.jar -nocompdaemon -usejavacp -savecompiled "$0" "$@"
!#

import jajmx._

if (args.size < 2) {
  println("Usage   : jmxgrep host port searchMask1 ... searchMaskN")
  println("Example : jmxgrep localhost 9999  vendor")
  println("   will self connect to jmx server, and looks for vendor keyword")
  System.exit(1)
}
val host  = args(0)
val port  = args(1).toInt
val masks = args.toList.drop(2) map {s=>("(?i)"+s).r}

def truncate(str:String, n:Int=60) = if (str.size>n) str.take(n)+"..." else str

JMX.once(host, port) { jmx =>
  for(mbean <- jmx.mbeans ; attr <- mbean.attributes; value <- mbean.getString(attr)) {
    val found = List(mbean.name, attr.name, value) exists { item => masks exists {_.findFirstIn(item).isDefined } }
    if (found) println("%s - %s = %s".format(mbean.name, attr.name, truncate(value)))
  }
}

lsthreads script : connects to a remote JVM using just the host adress and the used JMX port, and then list all active threads and theirs current states. Some java options (JAVA_OPTS) have been added to allow using this script against itself.

#!/bin/sh
JAVA_OPTS=""
JAVA_OPTS=$JAVA_OPTS" -Dcom.sun.management.jmxremote"
JAVA_OPTS=$JAVA_OPTS" -Dcom.sun.management.jmxremote.port=9999"
JAVA_OPTS=$JAVA_OPTS" -Dcom.sun.management.jmxremote.authenticate=false"
JAVA_OPTS=$JAVA_OPTS" -Dcom.sun.management.jmxremote.ssl=false"
exec java $JAVA_OPTS -jar jajmx.jar -nocompdaemon -usejavacp -savecompiled "$0" "$@"
!#
import jajmx._
import javax.management.openmbean.CompositeData

val host = if (args.size>=1) args(0) else "localhost" 
val port = if (args.size>=2) args(1).toInt else 9999

case class ThreadLock(name:String, ownerId:Option[Long], ownerName:Option[String])

case class ThreadInfo(name:String, id:Long, state:String,lock:Option[ThreadLock],
                      blockedCount:Long, waitedCount:Long)

object ThreadInfo {
  def apply(ti:CompositeData) :ThreadInfo = {
	  val id           = ti.get("threadId").toString.toLong
	  val name         = ti.get("threadName").toString
	  val state        = ti.get("threadState").toString
	  val blockedCount = ti.get("blockedCount").toString.toLong
	  val waitedCount  = ti.get("waitedCount").toString.toLong
	  val lock = Option(ti.get("lockName")) map {_.toString} filter {_.size>0} collect {
	               case lockName =>
	                   val lockOwnerId = Option(ti.get("lockOwnerId")) map {_.asInstanceOf[Long]} filterNot {_ == -1}
	                   val lockOwnerName = Option(ti.get("lockOwnerName")) map {_.toString.trim}
	                   ThreadLock(lockName, lockOwnerId, lockOwnerName)
	             }
	  ThreadInfo(name, id, state, lock, blockedCount, waitedCount)
  }
}

JMX.once(host,port) { jmx =>
  for (threading <- jmx.threading) {
    val ids = threading.get[Array[Long]]("AllThreadIds")
    val rawThreadsInfos = threading.call[Array[CompositeData]]("getThreadInfo", ids).get
    val threadsInfos = rawThreadsInfos map {ThreadInfo(_)} toList
    val countByState = threadsInfos groupBy {_.state} map { case (s,l) => s -> l.size}
    val countByStateStr = countByState map {case (s,l) => s+":"+l} mkString " "
    println("Total %d  %s".format(threadsInfos.size, countByStateStr))
    for ( ti <- threadsInfos sortBy {_.id } ) {
      println("%d - %s - %s".format(ti.id, ti.state, ti.name) )
    }
  }
}

JMX API usage examples :

  // -- Define Some MBean Interface 
  trait SomeoneMBean {
	def getAge():Int
  }
  // -- Our JMX Managed class 
  case class Someone(name:String, @BeanProperty age:Int) extends SomeoneMBean {
	  JMX.register(this, "people:name=%s".format(name))
  }
  
  // -- The test case : Creates a JMX managed instance, and queries it
  test("MBean create & use") {
    val marvin = Someone("Marvin", 30)
    
    val marvinAgeFromJMX = JMX.once() { jmx => 
      jmx("people:name=Marvin").getOption[Int]("Age")
    }
    marvinAgeFromJMX should equal(Some(30))
    info("Marvin age is %s".format(marvinAgeFromJMX map {_.toString} getOrElse "Unknown"))
  }
  test("Simple JMX test") {
    JMX.once() { jmx =>
    	val os = jmx("java.lang:type=OperatingSystem")
	    val List(name,version) =  os.get[String]("Name", "Version")
	    
	    name.size should be > (0)
	    version.size should be > (0)
	    info("OS Name & Version : %s %s".format(name, version))
	
	    val runtime = jmx("java.lang:type=Runtime")
	    val vmname = runtime.get[String]("VmName")
	    vmname should include ("Java")
	    info("JVM NAME : %s".format(vmname))

	    val threading = jmx("java.lang:type=Threading") 
	    threading.set("ThreadCpuTimeEnabled", true)
	    val cpuTimeEnabled = threading.get[Boolean]("ThreadCpuTimeEnabled")
	    info("isThreadCpuTimeEnabled=".format(cpuTimeEnabled))
	    
	    val mem = jmx("java.lang:type=Memory")
	    mem.call("gc")
	    info("Explicit GC invoked")
    }
  }

  // ================================================================================================
  test("short and safe usage") {
    JMX.once() { jmx =>
      val osname=jmx.getOption("java.lang:type=OperatingSystem") flatMap {_.getOption[String]("Name")}
    }
  }
  // ================================================================================================
  test("one line jmx browsing") {
	  for(mb <- JMX().mbeans ; attr <- mb.attributes; value <- mb.getString(attr)) {
	}
  }
  // ================================================================================================
  test("get numeric attributes") {
	JMX.once() { jmx =>
	  val os = jmx.os.get
	  val numAttrs  = os.attributes collect {case x:RichNumberAttribute => x}
	  val numValues = numAttrs map {a => os.getDouble(_)}
	}
  }

JVM options to enable JVM JMX remote access :

JAVA_OPTS=""
JAVA_OPTS=$JAVA_OPTS" -Dcom.sun.management.jmxremote"
JAVA_OPTS=$JAVA_OPTS" -Dcom.sun.management.jmxremote.port=9999"
JAVA_OPTS=$JAVA_OPTS" -Dcom.sun.management.jmxremote.authenticate=false"
JAVA_OPTS=$JAVA_OPTS" -Dcom.sun.management.jmxremote.ssl=false"
Powered by Google Project Hosting