ICE09 . playing with java, scala, groovy and spring .

Playing with Spring

Archive for December, 2009

File monitoring with Groovy, JMX and Vaadin

Posted by ice09 on December 1, 2009

Note: The complete sourcecode is available on github here. For just downloading, unzipping and importing the project into Eclipse, press on the github site (detailed instructions below).

Besides the Google Web Toolkit (GWT), there is a nice web application framework for Java-only users called Vaadin.
Out-of-the-box, it has a more polished interface and prettier components than GWT.
I used it in a small application, my experience was very positive, even though I used Vaadin in a non-standard way (periodic asynchronous updating of a component by a infinitely running process), it works very well and there was really good support by the Vaadin team.
Note: This post is not an introduction to Vaadin, I hardly know it myself. A great developer-friendly entry are the sample applications. For more info, there is a step-by-step tutorial and a book for deeper understanding.

Intention

Automated testing of a web application is used in a Continuous Integration Environment. Furthermore, load/stress testing of the web application takes place in a Performance Test setting. A process should monitor logfiles in these test environments and report errors in a web-based report.

Technically, a thread must monitor several logfiles, aggregate these errors in a managed component which must be analyzed by a visualizing component (eg. a automatically refreshing table), which must be accessible from several clients.

So, now with technological annotations…

Technically, a thread must monitor several logfiles (Groovy), aggregate these errors in a managed component (Groovy, JMX) which must be analyzed by a visualizing component (eg. a automatically refreshing table), which must be accessible from several clients (web based, Vaadin).

Ok, lets go.

The “server side components”

The server side just consists of a simple Groovy script:

import javax.management.remote.*
import javax.management.*
import groovy.jmx.builder.*
import java.rmi.registry.LocateRegistry

class ServerState implements Serializable {
    def list=[]
}
def jmxbean = new ServerState()

Thread.start {
    def jmx = new JmxBuilder()
    def beans = jmx.export {
        bean(name: "jmx.builder:type=ExportedObject", jmxbean)
    }
    LocateRegistry.createRegistry(9000)
    jmx.connectorServer(port: 9000).start()
}

def worker = { logfile ->
    println "analyzing ${logfile}"
    logfile.withReader { reader -> 
        reader.skip(logfile.length())
        while (true) {
            def line = reader.readLine();
            if (!line) { 
                Thread.sleep(1000); 
            } else {
                def inner = [:]
                inner["server"] = logfile.name
                inner["ts"]= new Date().toString()
                inner["error"] = line
                jmxbean.getList().add(inner)
            }
        }
    }
}

new File("c:/temp/").eachFileMatch(~/^log.txt.*/) { file ->
    Thread.start worker.curry(file)
}

First, a thread is started that exports the aggregating bean via JMX (it’s so easy in Groovy).
After calling jmx.connectorServer(port: 9000).start(), the bean is available under the JNDI-Name service:jmx:rmi:///jndi/rmi://localhost:9000/jmxrmi to client processes (using the name jmx.builder:type=ExportedObject).
Afterwards, a worker closure is created. Nothing special in there, just the reader.skip(logfile.length()) makes sure the end of the file is used at the beginning (before the monitoring process starts).
The last three lines are quite dense, though. Here, all files matching the pattern (eg, log.txt, log.txt1, log.txt2) are used to create threads which monitor these files. This is easily achieved by using currying, which is described in detail here.
In this case, file is applied and the rest (the partially applicated closure) is submitted to Thread.start, which takes a closure (now with an already defined logfile) as the argument.

Tip: Using the Groovy script

All information necessary to run Groovy in any environment is stated here.
However, there is a really cool way of testing Groovy scripts without any settings, etc. Just JAVA_HOME must be set correctly.

  1. Download Groovy, unzip or install to some directory (eg. c:\dev\groovy)
  2. Set your JAVA_HOME environment variable to your Java installation (eg. set JAVA_HOME=c:\java\jdk1.6.0_17).
  3. Start groovyConsole.exe (in \$GROOVY_HOME\bin). Paste the script there. Run it.

Located in GROOVY_HOME\bin: groovyConsole

groovyConsole in action

The “client side”

Testing with jconsole

jconsole is included in the Java SDK (JAVA_HOME\bin)

The JMX-URL is determined by the Groovy script

Result of calling the exported operation getList()

Creating a web app client with Vaadin

  1. Download the Vaadin Eclipse plugin.
  2. Download the zipped files (click on ). It’s subproject Vaadin.
  3. Create new Vaadin project, choose Vaadin jar (cmp. image) – this post was tested with vaadin-6.2.nightly-20091016-c9216.jar.
  4. Delete generated src and Webcontent (!! except WEB-INF/lib !!) directores and copy from downloaded zip.

Structure and Functioning

The JMX-component to connect to Groovy-JMX-server

public class JmxService {

	private JMXServiceURL url;
	private JMXConnector jmxc;
	private MBeanServerConnection mbsc;
	private ObjectName mbeanName;
	
	private static JmxService INSTANCE;
	
	private JmxService() {
		try {
			url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9000/jmxrmi");
			jmxc = JMXConnectorFactory.connect(url, null);
			mbsc = jmxc.getMBeanServerConnection();
			mbeanName = new ObjectName("jmx.builder:type=ExportedObject");
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MalformedObjectNameException e) {
			e.printStackTrace();
		} catch (NullPointerException e) {
			e.printStackTrace();
		}
	}
	
	public static JmxService getInstance() {
		if (INSTANCE == null) {
			INSTANCE = new JmxService(); 
		}
		return INSTANCE; 
	}
	
	public ServerState getServerState() {
		return JMX.newMBeanProxy(mbsc, mbeanName, ServerState.class, true);
	}

}

The table with its on-click-listener is created in pure Java

	private Table initTable(final IndexedContainer container) {
		final Table table = new Table();
		table.setContainerDataSource(container);
		table.setWidth("100%");
		table.setPageLength(PAGE_LENGTH);
		table.setColumnExpandRatio("error", 1);
		table.setSelectable(true);
		final Application self = this;
		table.addListener(new Table.ValueChangeListener() {
			@Override
            public void valueChange(ValueChangeEvent event) {
				Object o = table.getValue();
                if (o != null) {
                	preformattedText = new Label("Here can be sources");
                    preformattedText.setContentMode(Label.CONTENT_PREFORMATTED);
                    self.getMainWindow().addComponent(preformattedText);                
            	} else {
                    self.getMainWindow().removeComponent(preformattedText);                
                }
          	}
        });
		return table;
	}

The Refresher”pattern” is explained here, the sources are in the repository. All necessary sources are also included in the github sources to this post.

Running the project

You can just start the server-side Groovy script (after configuring the file matching loop at the end of the script corresponding to your system). Afterwards, just start the Vaadin project with “Run on Server”.
Now, you have to change a monitored file (eg. add a line and save the file). If you have chosen “Aktualisierung starten” (ie. start polling for new JMX events) the changed line should appear in the table. You can choose the line (details view is not implemented yet) to display more information. The table scrolls accordingly (the last item is always displayed at the bottom of the table).
The refresh mechanism necessary for polling the JMX state is copied from here.
Note: for real usage, it would be much better to use JMX notification for this use case.

Advertisements

Posted in Groovy, Vaadin | Leave a Comment »