Wednesday, December 14, 2011

Easy SSH scala scripting

Visit janalyse-ssh, and download "jassh.jar" standalone executable jar.
In the same directory where you've downloaded "jassh.jar"file, create a file named "helloworld", and copy&paste the following content in it :
#!/bin/sh
exec java -jar jassh.jar -nocompdaemon -usejavacp -savecompiled "$0" "$@"
!#
import fr.janalyse.ssh._
SSH.connect(host="localhost", username="test", password="testtest") { ssh =>
  println(ssh.execute("""echo -n "Hello World from `hostname`" """))
}
Change the specified user to an existing user, or create a "test" user. Give the right password or remove it if you've exported your public SSH key in remote user ~test/.ssh/authorized_keys file
Then make your "helloworld" file executable (chmod u+x helloworld), and execute it :
toto@myhost $ ./helloworld 
Hello World from myhost
Easy ! And you'll take all benefits of the scala language using just vi, in order to make any kind of remote operations ! No graphical user interface required, no compiler (automatic & transparent compilation on the fly when needed), high performances (I've reached a command throughput up to 545 cmd/s on my host), parallelism using actors, easy parsing using combinators, ...

Sunday, December 11, 2011

JMX grep scala script

A simple script to search for mbeans/attributes-names/attribute-values matching regular expressions given as script parameters. This script is using janalyse-jmx scala API; jajmx.jar java executable is used to startup the script.
This jmx grep script can be used against itself, with its own embedded jmx platform. This feature is enabled through java options (JAVA_OPTS) specified within script startup header.
$ ./jmxgrep localhost 9999  operating
java.lang:type=OperatingSystem - MaxFileDescriptorCount = 4096
java.lang:type=OperatingSystem - OpenFileDescriptorCount = 18
java.lang:type=OperatingSystem - CommittedVirtualMemorySize = 4520407040
java.lang:type=OperatingSystem - FreePhysicalMemorySize = 7900864512
java.lang:type=OperatingSystem - FreeSwapSpaceSize = 10742177792
java.lang:type=OperatingSystem - ProcessCpuTime = 1490000000
java.lang:type=OperatingSystem - TotalPhysicalMemorySize = 16848113664
java.lang:type=OperatingSystem - TotalSwapSpaceSize = 10742177792
java.lang:type=OperatingSystem - Name = Linux
java.lang:type=OperatingSystem - AvailableProcessors = 6
java.lang:type=OperatingSystem - Arch = amd64
java.lang:type=OperatingSystem - SystemLoadAverage = 0.0
java.lang:type=OperatingSystem - Version = 3.0.6-gentoo

$ ./jmxgrep localhost 9999 threadcount
java.lang:type=GarbageCollector,name=PS MarkSweep - LastGcInfo = javax.management.openmbean.CompositeDataSupport(compositeTyp...
java.lang:type=Runtime - SystemProperties = javax.management.openmbean.TabularDataSupport(tabularType=ja...
java.lang:type=Threading - DaemonThreadCount = 12
java.lang:type=Threading - PeakThreadCount = 13
java.lang:type=Threading - ThreadCount = 13
java.lang:type=Threading - TotalStartedThreadCount = 13
java.lang:type=GarbageCollector,name=PS Scavenge - LastGcInfo = javax.management.openmbean.CompositeDataSupport(compositeTyp...
jmxgrep code (just copy paste this code into a file named jmxgrep, and make it executable using chmod a+x jmxgrep) :
#!/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"
SCA_OPTS="-nocompdaemon -usejavacp -savecompiled"
exec java $JAVA_OPTS -jar jajmx.jar $SCA_OPTS "$0" "$@"
!#

import fr.janalyse.jmx._
import JMXImplicits._

if (args.size < 2) {
  println("Usage   : jmxgrep host port searchMask1 ... searchMaskN")
  println("Example : jmxgrep localhost 1099  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.connect(host, port) { implicit jmx =>
  for(on <- jmx.browse ; attr <- on.browse) {
    val value = try { on.get[Any](attr).toString} catch { case _ => "**error**"}
    val found = List(on.toString, attr, value) exists { item =>
      masks exists {re => (re findFirstIn item).isDefined} 
    }
    if (found || masks.isEmpty) println("%s - %s = %s".format(on, attr, truncate(value)))
  }
}
Notice the "-savecompiled" scala option which enable the script compilation result to be stored in a file named "jmxgrep.jar". When started, already compiled code will be reuse if the script hasn't been modified since last compilation time. This allow very fast script startup.

Scala JMX console & scripts examples

I've released a JMX API for scala, ( project-link ) which is straightforward to use in scripts : I provide a single executable jar file named "jajmx.jar", containing everything required to start a scala console or to run a scala script dedicated to jmx operations.
Console mode usage example :
$ java -jar jajmx.jar -usejavacp
Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import fr.janalyse.jmx._
import fr.janalyse.jmx._

scala> val jmx=JMX("localhost", 9999, None)
jmx: fr.janalyse.jmx.JMX = fr.janalyse.jmx.JMX@1ce59895

scala> jmx.domains
res0: List[java.lang.String] = List(JMImplementation, com.sun.management, java.lang, java.util.logging)

scala> jmx.runtime map {_.get[Long]("Uptime")}
res5: Option[Long] = Some(149014)

scala> jmx.os map {_.get[String]("Version")}
res6: Option[String] = Some(3.0.6-gentoo)

scala> jmx("java.lang:type=Memory").set("Verbose", true)

scala> jmx("java.lang:type=Memory").call("gc")
[GC 45515K->28456K(310720K), 0.0010380 secs]
[Full GC 28456K->26792K(310720K), 0.2427710 secs]
res11: Option[Nothing] = None

A simple script example (Invoke an explicit GC from JMX) :
#!/bin/sh
exec java -jar jajmx.jar -nocompdaemon -usejavacp -savecompiled "$0" "$@"
!#
import fr.janalyse.jmx._

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

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

Saturday, December 3, 2011

Scala project skeleton using SBT

Let's create a minimal scala project skeleton, based on SBT build system, with some SBT plugins enabled (eclipse and assembly). The goal is to generate a single standalone executable jar (without any external dependencies), using scala language (and/or java) and eclipse as an optional IDE.
The scala project skeleton is available as a google code project. visit : scala-dummy-project or checkout the project :
# Quick start
$ git clone git://github.com/dacr/scala-dummy-project.git
$ cd scala-dummy-project
$ sbt assembly
$ java -jar target/dummy.jar
Hello userX
Requirements are :
- JVM >= 1.5 available in your PATH
- get and install SBT (Simple Build Tool)
- if you want to use eclipse, download and install Scala IDE for eclipse

To use eclipse, you must initialize the project for eclipse, run "sbt eclipse" or just "eclipse" from SBT console.
All steps :
$ cd scala-dummy-project
$ sbt 
> eclipse
> run
> test
> assembly
> exit
$ java -jar target/dummy.jar
#OR
$ sbt run
The project is configured as follow :
import AssemblyKeys._

seq(assemblySettings: _*)

name := "ScalaDummyProject"

version := "0.1"

scalaVersion := "2.9.1"

mainClass in assembly := Some("dummy.Dummy")

jarName in assembly := "dummy.jar"

libraryDependencies += "org.scalatest" %% "scalatest" % "1.6.1" % "test"

// Junit is just required for eclipse to be able to start tests.
libraryDependencies += "junit" % "junit" % "4.10" % "test"

What's great with sbt is that it manages all dependencies for you (even sub-dependencies !), no need to download anything, everything is done automatically.
CONTEXT : Scala 2.9.1 / SBT 0.11 / ScalaTest 1.6.1 / sbt-assembly 0.7.2 / sbteclipse 1.5.0