Rapid Application Development mit Groovy

Einführung

Als ich 2007 meinen Linux-Rechner mal wieder aufräumte, benötigte ich ein Programm, das mir anzeigt, welche Dateien am meisten Plattenplatz belegen. Ich konnte im Web keins finden. Zu dieser Zeit lernte ich Groovy und hatte gehört, dass damit "Rapid Application Development" möglich sei. Also habe ich es ausprobiert.

Anforderungen: Der Benutzer wählt ein Verzeichnis. Die Inhalte dieses Verzeichnisses werden dann vom Programm als Kuchendiagramm bezüglich der Summe der Dateigrößen dargestellt. Wenn der Nutzer ein Unterverzeichnis anklickt, geht es rekursiv weiter.

Screenshot of disk usage tool

Das Diagramm wird mit der JFreeChart-Bibliothek gezeichnet. Diese ist in Java geschrieben und kann daher mit Groovy verwendet werden. Das ist einer der Gründe, warum ich seit 2007 für Skripte statt Perl Groovy verwende: die hohe Anzahl von qualitativ hochwertigen Bibliotheken.

Anmerkungen zu Design und Implementierung

Überblick

Das Programm muss die folgenden drei Aufgaben erfüllen.

  1. Die Inhalte des Verzeichnisses und der Unterverzeichnisse müssen eingelesen und gespeichert werden.
  2. Das GUI muss designed und implementiert werden.
  3. Der Chart muss mit den Daten aus dem ersten Schritt gefüllt werden.

Schritt 1

Hier gibt es einen einfachen rekursiven Algorithmen, wenn man die Java Klasse File benutzt. Die Daten werden in einem Baum gespeichert.

Allerdings ist dieser Schritt bei großen Verzeichnissen auf Mehrkern-Prozessoren durch Parallelität verbesserbar. Aufgrund dem Einlesen der Verzeichnisse wartet der Prozessor auf die Festplatte. Zu dieser Zeit kann ein anderer Prozessor schon weiterarbeiten.

Schritt 2

Die Implementierung ist mit dem Groovy Swing Builder schnell erledigt. Das Fenster im obigen Screenshot wurde mit den folgenden einfachen Statements erzeugt.

def swing = new SwingBuilder()
def frame = swing.frame(title: 'Disk Usage V0.2', layout: new GridLayout(1,1), 
                        minimumSize: [width: 600, height: 500], defaultCloseOperation:WC.EXIT_ON_CLOSE) {
	menuBar  {
		menu(mnemonic: 'F', 'File') {
			menuItem(action: actionChoose)
			separator()
			menuItem(action: actionExit)
		}
		menu(mnemonic: 'H', 'Help') {
			menuItem(action: actionAbout)
		} 
	}
	splitPane(id: 'panes') {
		scrollPane(minimumSize: [width: 200, height: 100]) {
			tree = tree(id: 'tree', model: treeModel, cellRenderer: renderer)
		}
		scrollPane() {
			panel(id:'canvas', layout: new GridLayout(1,1) ) { 
				widget(new ChartPanel(chart))
			} 
		}
	}
}

Die hierarchische Struktur des Swing GUIs wird durch die Block-Struktur reflektiert. Wie in Java und C/C++ werden Blöcke mit geschweiften Klammern geöffnet und geschlossen. Für jede Swing-Klasse Jxyz gibt es einen "Wrapper" im SwingBuilder, z. B. "menu" für "JMenu" und "panel" für "JPanel". Die Aktionen und die Dialoge werden wie folgt definiert.

def actionAbout = swing.action( name:'About ...', closure: this.&actionAbout, mnemonic: 'A', accelerator: 'F1' )
def fileChooserDialog = swing.fileChooser(id: 'fileChooser', fileSelectionMode: JFileChooser.DIRECTORIES_ONLY )
DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer()
renderer.setLeafIcon(renderer.getClosedIcon()) // Only display folder icons in the tree

Das Tortendiagramm wird folgendermaßen initialisiert.

def piedataset = new DefaultPieDataset(); // the data of the chart
def chart = ChartFactory.createPieChart3D('untitled', piedataset, true, true, true)
chart.backgroundPaint = Color.white
PiePlot3D pieplot3d = (PiePlot3D) chart.getPlot();
pieplot3d.setForegroundAlpha(0.6F);
pieplot3d.setNoDataMessage('No data to display');

Schritt 3

Die Daten im Tortendiagramm werden mit dem folgenden Code initialisiert. Die Klasse Element ist ein Knoten im Baum.

private void updatePieDataSet(Element node) {
	piedataset.clear()
	for (child in node.children) {
		piedataset.setValue(child.format(format), child.sum)
	}
}

Fazit

Mit Groovy funktioniert "Rapid Application Development". Die Voraussetzung ist natürlich, dass man sich mit Java-Bibliotheken und Groovy schon auskennt.

Ergänzung 2012: Parallelisierung mit GPars

Die Performance konnte mit GPars erheblich verbessert werden. Hier wird einfach der Threadpool mit GParsPool.withPool gestartet und für jedes Verzeichnis wird ein neuer Task mit GParsPool.runForkJoin gestartet.

Download

Der Code ist bei GitHub verfügbar.