Montag, 26. Mai 2008

Groovy Builders, JCR (and MigLayout to the rescue)

I still am struggling with the JCR Query functionality. The whole idea of supporting both XPath and/or SQL-like syntax with some additions is, for me personally, rather challenging.

What to do? Either read docu, source code, create tests against the API or create a little GUI to test it with.

So, finally a good excuse to use the SwingBuilder. Beeing in dire need of a refresh on Swing (remember Java 1.2? Sheesh...) I stumbled upon MigLayout, which, kind of ironically, plays to my (meager) CSS know-how.

In order to have some test data available, I make use of a JCR Builder, which is described here. Groovy builders are a nice way to create hierarchical structures.

And this is what the GUI looks like:



import groovy.swing.SwingBuilder
import net.miginfocom.swing.MigLayout
import javax.swing.WindowConstants as WC
import javax.jcr.Repository
import org.apache.jackrabbit.core.TransientRepository
import javax.jcr.Session
import javax.jcr.SimpleCredentials
import ch.bluepenguin.jcr.groovy.builder.JcrBuilder
import javax.jcr.Workspace
import javax.jcr.query.QueryManager
import javax.jcr.query.Query
import javax.jcr.query.QueryResult
import javax.jcr.NodeIterator
import javax.jcr.ValueFormatException


class JCRGroovy implements Runnable {
def swing
def builder
Session session

public void run() {
swing = new SwingBuilder()

def queryTextField = swing.textArea(rows: 5, columns: 20)
queryTextField.text = "/jcr:root/blueprint/element(*,nt:unstructured)"
def valueTextField = swing.textArea(rows: 5, columns: 20)
def deleteTextField = swing.textField()
valueTextField.text =
"""blueprint() {
master1(['name':'String',
'type':'String']) {
detail()
}
master2(){
references('otherMaster':'../master1')
}
}
"""

deleteTextField.text ="/blueprint"
def resultTextField = swing.textArea(rows: 20, columns: 20)
Repository repository = new TransientRepository();
session = repository.login(new SimpleCredentials("username", "password".toCharArray()))
builder = new JcrBuilder()
builder.session = session
def gui = swing.frame(title: 'JCRQuery', defaultCloseOperation: WC.EXIT_ON_CLOSE) {
panel(layout: new MigLayout("fill")) {
widget(queryTextField, constraints: 'grow')
button(text: 'Query', constraints: 'span, aligny bottom', actionPerformed: {
resultTextField.text = queryAction(queryTextField.text, valueTextField.text)
})

widget(valueTextField, constraints: 'grow')
button(text: 'Create', constraints: 'span, aligny bottom', actionPerformed: {
resultTextField.text = createAction(valueTextField.text)
})
widget(deleteTextField, constraints: 'grow')

button(text: 'Delete', constraints: 'span, aligny bottom', actionPerformed: {
resultTextField.text = deleteAction(deleteTextField.text)
})
//label(text: 'Result')
scrollPane(constraints: 'wrap, grow') {{
widget(resultTextField)
}
}
}
gui.pack()
gui.show()
}

def deleteAction(path) {
def results = ""
try {
def relPath = path.charAt(0) == '/' ? path.substring(1) : path
session.rootNode.getNode(relPath).remove()
session.save()
} catch (Exception e) {
results = e.toString()
}
return results
}

def queryAction(query, model) {
return executeQuery(query, printClosure)
}

def createAction(model) {
return buildModel(model)
}

def executeQuery(queryString, clos) {
def results = ""
try {
Workspace workspace = session.getWorkspace();
QueryManager qm = workspace.getQueryManager();
Query query = qm.createQuery(queryString, Query.XPATH);
QueryResult queryResult = query.execute();
NodeIterator nodeIterator = queryResult.nodes
for (n in nodeIterator) {
results += clos.call(n)
}
} catch (Exception e) {
results = e.toString()
}
return results
}

def printClosure = {node ->
def filler = " "
def results = ""
def to = node.depth
for (i in 0..(to-1)) filler += " "
results += filler + node.path + '\n'
node.properties.each {property ->
try {
results += filler + " " + property.name + ": " + property.string + '\n'
} catch(ValueFormatException wft) {
results += filler + " " + property.name + ": (multiple values) \n"
}
}
return results
}

def removeClosure = {node ->
try {
if (node.path == "/" || node.path.startWith("/jcr:system")) {
println "wont remove root node"
} else {
node.remove()
}
} catch (Exception e) {}
}

def buildModel(model) {
GroovyShell groovyShell = new GroovyShell(new Binding([builder: builder]))
def modelScript = "builder." + model + "\nbuilder.build({})"
return groovyShell.evaluate(modelScript)
}
}


So with some fumbling and trial & error a quick testing GUI to check for those JCR Query result. Of course, many things are open:
  • XPath queries support
  • use of a JTree instead of a dump message
  • "remember me" functionality
But it does the trick and demonstrates (once again) the real power of Groovy: while beeing a real cool language in itself, the usage of such powerfull tools as Jackrabbit and MigLayout are a breeze.

Keine Kommentare: