Saturday, October 18, 2008

My First Groovy Script

I just read the Groovy In Action book, which i think is great. It´s the kind of book that makes you want to put everything in practice right away.

So, i started to look for somewhere to put it in practice... i looked at almost every piece of code in my current project and started to imagine how to put a little groovy into it.. I found a place or two where to add some groovy sintactics.

Then i decided to create a mini administration console to control some aspects of my application. And i decided to make it in groovy.. Maybe i'm not following any programming rule or not even using groovy the right way. I just know that i had a lot of fun making this mini script which am sharing here..

Higlights:

- The Console will access MBeans in a local or remote machine so the first thing was creating a connection to JMX Server based in a parameter provided IP.
- Each operation that you can do in the console follows the same basic idea. A label that identifies the name of the action, One or more TextFields to Introduce Data, and an action to make.
- So the "Operation" class defined what i needed. A name property, a numberOfInputs property and a closure (action) property. 
- I made use of the easy configuration of properties in groovy. No getters and setters, no public or private. Just a "def" word. nice :) .
- Then i made the list of operations. With the easy array sintax. No "new" to create the list. just put each element in the [].
- The construction and setting of properties in each operation was also nice.. not calling to any setter. just setting values in the construction of the object. This was even nicer when i set the closure property.
- The best here was that i knew that the code in the closure property of each operation was ready to be invoked, as is. nothing else needed. The closures receive a list parameter which i'll talk about in a few lines.
- The next great thing to do was to create the interface. For that i choosed SwingBuilder. Another great thing of groovy. I'm not very much into Swing. As a matter of fact this Layout Manager things have always given me headaches. So i wont talk abput swing builder here. Just letting you know that for small simple interfaces it is really easy to use.
- The next groovy sintax i used was the so important foreach loop. which was a great addition to Java 5 also (with a little diferent sintax) so i wont talk about it here.
- Next is the range use  and the list appender operator in lines 
           (0..
                        currentTextFields<
                    }

That couple of lines. Printed as much textfields as number of inputs declared on the Operation object and also added them to another list. for the current control. beautiful.. i think.

- The last interesting line is the one which calls the closure

currentControl.closure.call(currentTextFields*.text)

That line calls the closure of the current control component with a list of the values of all its textfields... Imagine the Java equivalent of making a list of all the values of each textfield... i think it is not that easy..

Well that was my first Groovy Script ever.. and as i said.. i really loved it to see it work.. and also loved the fact of editing the file in notepad, saving and executing.. no compiling or anything (i'm basicaly a Java developer so compiling is day to day).. even droping the file in another machine (with groovy instaled) and executing... no compile.

Next is the code....
 

The administration console was basically a Swing frame with a few labels, textfields and buttons.


import groovy.swing.*
import java.awt.*
import javax.swing.*
import org.jasypt.util.text.*
import es.indra.lloyds.si.groovy.encripter.ui.*
import javax.management.*;
import javax.management.remote.*;

def iparg=args[0]
JMXServiceURL serviceURL= new JMXServiceURL("service:jmx:rmi:///jndi/rmi://$iparg:1993/server");
JMXConnector  con=JMXConnectorFactory.connect(serviceURL, null);
MBeanServerConnection connection=con.getMBeanServerConnection();


def init(ip){
    println "haciendo init"
    this.serviceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://$ip:1993/server");
    this.con=JMXConnectorFactory.connect(serviceURL, null);
    this.connection=con.getMBeanServerConnection();
}

swing = new SwingBuilder();
def controles=[
    new Operation(name:"Cambiar IP del servicio",numberOfInputs: 1,closure:{
            lista->
            def ip=lista[0]
            init ip
            labelip.text="IP DE SERVIDOR $ip"
            areaTexto.text="INVOCACION EXITOSA"
        }),
    new Operation(name: "Resumir Servicio ",numberOfInputs: 1,closure:{
            lista->
            def name=lista[0]
            connection.invoke(new ObjectName(/Mule.instance-1:type=org.mule.Service,name="$name"/), "start", null,null)
            areaTexto.text="INVOCACION EXITOSA"
        }),
    new Operation(name:"Pausar Servicio ",numberOfInputs:1,closure:{
            lista-> 
            def name=lista[0]
            connection.invoke(new ObjectName(/Mule.instance-1:type=org.mule.Service,name="$name"/), "stop", null,null)
            areaTexto.text="INVOCACION EXITOSA"
        }),
    new Operation(name:"Generar Fichero HTML de estadisticas",numberOfInputs:0,closure:{
            lista->
            def htmlString=connection.invoke(new ObjectName(/Mule.instance-1:type=org.mule.Statistics,name=AllStatistics/), "printHtmlSummary", null,null)
            def file=new File("stats.html")
            file<
            areaTexto.text="INVOCACION EXITOSA fichero stats.html generado"
        }),
    new Operation(name:"Generar Fichero XML de estadisticas",numberOfInputs:0,closure:{
            lista->
            def htmlString=connection.invoke(new ObjectName(/Mule.instance-1:type=org.mule.Statistics,name=AllStatistics/), "printXmlSummary", null,null)
            def file=new File("stats.xml")
            file<
            areaTexto.text="INVOCACION EXITOSA fichero stats.xml generado"
        }),
    new Operation(name:"Ver Elementos de Cola Persistete",numberOfInputs:0,closure:{
            listax->
            def lista=connection.invoke(new ObjectName(/Mule.Additional:name=Test Operations/), "watchPersistentQueueContent", null,null)
            areaTexto.text=lista.toString()
        }),
    new Operation(name:"Eliminar elemento no respondido de cola persistente",numberOfInputs:0,closure:{
            listax->
            def lista=connection.invoke(new ObjectName(/Mule.Additional:name=Test Operations/), "pollFromQueue", null,null)
            areaTexto.text=lista.toString()
        }),
    new Operation(name:"Ver el ultimo numero de secuencia recibido de Inversis",numberOfInputs:0,closure:{
            listax->
            def id=connection.invoke(new ObjectName(/Mule.Additional:name=Test Operations/), "seeLastSequenceId", null,null)
            areaTexto.text=id
        }),
    new Operation(name:"Setear el Ultimo numero de secuencia recibido de Inversis",numberOfInputs:1,closure:{
            listax->
            def  value=listax[0]
            Object[] arrOb=[value]
            String[] arrStr=["java.lang.String"]
            def id=connection.invoke(new ObjectName(/Mule.Additional:name=Test Operations/), "setLastSequenceId",arrOb,arrStr) 
            areaTexto.text=id

        }),
    new Operation(name:"Ver el contenido del Fichero X en Modo Bucle. Dar ruta",numberOfInputs:1,closure:{
            listax->
            def location=listax[0]
            def file=new File(location)
            def reader=file.newReader()
            Thread.start {
                while(true){
                    def line=reader.readLine()
                    if(line)areaTexto.append(line+"\n")
                }
            }            
        }),
    new Operation(name:"Ver los Servicios sobre los que se pueden invocar PAUSAR o RESUMIR",numberOfInputs:0,closure:{
            listax->
            areaTexto.text= 
                """
                dataQueueSender      - Servicio que envia desde ActiveMQ a la cola DataQueue\n
                customPersistentService      - Servicio que envia desde Cola Persistente a la cola DataQueue
                """
        
        }),
    new Operation(name:"Transformar mensaje de Texto a XML",numberOfInputs:3,closure:{
            listax->
            def  serviceName=listax[0]
            def  messageName=listax[1]
            def  message=listax[2]
            Object[] arrOb=[serviceName,messageName,message]
            String[] arrStr=["java.lang.String","java.lang.String","java.lang.String"]
            def id=connection.invoke(new ObjectName(/Mule.Additional:name=Test Operations/), "testTextToSmlTransformation",arrOb,arrStr) 
            areaTexto.text=id
        }),
    new Operation(name:"Transformar mensaje de XML a Texto",numberOfInputs:1,closure:{
            listax->
            def  value=listax[0]
            println value
            Object[] arrOb=[value]
            String[] arrStr=["java.lang.String"]
            def transformed=connection.invoke(new ObjectName(/Mule.Additional:name=Test Operations/), "testXmlToTextTransformation",arrOb,arrStr) 
            areaTexto.text=transformed
        }),
   new Operation(name:"Probar Send Mail",numberOfInputs:0,closure:{
            listax->
            connection.invoke(new ObjectName(/Mule.Additional:name=Test Operations/), "sendMail", null,null)
            areaTexto.text="INVOCACION EXITOSA revise el mail"
        }),

   new Operation(name:"Ver Colas ActiveMQ",numberOfInputs:0,closure:{
            listax->            
            areaTexto.text="Para esta invocacion se asume que firefox esta instalado en C:/Archivos de programa/Mozilla Firefox/.\n Si no lo tiene, vaya a su explorador favorito y teclee el URL http://${iparg}:8161/admin"
            try{
                "C:/Archivos de programa/Mozilla Firefox/firefox.exe http://${iparg}:8161/admin".execute()
                }catch(Exception e){
                }
        })

]

gui = swing.frame(title:'LLOYDS_SI Administration Console', size:[800,800],defaultCloseOperation:2) {
    panel(){
        boxLayout(axis:BoxLayout.Y_AXIS)
        labelip=label(text:"IP de Servicio localhost")
        panel(){
            boxLayout(axis:BoxLayout.Y_AXIS)
            for(control in controles){
                panel(){
                    flowLayout(alignment:3)
                    swing.label(text:control.name)
                    def currentTextFields=[]
                    def currentControl=control
                    (0..
                        currentTextFields<
                    }
                    button(id:control.name,text:"Ejecutar",actionPerformed:{
                            println currentControl.name
                            try{
                                currentControl.closure.call(currentTextFields*.text)
                            }catch(Exception e){
                                e.printStackTrace()
                                areaTexto.text="Error tratando de Realizar la Operacion"
                            }
                        })
                }
            }
        }
        scrollPane(){
            areaTexto=textArea(columns:500,rows:20)
        }
    }
}

gui.show();



class Operation {
    def name
    def numberOfInputs
    def closure
}