|
Project Information
Featured
Downloads
Links
|
JAJMX : High level scala JMX APIThe 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" |