Showing posts with label groovy. Show all posts
Showing posts with label groovy. Show all posts

Saturday, September 25, 2010

Revisiting JMX DSL with Groovy GEP3

In one of the previous blog post, I experimented with a sample DSL for JMX reporting using Jfree chart. Groovy 1.8.2 (Beta) has introduced command expression language. This GEP-3 essentially allows you to create command expression language which can further simplify language of DSL. The primary use of this is to aid in writing impromptu scripts to visually demo various MBean values. In this example we will see bar charts for module processing time for all web modules.

Command expression language allows you to alternate method name and parameter for even number of argument
e.g.
foo a1 a2 a3 a4 a5. Foo(a1).a2(a3).a4(a5)
You can find more example and explanation on GEP-3 page.

Revisiting JMX example so that I can simplify the language as

server.url "service:jmx:rmi://localhost/jndi/rmi://localhost:1090/jmxconnector" query "jboss.web:*" filter "j2eeType=WebModule"
def filteredModules = server.filteredModules

chart.type "Bar" modules filteredModules title "Module Processing Time" width 1200 height 700 refresh 500 attributes params labels graphLabels
chart.show()

Let's look at the supporting code.

import org.jfree.chart.ChartFactory
import groovy.swing.SwingBuilder

import org.jfree.data.category.DefaultCategoryDataset
import org.jfree.chart.plot.PlotOrientation as Orientation
import javax.swing.WindowConstants as WC
import javax.management.ObjectName
import javax.management.remote.JMXConnectorFactory
import javax.management.remote.JMXServiceURL as JmxUrl
import javax.naming.Context

class Chart {
    def chartModules
      def chartType
    def chartAttributes = {m -> [m.processingTime, m.path]}
    def chartLabels =["Time per Webapp", "Webapp", "Time"]
    def chartOptions = [false, true, true]
    def windowTitle
    def w
    def h
    def refreshRate
    def orientation = "VERTICAL"
    def dataset

    def modules(m) {
          chartModules = m
          this
      }

      def type(type) {
        chartType = type
         this
      }

      def attributes(attr) {
        chartAttributes = attr
        this
      }

      def labels (lbls) {
        chartLabels = lbls
        this
      }

      def options (opts) {
        chartOptions = opts
        this
      }

      def title (title) {
        windowTitle = title
        this
      }

      def width(width) {
        w = width
        this
      }

      def height(height) {
        h = height
        this
      }

      def refresh(r) {
        refreshRate = r
        this
      }

    void show(){
        switch(chartType){
            case "Bar": drawBarChart(); break;
            default: break;
        }
    }

    void drawBarChart(){
        calculateData()
        def chart = ChartFactory.createBarChart(*chartLabels, dataset, Orientation."${orientation}", *chartOptions)
        def swing = new SwingBuilder()
        def frame = swing.frame(title:windowTitle, defaultCloseOperation:WC.EXIT_ON_CLOSE){
            panel(id:'canvas') {rigidArea(width:w, height:h)}
        }
        while(true){
            calculateData()
            chart.fireChartChanged()
            frame.pack()
            frame.show()
            chart.draw(swing.canvas.graphics, swing.canvas.bounds)
            sleep(refreshRate)
        }
    }
    void calculateData(){
        def newDataset = new DefaultCategoryDataset()
        chartModules.each{ m ->
            def dsCall = chartAttributes.call(m)
            newDataset.addValue dsCall[0], 0, dsCall[1]
        }
        this.dataset = newDataset
    }
}


class Server {
    def server
    def queryObjects
    def moduleFilter
    def filteredModules

    def url(serverName){
    server = JMXConnectorFactory.connect(new JmxUrl(serverName)).MBeanServerConnection
    this
    }

    def query (queryString) {
        queryObjects = new ObjectName(queryString)
       this
    }

    def filter (filterString) {
         String[] allNames = server.queryNames(queryObjects, null)
        filteredModules = allNames.findAll { name -> name.contains(filterString)}.collect { new GroovyMBean(server, it)}
    }
}

server = new Server()
chart = new Chart()
params = {m -> [m.processingTime, m.path]}
graphLabels = ["Time per Webapp", "Webapp", "Time"]

server.url "service:jmx:rmi://localhost/jndi/rmi://localhost:1090/jmxconnector" query "jboss.web:*" filter "j2eeType=WebModule"
def filteredModules = server.filteredModules

chart.type "Bar" modules filteredModules title "Module Processing Time" width 1200 height 700 refresh 500 attributes params labels graphLabels
chart.show()

Problems faced
For some reason with the current beta build I couldn't do arrays or closures inline. so I had to put them as separate variables. I do not think it is by design and as 1.8.X comes close to release these wrinkles will be worked out.
Blogged with the Flock Browser

Tuesday, March 16, 2010

Using Groovy Scriptom to report on Outlook

As with most work places, I use MS Outlook Calendar at work for meetings, calendars, etc. With Outlook 2007 you have capability to categorize each meeting/time block that you have on your calendar. So you can classify each meeting as either design, architecture, learning, recurring, etc. Using Groovy Scriptom module you can do what you would usually do using VBScript. Scriptom allows you to use ActiveX windows components from Groovy. Needless to say this will only work on windows platform.

I used the scriptom to report on how I am spending my time. (ah.. have to fill those time sheet) Using Swingbuilder and JFreeChart, I threw in this simple script. You will need to setup dependency exactly as described on the help page here.

import org.codehaus.groovy.scriptom.ActiveXObject
import org.jfree.chart.ChartFactory
import javax.swing.WindowConstants as WC

def outlook = new ActiveXObject("Outlook.Application")
def namespace = outlook.GetNamespace("MAPI")
def calFolder = namespace.GetDefaultFolder(9)
def myItems = calFolder.Items

def today = new Date()
startWeek = today - 7
endWeek = today + 7
def categoryMap = [:]

for (i in 1..myItems.Count.value) {
     def currentMeeting = myItems.Item(i)
     if (currentMeeting.Start >= startWeek && currentMeeting.Start <= endWeek) {
           println "Subject: " + currentMeeting.Subject.value
           println "Category: " + currentMeeting.Categories
           println "Duration: " + currentMeeting.Duration.value

           category = currentMeeting.Categories
           durationValue = currentMeeting.Duration.value

           def value = categoryMap.get(category)
           value = value?value:0
           def newValue = value + durationValue
           categoryMap.put(category,newValue);
     }
}

def swing = new groovy.swing.SwingBuilder()
def titleString = 'Time Outlook: ' + String.format('%tm/%<td/%<tY', startWeek) + "-" + String.format('%tm/%<td/%<tY', endWeek)
def frame = swing.frame(title:titleString,
                        defaultCloseOperation:WC.EXIT_ON_CLOSE,
                        size:[800,600], locationRelativeTo: null) {
     borderLayout()
     panel(id:'canvas') { rigidArea(width:700, height:500) }
 }

def bounds = new java.awt.Rectangle(0,0, 700,500).bounds
def piedata = new org.jfree.data.general.DefaultPieDataset()
for(entry in categoryMap)
{
  piedata.setValue entry.key + " = " + entry.value, entry.value
}
def options = [false, true, false]
def chart = ChartFactory.createPieChart(titleString, piedata, *options)
chart.backgroundPaint = java.awt.Color.white
frame.pack()
frame.show()
chart.draw(swing.canvas.graphics, bounds)

Simple enough script... It gets the ActiveXObject for Outlook application. Then from namespace it gets calendarFolder (constant 9). You can find out more about constant here. calFolder contains all the meeting appointmentItems. Once you get to the appointmentItem you can ask for the properties using groovy dot-notation.

Here is the snapshot of the output

groovy-scriptom-outlook.png

Blogged with the Flock Browser

Sunday, February 28, 2010

Revisit Jmx Groovy DSL - Using AstTransformation

In the previous blog entry, we created Jmx Charting DSL. Let visit some aspects of creating DSL using groovy.

In the previous blog example we created DSL using ExpandoMetaClass. Users will define the script starting with node jmx { }. With the use of ExpandoMetaClass we added the dynamic method which in turn delegated to class JmxClosureDelegate. Here is the snippet of the code.

static void runEngine(File dsl){
    Script dslScript = new GroovyShell().parse(dsl.text)
    dslScript.metaClass = createExpandoMetaClass(dslScript.class, {
      ExpandoMetaClass emc ->
        emc.jmx = {
          Closure cl ->
            cl.delegate = new JmxClosureDelegate()
            cl.resolveStrategy = Closure.DELEGATE_FIRST
            cl()
        }
    })
    dslScript.run()
   }
What we essentially did here:
  • Using GroovyShell it parses the script file passed as input.
  • Defines the ExpandoMetaClass and adds method by name "jmx" having closure as parameter.
So the script file you wrote gets a dynamic method injected into it using ExpandoMetaClass. This happens at runtime.

Getting rid of the Commas
The problem with creating methods with two arguments is that you have to specify commas between them. For example,

     server "nameofserver", {
          ...
     }
Undoubtedly commas clutter the langauge grammer. To get rid of the commas we used trick provided on Groovy users list.

//To avoid using "," between String and Closure argument 
  def methodMissing(String name, args) { 
    return [name, args[0]] 
  } 

Using AstTransformation
Now lets explore a different possibility. You can achieve similar result using AstTransformation at compile time. The goal remains the same and that is to add method with name "jmx" with closure parameter.

First we will define the annotation.

//import statements skipped for brevity
@Retention(RetentionPolicy.SOURCE)
@Target([ElementType.METHOD])
@GroovyASTTransformationClass(["info.kartikshah.jmx.ast.JmxDslTransformation"])
public @interface UseJmxDsl {}
Transformation Class

This transformation class needs to perform two activities:
  • Add method jmx(Closure cl) method
  • Invoke the script method being defined
We need to generate AST statements for following snippet of code.

     jmx = { 
          Closure cl -> 
            cl.delegate = new JmxClosureDelegate() 
            cl.resolveStrategy = Closure.DELEGATE_FIRST 
            cl() 
        } 
We will use AstBuilder's buildFromSpec option to generate it. (AstBuilders added with Groovy 1.7 definitely makes generating statement structure relatively easy and clutter free. Not to mention it is also an example of DSL added to the groovy language :-) )

@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
class JmxDslTransformation implements ASTTransformation {
  static int PUBLIC = 1
  static int STATIC = 8

  void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
    //Add method jmx(Closure cl)
    ClassNode declaringClass = astNodes[1].declaringClass
    MethodNode jmxMethod = makeMethod()
    declaringClass.addMethod(jmxMethod)

    //Insert method call inside run method of the script class
    MethodNode annotatedMethod = astNodes[1]
    List<MethodNode> allMethods = sourceUnit.AST?.classes*.methods.flatten()
    MethodNode runMethod = allMethods.find{ MethodNode method ->
      method.name == "run"
    }
    List existingStatements = runMethod.getCode().getStatements()
    existingStatements.add(0, createMethodCall(annotatedMethod))
  }

  Statement createMethodCall(MethodNode methodNode){
    def statementAst = new AstBuilder().buildFromSpec {
      expression{
         methodCall {
           variable "this"
           constant methodNode.name
           argumentList {}
         }
      }
    }
    Statement stmt = statementAst[0]
    stmt
  }

  MethodNode makeMethod() {
    def ast = new AstBuilder().buildFromSpec {
      method('jmx', PUBLIC | STATIC, Void.TYPE) {
        parameters {
          parameter 'cl': Closure.class
        }
        exceptions {}
        block {
          expression {
            binary {
              property {
                variable "cl"
                constant "delegate"
              }
              token "="
              constructorCall(JmxClosureDelegate.class) {
                argumentList()
              }
            }
          }
          expression {
            binary {
              property {
                variable "cl"
                constant "resolveStrategy"
              }
              token "="
              property {
                classExpression Closure
                constant "DELEGATE_FIRST"
              }
            }
          }
          expression {
            methodCall {
              variable "cl"
              constant "call"
              argumentList {}
            }
          }
        }
      }
    }
    MethodNode jmxMethod = ast[0]
    jmxMethod
  }
}
Why use AstTransformation?
The question is why one would want to use AstTransformation when you can add method during runtime. For the given scenario, it is correct that you want to stick with adding method runtime. But consider scenario where you want to "redefine" meaning of Groovy's syntax. For example like following imaginary script using Statement Labels to add more readability to your DSL syntax.

@info.kartikshah.jmx.ast.UseJmxDsl
runDsl () {
  jmx {
    setup:
      server "service:jmx:rmi://localhost/jndi/rmi://localhost:1090/jmxconnector"
      query "jboss.web:*"
      findAll "j2eeType=Servlet"

    draw:
    chart {
        chartType="Bar"
        attributes={m-> [m.loadTime, m.objectName.find("name=([^,]*)"){it[1]}]}
        labels=["Load Time per Servlet", "Servlet", "Time"]
        options=[false, true, true]
        windowTitle="JBoss Servlet Processing Time"
        width=1200
        height=700
        orientation="HORIZONTAL"
        refreshRate=5000
        show()
      }
  }
}
Spock Framework does similar twist by redefining meaning of existing construct.

With this type of language structure you will end up defining your own set of keywords, supporting parser and few AstTransformation to change the meaning of existing Groovy Syntax.


Blogged with the Flock Browser

Tuesday, February 16, 2010

A Groovy DSL - JMX Reporting

In previous blog example, using JFreeChart, JMX and SwingBuilder we came up with dashboard type utility. With this post we will explore how to write small DSL which will allow to report on JMX using reporting charts. The DSL will using Groovy's SwingBuilder to draw JFree Chart to report on various MBeans. This type DSL can be used to write small scripts to monitor behavioral aspect of application server (or any JMX based application).

Simple DSL
First we will chart out how we want our domain language to look like. DSL syntax can be done multiple ways. It is necessary that you play with the syntax to come up with one that works for the scenario. For our example, here is the first cut that we will use.

Essentially, the script reports processing time of all web modules defined on (in this example - JBoss) application server using Bar Chart.

jmx {
    server "service:jmx:rmi://localhost/jndi/rmi://localhost:1090/jmxconnector" {
      query "jboss.web:*" {
        findAll "j2eeType=WebModule" {
          chart{
            chartType="Bar"
            attributes={m-> [m.processingTime, m.path]}
            labels=["Time per Webapp", "Webapp", "Time"]
            options=[false, true, true]
            windowTitle="JBoss Module Processing Time"
            width=1200
            height=700
            refreshRate=5000
            show()
          }
        }
      }
    }
  }

Other Use cases
Target users for this DSL are system administrators who can write simple scripts to monitor and/or report on various aspects of app server instances graphically.

Some other use cases for this type of DSL
  • Compare Processing Time of Web Application
  • Compare Load Time of Servlets
  • Compare Response Time
  • Compare memory usage
  • Compare total requests
DSL Engine
With Groovy there are multiple ways to write domain specific language as described in groovy documentation here

We will use nested closure approach. More information about nested closure here.

Here we use ExpandoMetaClass and series of Delegate classes to handle each closure. In the sample script above read each node (eg. jmx, server, query) as a dynamic method call with one or two arguments. For example jmx has a string argument followed by closure argument. String argument is we just store in instance variable. For closure argument we delegate the handling to separate Delegate Class. We follow the same pattern for the rest of the nested nodes.

First up Engine
This is the main class for the DSL. It gets the file passed as command args and pass it to GroovyShell to create Script object. It uses ExpandoMetaClass to dynamically add methods/closure. It add adds jmx closure and sets properties for handling closure.

class JmxReportingDslEngine {
 
  static main(String[] args){
    if(args.length != 1)
    {
      println("Usage: JmxReportingDslEngine <ScriptFileName>")
    }
    runEngine(new File(args[0]))
  }
 
  static void runEngine(File dsl){
    Script dslScript = new GroovyShell().parse(dsl.text)
    dslScript.metaClass = createExpandoMetaClass(dslScript.class, {
      ExpandoMetaClass emc ->
        emc.jmx = {
          Closure cl ->
            cl.delegate = new JmxClosureDelegate()
            cl.resolveStrategy = Closure.DELEGATE_FIRST
            cl()
        }
    })
    dslScript.run()
   }

  static ExpandoMetaClass createExpandoMetaClass(Class clazz, Closure cl){
    ExpandoMetaClass emc = new ExpandoMetaClass(clazz, false)
    cl(emc)
    emc.initialize()
    return emc
  }
}

Delegates


Next up we write series of Delegate Class responsible for handling each node of the language.

JMXClosureDelegate
This handles closure passed to jmxtag. It instantiates MBeanServerConnection and passes the reference down to the delegate chain.

class JmxClosureDelegate {
//To avoid using "," between String and Closure argument
  def methodMissing(String name, args) {
    return [name, args[0]]
  }
  void server(param){
    def (serverUrl, cl) = param
    def server = JMXConnectorFactory.connect(new JmxUrl(serverUrl),env).MBeanServerConnection
    cl.delegate = new JmxServerClosureDelegate(server)
    cl.resolveStrategy = Closure.DELEGATE_FIRST
    cl()
  }

JMXServerClosureDelegate

This delegate handles closure passed to server tag.

class JmxServerClosureDelegate {
  def server
  JmxServerClosureDelegate(server){
    this.server = server
  }
  def methodMissing(String name, args) {
   return [name, args[0]]
  }
  void query(param){
    def (objectName, cl) = param
    def query = new ObjectName(objectName)
    String[] allNames = server.queryNames(query, null)
    cl.delegate = new JmxQueryClosureDelegate(allNames, server)
    cl.resolveStrategy = Closure.DELEGATE_FIRST
    cl()
  }
}
JmxQueryClosureDelegate
and so on...

class JmxQueryClosureDelegate {
  def allNames
  def server
  JmxQueryClosureDelegate(allNames, server){
    this.allNames = allNames
    this.server = server
  }
  def methodMissing(String name, args) {
    return [name, args[0]]
  }
  void findAll(param){
    def (filter, cl) = param
    def modules = allNames.findAll{ name ->
          name.contains(filter)
      }.collect{ new GroovyMBean(server, it) }
    cl.delegate = new JmxFindAllClosureDelegate(modules)
    cl.resolveStrategy = Closure.DELEGATE_FIRST
    cl()
  }
}
JmxFindAllClosureDelegate

class JmxFindAllClosureDelegate {
  def modules
  JmxFindAllClosureDelegate(modules){
    this.modules = modules
  }
  void chart(Closure cl){
    cl.delegate = new ChartDelegate(modules)
    cl.resolveStrategy = Closure.DELEGATE_FIRST
    cl()
  }
}
ChartDelegate
This class
  • Creates the dataset from the MBean values
  • Creates the chart with the dataset values
  • Creates the external frame using SwingBuilder

class ChartDelegate {

  def modules
  def chartType
  def attributes
  def labels
  def options
  def windowTitle
  def width
  def height
  def refreshRate
  def orientation = "VERTICAL"
  def dataset
  ChartDelegate(modules){
    this.modules = modules
  }
  void show(){
    switch(chartType){
      case "Bar": drawBarChart(); break;
     //TODO:Add more chart types
      default: break;
    }
  }
  void drawBarChart(){
    calculateData()
    def chart = ChartFactory.createBarChart(*labels, dataset, Orientation."${orientation}", *options)
    def swing = new SwingBuilder()
    def frame = swing.frame(title:windowTitle, defaultCloseOperation:WC.EXIT_ON_CLOSE){
      panel(id:'canvas') {rigidArea(width:width, height:height)}
    }
    while(true){
      calculateData()
      chart.fireChartChanged()
      frame.pack()
      frame.show()
      chart.draw(swing.canvas.graphics, swing.canvas.bounds)
      sleep(refreshRate)
    }
  }
  void calculateData(){
    def newDataset = new DefaultCategoryDataset()
    modules.each{ m ->
      def dsCall = attributes.call(m)
      newDataset.addValue dsCall[0], 0, dsCall[1]
    }
    this.dataset = newDataset
  }
}
Output
Let's run and look at sample output...

web-app-processing-time

Click here for bigger images

Here is another script for Servlet Load Time

This one uses some intermediate groovy knowledge with attributes param taking a closure. It allows user to perform transformation on the MBean parameters. Below in the script it performs operates on long objectName attribute to get servlet name parameter.

jmx {
    server "service:jmx:rmi://localhost/jndi/rmi://localhost:1090/jmxconnector" {
      query "jboss.web:*" {
        findAll "j2eeType=Servlet" {
            chart{
              chartType="Bar"
              attributes={m-> [m.loadTime, m.objectName.find("name=([^,]*)"){it[1]}]}
              labels=["Load Time per Servlet", "Servlet", "Time"]
              options=[false, true, true]
              windowTitle="JBoss Servlet Processing Time"
              width=1200
              height=700
              orientation="HORIZONTAL"
              refreshRate=5000
              show()
            }
        }
      }
    }
}
and here is the output...

servlet-load-time

Click here for bigger image

Further you can,
  • Add different type of chart support to the DSL - Bar, XY, trending, etc
  • Use/Extend to work with different application server and/or application

Complete source code can be found at http://github.com/kartikshah/jmx-dsl
Blogged with the Flock Browser

Saturday, January 23, 2010

Helpful tips - Working with AST Transformation

I have been playing with Groovy AST transformation for some time now. You can walk through some of the earlier posts here. With this entry I want to capture some of the lessons learnt, tips, etc.
 
Using AstViewer Tool (Inspect Ast)
With Groovy 1.6 AST transformation, one of the difficulty was to come up with tree structure for the code you want to inject or change. It involves calling complex set of Statement and Expression APIs. It is ridden with intricate details of language grammar. With groovy 1.7 AstBuilder, you can use buildFromString and buildFromCode which is lot more easier. But still if you have to use buildFromSpec you will need to have some understanding of Statement/Expression API.
 
One of the tool I have found useful is Groovy Console's AST Browser or Eclipse's ASTViewer. Write code you want to generate language structure for and try to replicate using API or using AstBuilder's buildFromSpec. The tool has gotten better from 1.6 to 1.7.
Open Groovy Console and type in following code

            def advice = new String("Hello AST")
            println advice

Go to Script --> Inspect Ast (or press Ctrl+T) and you will see the AST structure needed.

 
Educated Guess in Script
With AstBuilder now you can just start building AST in main method or just as script. This has proven to be useful if you just want to compare what you are generating with what is displayed in AstBuilder.

def nodes = new AstBuilder().buildFromXXX{
}
 
Working with Variables
If you are using variables that gets replaced during runtime you may face problem generating AST. One of the method that I found useful was to replace the variable with static value and see if it works. If it does than the problem is possibly due to the type of the variable.
For example, I wanted to generate

          def advice = new <<variableClassName>>()

where variableClassName will be passed in from annotation value. One option is to try generating it with static value like java.lang.String (or just String), if it works than there is some problem with the type of the variable you are passing.
 
Compile file individually (if using IDE)
Sometimes the tools you are working with just does not seem to give different output even when you are changing things around in your code. Sometimes stale classes just gets called and you never see the output from the updated classes. You may want to compile both transformation class and the class where the transformation gets used. 
 
ByteCode Viewer
Also using Byte Code Viewer like this helps in looking at the classes that are getting generated. You can also write your class how it will look after AST transformation. Use byte code viewer to see the class and compare it with the class that is being generated through AST Transformation. See which statements are properly generated and which are not.
 
Testcases
Another extremely resourceful source for AstBuilder is test cases found here. It contains rich example set of how to use each node in the builder specifically for buildFromSpec option. AstAssert is another excellent utility to compare and test expected and actually generated structure. Looks like AstAssert class is not packaged with groovy-1.7, so you may need to download and drop it in your project.

Divide and Conquer
Treat each section of code for which AST is being generated as separate section. Comment out blocks of code and gradually uncomment them to see which section is actually failing and/or not giving desired output.
 
Hope this helps! Drop in a line to share your tips or problems you face with Groovy's Ast transformation. 

Blogged with the Flock Browser

Monday, January 18, 2010

Revisiting Groovy AST Transformation with AstBuilder

Groovy AST is powerful tool to inject changes into classes at various compile phases. It allows one to operate on AST (Abstract Syntax Tree) to manipulate class structure.

With Groovy 1.7 AstBuilder there are easier ways to write AST transformation. One can build AST nodes using one of following three options
  • Build From String
  • Build From Spec
  • Build from Code
Previously on this blog, we experimented with Groovy 1.6 AST Transformation Example and also AOP style usage  at Groovy AST Transformation - AOP Style. Lets revisit this transformation from earlier post.

Lets see how we can generate the AST tree using these new methods from AstBuilder. Recap this previous post before proceeding ahead.

Looks like we need to rewrite initAdviceCall and createMethodCall to experiment with new methods of AstBuilder.

Build From String
Using buildFromString the task would become very easy. With this options, it is not required to build complicated statement and expression structure. All you need to do is embed code within string and pass it in to buildFromString method. If code you generate is not completely static in nature, you may have to parameterize the value. Here in this example, the declaration of "advice" instantiation depends on the Class value defined in the annotation.  

  def initAdviceCall(classNodeType) {
    def codeString = "def advice = new " + classNodeType.type.typeClass.canonicalName + "()"
    def buildNodes = new AstBuilder().buildFromString(CompilePhase.SEMANTIC_ANALYSIS, true, codeString)
    Statement stmt = buildNodes[0].statements[0]
    stmt
  }

  def createMethodCall(method, Parameter[] parameters) {
    def codeString = "advice.before (" + "'$method.name'" +", [" + parameters.collect {it.name}.join(',') + "])"
    def buildNodes = new AstBuilder().buildFromString(CompilePhase.SEMANTIC_ANALYSIS, true, codeString)
    Statement stmt = buildNodes[0].statements[0]
    stmt
  }

Build From Spec
buildFromSpec option is more verbose. It also requires you to understand internal structure that is required to be build. With use of GroovyConsole's Inspect Ast option, it is not difficult to come up with the AST structure.

  def initAdviceCall (classNodeType) {
    def buildNodes = new AstBuilder().buildFromSpec {
      expression {
        declaration {
          variable "advice"
          token "="
          constructorCall(classNodeType.getType().getTypeClass()){
            argumentList()
          }
        }
      }
    }
    Statement stmt = buildNodes[0]
    stmt
  }

  def createMethodCall(method, Parameter[] parameters) {
    def buildNodes = new AstBuilder().buildFromSpec {
      expression {
        methodCall {
          variable "advice"
          constant "before"
          argumentList {
            constant method.name
            list {
              parameters.each {variable it.name}
            }
          }
        }
      }
    }
    Statement stmt = buildNodes[0]
    stmt
  }

There are some differences between using API directly and using spec builder option. One thing specifically that threw me off was that ConstructorCallExpression takes in ClassNode as parameter, however the constructorCall in builder takes Class. It wouldn't have been much of an annoyance, if I had read GEP-2 which states that any expression taking ClassNode, for builder you just need to pass class instance. Well in this example, value provided by annotation was ClassNode, so I had to get Class and pass it on to constructorCall.

Build From Code
Unlike above two options buildFromCode option did not work for me in above scenario. The problem I faced was I couldn't do def advice = <VariableClassName>() without using reflection. Method would have looked something like this

def initAdviceCall(classNodeType){
new AstBuilder().buildFromCode {
     def advice = classNodeType.getType().getTypeClass().newInstance()
}
Similarly, for createMethodCall, I would have to use reflection for passing in parameters.

More later...
Blogged with the Flock Browser

Tuesday, January 05, 2010

Groovy Truth

Groovy had its truth. Groovy expanded on what Java allows to use in various control structures. While Java allows only boolean expression in if and while control structures, Groovy relaxed the requirement. In Groovy you can use objects in if and while expressions. Only a non-null and non-empty string will evaluate to true. A non-empty collection will evaluate to true. Non zero numbers will evaluate to true.

With Groovy 1.7, Groovy Truth got better. You can now customize the Truth based on your requirement. This can be done by implementing boolean asBoolean() method. Implement the method on any object and logic defined in the method will be used to evaluate. Look at the following example


class Account {
     ...
     String status
     boolean asBoolean(){
          if(status != 'active')
               false
          else
               true
     }
}
assert new Account(status:'active')
assert !new Account(status:'prospect')


You can also expand (or change) existing classes like String, Integer, etc using metaClass. Let's expand the groovy truth to return null if the string value is 'null'


     String.metaClass.asBoolean = {
                                    if(delegate  == null || delegate == '' || delegate == 'null') false
                                    else true
                                  }

     String nullString = null
     String nullLiteralString = "null"
     String validString = "JohnDoe"
     String emptyString = ''
    
     assert !nullString
     assert !nullLiteralString
     assert validString
     assert !emptyString 


Asserts are one place where the overridden logic will be applied. It works with if,while, and ternary operator as well.


//continued from previous script
println "null"?"FALSE":"TRUE"

str1 = "null"
if(str1) println "Valid String"
else println 'Null, empty or "null" literal string'


If you do not want to override the way an object evaluates to boolean globally. You can use categories.


class StringNullTruthCategory{
    boolean asBoolean(String str){
        if(str || str == 'null')  false
        else true
    }
}

use(StringNullTruthCategory){
   String parameterValue = "null"
   if(parameterValue)   {
       println 'Should not be printed'
   } else {
       println 'Should print'
   }
}

def httpRequestParmValue = "null"
print 'Valid Value: '
println httpRequestParmValue?'YES':'NO'



Similarly, you can expand Integer's truth value if value is either zero or negative value. you can expand truth value of Map based on specific key-value. You have to be careful in doing so though, completely overriding the evaluation criteria can lead to confusion. You can apply on customize builder object. Let's say you have customized builder parsing order.xml into OrderEntity who has multiple OrderItems. OrderEntity truth can be derived from all of its OrderItem.


class OrderEntity{
     ...
     OrderItem[] orderItems
     String status
     ...
     boolean asBoolean(){
          //Define based on status or status of orderItems or presense/absence of orderItems
     }
}

if(currentOrder) {
     //do time consuming process
}


You can define asBoolean objects in your Java class and use that definition to determine boolean value. However, you have to be careful of legacy Java Objects which has method asBoolean already defined for different purpose.




Blogged with the Flock Browser

Thursday, November 19, 2009

Grails: belongsTo


In Grails hasMany and belongsTo are used to define relationship between objects. There are various other uses of belongsTo.  Trying to capture them here in this short post.
 
Let's take an example, say you have domain class TaskList which had many Task
 
Direction of Access
In class Task if you define belongsTo as
             belongsTo = TaskList
it means that Task object can not access its associated instance of TaskList
If it is defined as
            belongsTo = [taskList: TaskList]
it means that Task object can access its associated instance of TaskList by doing task.taskList
 
Cascading Deletes
Only if belongsTo is defined cascading delete will happen. If TaskList is deleted all Tasks part of that TaskList will be deleted.
 
addTo* method access
For many-to-many relationship, defining belongsTo enables dynamic addTo* method
Extending previous example, say each Task can have multiple Tag and a Tag can be given to multiple Tasks. Defining belongsTo enables addTo* dynamic methods. e.g. task.addToTags(tag)


Blogged with the Flock Browser

Friday, July 10, 2009

Groovy AST Transformation - AOP Style

In my last blog about AST Transformation I followed a sample example and created AssertParamsNotNull local transformation. Further, I wanted to create AST Transformation which is generic enough and performs any check before method is executed. In process, goal is to learn about AST Transformation.

BeforeAdvisor AST Transformation
Use case for BeforeAdvisor AST involves:
  • Authorization Checking - Security by checking role from context
  • Print Parameter values with which the method is called
  • Asserts Parameters are not null
  • Check various entry-conditions/Pre-Conditions of the method
To make transform generic enough, idea is to inject a method call to before method of advice for each method annotated with @BeforeAdvisor(MyPreConditionAdvice). Advice method can choose to implement any checks/conditions to be performed before actual methods gets executed.

Few of the subtle characteristics of this type of solution are
  • It does not allow changing the method parameters. 
  • It does not allow to stop execution of method. However, you can throw runtime exception.
  • Advice needs no arg constructor and must implement method before

Usage and Client
Starting with class that will use this transform. Following defines script-level method with annotation having advice information.

package com.learn.sts.groovy.ast

@com.learn.sts.groovy.ast.BeforeAdvisor(value= com.learn.sts.groovy.MyAdvice)
def sayHello(name, name2)
{
    println "Hello " + name + name2
}

sayHello("World", "Groovy")
Sample Advice
The advice that we will try to invoke will be something like this. This advice just prints parameter value that method is being invoked with. But you can implement any of the use cases described above.

package com.learn.sts.groovy

public class MyAdvice
{
    def before(String methodName, List listArg)
    {
        println 'Entering Method ' + methodName + ' with params ' + listArg
    }
}
Annotation
Now lets create the Annotation

package com.learn.sts.groovy.ast;

import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
import java.lang.annotation.ElementType
import org.codehaus.groovy.transform.GroovyASTTransformationClass
@Retention(RetentionPolicy.SOURCE)
@Target([ElementType.METHOD])
@GroovyASTTransformationClass(["com.learn.sts.groovy.ast.BeforeAdvisorASTTransformation"])
public @interface BeforeAdvisor {
    Class value ();
}
One difference to this annotation is that it declares a method value(). This allows us to get value being passed during annotation declaration on method.

AST Transformation

package com.learn.sts.groovy.ast

//Imports section skipped for brevity

@GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)
public class BeforeAdvisorASTTransformation implements ASTTransformation
{
    public void visit(ASTNode[] nodes, SourceUnit source)
    {
        AnnotationNode node = (AnnotationNode) nodes[0];
        final Expression classNode = node.getMember("value")
       
        List<MethodNode> allMethods = source.AST?.classes*.methods.flatten()
        List annotatedMethods = allMethods.findAll{ MethodNode method ->
                 method.getAnnotations(new ClassNode(BeforeAdvisor))
                }
        annotatedMethods.each{MethodNode method ->
             List existingStatements = method.getCode().getStatements()
            Parameter[] parameters = method.getParameters()
            int i = 0;
            existingStatements.add(i++, initAdviceCall(classNode))
            existingStatements.add(i++, createMethodCall(method, parameters))
        }
      }
 
    public Statement initAdviceCall(classNode)
    {
      return new ExpressionStatement(
              new DeclarationExpression(
                      new VariableExpression("advice"),
                      new Token(Types.ASSIGNMENT_OPERATOR, "=", -1, -1),
                      new ConstructorCallExpression(classNode.getType(), new ArgumentListExpression())
                      )
              )
    }
 
  public Statement createMethodCall(method, Parameter[] parameters){
      List parameterExpressionList = new ArrayList()
      parameters.each{ parameter -> parameterExpressionList.add(new VariableExpression(parameter))}
      return new ExpressionStatement(
        new MethodCallExpression(
            new VariableExpression("advice"),
            "before",
            new ArgumentListExpression(new ConstantExpression(method.getName()),
                    new ListExpression(parameterExpressionList)
            )
        )
      )
  }
}

Couple of things to notice here:

First, creation of the AST tree involves
  • Creating instance of Advice Class - This is done through method initAdviceCall
  • Invocation of the method before with methodName and Parameter List - This is done through method createMethodCall
Creating AST structure is not the easiest task. One approach that has worked for me is the write the sample code that you are trying to generate AST for and then use Groovy AST viewer in eclipse.

Second, inspecting the value on ASTNode and getting Advice class value. First node contains information about the annotation and second node is the annotated node.

Lessons Learned

Compilation
It was required to compile Transformations files first and then compile the classes that use AST Transformation. If I compiled all three of them together the AST Transformation did not kick in. I used Eclipse IDE and it forced me to use Compile Groovy File explicitly each time I made change. "Build Automatically" or "Clean" option did not work.

Script Level Methods vs all Class Methods         
In AssertParamsNotNull AST Transformation in previous blog, I used source.getAST()?.getMethods()  what it did is that it only found top level (Script Level) methods. So AST Transformation did not apply to Class Methods, it only got applied to Script Level Method. For this one I changed to source.ast?.classes*.methods.flatten() (Line 12 in BeforeAdvisorASTTransformation). This also become apparent once you see AST structure for a given class using Groovy AST View in eclipse.

Retrive value from annotations
ASTNode being passed to visit method carries information about the annotation. First element (node[0]) contains information about annotation. You can get the values passed in to annotation during the visit method and use it during the transformation. In case above we get class passed in as value and it gets instantiated and before method gets invoked.

Groovy Compilation
When groovy compiler is invoked, any sourcefile.groovy goes under series of transformations.
From Source --> ANTLR Tokens --> ANTLR AST --> Groovy AST --> Bytecode

Using AST Transformation, we manipulate the way groovy AST gets generated. It allows to insert additonal statements.

There are various CompilerPhase that goes along with this process. I couldn't find much documentation on the process. Best information is found on Jochen Theodorou's blog post
Blogged with the Flock Browser

Saturday, June 20, 2009

JMX, Groovy, JFreeChart and Swing

Recently, I worked on creating JMX based dashboard application showing Weblogic instances' information. During the exercise I came across JMX and Groovy examples

Using JMX, Groovy, Swing and JFreeChart, I came up with a dashboard application which looks like this.

Memory-Usage

It show memory usage on two instances. It automatically updates every 10 seconds and redraws chart.

Surprisingly (or not so surprisingly), code required to be written to perform this activity is minimal. Specifically thanks to Groovy's SwingBuilder and JMX libraries. Groovy makes the task of using 4 different technologies really easy and fun.

import org.jfree.chart.ChartFactory
import javax.swing.WindowConstants as WC
import javax.management.remote.*
import javax.naming.Context;

/**
* WeblogicJMXReporter monitors Memory value for given node
* @author kartik.shah
*/
public class WeblogicJMXReporter
{

static void main(String[] args)
{
def env = [:]
env["java.naming.factory.initial"] = "weblogic.jndi.WLInitialContextFactory"
env[Context.SECURITY_PRINCIPAL] = "system"
env[Context.SECURITY_CREDENTIALS] = "*******" //Password is always stars
env[JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES] = "weblogic.management.remote"
def jmxServiceURL = new JMXServiceURL("http", "10.50.120.110", 19400,"/jndi/weblogic.management.mbeanservers.domainruntime")
def server = JMXConnectorFactory.connect(jmxServiceURL, env).MBeanServerConnection

def chart1Map = getChart("node1_svr", server);
def piedata1 = chart1Map."pieData"
def chart1 = chart1Map."Chart"
def jvmInfo1 = chart1Map."MBean"

def chart2Map = getChart("node2_svr", server);
def piedata2 = chart2Map."pieData"
def chart2 = chart2Map."Chart"
def jvmInfo2 = chart2Map."MBean"

def swing = new groovy.swing.SwingBuilder()

def frame = swing.frame(title:'Weblogic INT Memory Usage', defaultCloseOperation:WC.EXIT_ON_CLOSE,
size:[800,600], locationRelativeTo: null) {
borderLayout()
panel(id:'canvas') { rigidArea(width:700, height:250) }
}

def bounds1 = new java.awt.Rectangle(0,0, 350,250).bounds
def bounds2 = new java.awt.Rectangle(351,0, 350, 250).bounds
while(true)
{
recalculatePieData(piedata1, jvmInfo1)
recalculatePieData(piedata2, jvmInfo2)

chart1.fireChartChanged()
chart2.fireChartChanged()

frame.pack()
frame.show()
chart1.draw(swing.canvas.graphics, bounds1)
chart2.draw(swing.canvas.graphics, bounds2)
sleep(10000)
}
}

static getChart(nodeName, server)
{
def jvmInfo = new GroovyMBean(server, 'com.bea:Location='+nodeName+',Name='+nodeName+',ServerRuntime='+nodeName+',Type=JVMRuntime')
def piedata = new org.jfree.data.general.DefaultPieDataset()
piedata.setValue "Free", jvmInfo.HeapFreeCurrent
piedata.setValue "Used", jvmInfo.HeapSizeMax - jvmInfo.HeapFreeCurrent
def options = [true, true, true]
def chart = ChartFactory.createPieChart(nodeName, piedata, *options)
chart.backgroundPaint = java.awt.Color.white

["MBean":jvmInfo, "pieData":piedata, "Chart":chart]
}

static recalculatePieData(piedata, jvmInfo)
{
piedata.setValue "Free", jvmInfo.HeapFreeCurrent
piedata.setValue "Used", jvmInfo.HeapSizeMax - jvmInfo.HeapFreeCurrent
println piedata.getValue("Free")
println piedata.getValue("Used")

}
}
For purpose of this blog and keeping it simple, I kept it all in one groovy class.
Blogged with the Flock Browser