Monday, January 10, 2011

Groovy: Sorting a Map by Values

Here's a real quick Groovy snippet demonstrating one way to sort a Map by the values stored in its entries (mostly so I don't forget how to do it):

def map = ["ghi":6, "abc":4 ,"def":5]
def sortedByValue = map.sort { a,b -> a.value <=> b.value }
println sortedByValue.keySet()
The output from this snippet is:
[abc, def, ghi]
Anyone have any other methods for doing the same thing?

Saturday, January 8, 2011

Using Maven to Publish and Verify Schemas

We use Maven to publish schemas and other documents, like WSDLs, that we would like to share across projects. This makes publishing schema releases, along with schemas that are in-development, easy for consumption by clients. Using Maven also allows to easily to publish the schema together with example documents as a resource bundle in addition to validating the examples messages against the schema during the build so we know the schema and what we would expect a message to look like are in sync.

Here I'm going to demonstrate our base project structure and the minimum POM we use for our schema projects. I'll follow that up by adding in additional plugins to make the project more worthwhile.

To start, we'll use a real simple schema, people.xsd, describing a list of people with each person being described with a first name, last name, and his/her age:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="people">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="unbounded" ref="person"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="person">
<xs:complexType>
<xs:sequence>
<xs:element name="firstName" type="xs:string"/>
<xs:element name="lastName" type="xs:string"/>
<xs:element name="age" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

An example document containing two people:

<people>
<person>
<firstName>George</firstName>
<lastName>Costanza</lastName>
<age>40</age>
</person>
<person>
<firstName>Cosmo</firstName>
<lastName>Kramer</lastName>
<age>42</age>
</person>
</people>

At the root of the project, we of course have the pom.xml. The schema and example documents are kept in the src/main/resources directory:

  pom.xml
src/main/resources/people.xsd
src/main/resources/twoPeople.xml
src/main/resources/onePerson.xml

The POM itself is relatively simple. By default, Maven will look to package a JAR. We have no classes in this project, but the files in the src/main/resources directory will be packaged. Since we want to publish the schema as a separate artifact, we can use the Build Helper plugin to attach the schema to the build for publishing. The location of the schema is defined by the schema property, which we will reuse later:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.prystasj.schemas</groupId>
<artifactId>people</artifactId>
<version>1.0-SNAPSHOT</version>
<name>People Schema</name>
<properties>
<schema>src/main/resources/${artifactId}.xsd</schema>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<phase>package</phase>
<id>attach-artifacts</id>
<goals>
<goal>attach-artifact</goal>
</goals>
</execution>
</executions>
<configuration>
<artifacts>
<artifact>
<file>${schema}</file>
<type>xsd</type>
</artifact>
</artifacts>
</configuration>
</plugin>
</plugins>
</build>
</project>

Now we can run the install phase and see that the schema is installed to our local repository:

$ mvn install
[INFO] Scanning for projects...
...
[INFO] [install:install {execution: default-install}]
[INFO] Installing /home/prystasj/workspace/prystasj/writing/maven-xsd/target/people-1.0-SNAPSHOT.jar to /home/prystasj/.m2/repository/org/prystasj/schemas/people/1.0-SNAPSHOT/people-1.0-SNAPSHOT.jar
[INFO] Installing /home/prystasj/workspace/prystasj/writing/maven-xsd/src/main/schemas/people.xsd to /home/prystasj/.m2/repository/org/prystasj/schemas/people/1.0-SNAPSHOT/people-1.0-SNAPSHOT.xsd
...

The schema, along with the JAR, are installed separately to the local repository, and of course we can deploy it for public consumption by running the deploy phase.

Since we have a couple of sample messages, we can add a step to the build to validate them against the schema to help insulate us against any inconsistencies if the schema ever changes. To do just that, we'll add an execution of the XML Maven Plugin:

      <plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>xml-maven-plugin</artifactId>
<version>1.0-beta-3</version>
<executions>
<execution>
<goals>
<goal>validate</goal>
</goals>
</execution>
</executions>
<configuration>
<validationSets>
<validationSet>
<systemId>${schema}</systemId>
<dir>src/main/resources</dir>
<excludes>
<exclude>${artifactId}.xsd</exclude>
</excludes>
</validationSet>
</validationSets>
</configuration>
</plugin>

The systemId property is used to define the location of the schema we want to validate against relative to the base of the project. Here I'm using the value of the schema property from the original version of the POM. The dir property describes the directory containing the instance documents to validate. I've added an exclude element to ensure the schema itself is not used.

When we run the install phase now, we should see the following build output if the validation succeeds:

[INFO] [surefire:test {execution: default-test}]
[INFO] No tests to run.
[INFO] [xml:validate {execution: default}]
[INFO] [jar:jar {execution: default-jar}]

To make sure things are setup correctly, let's add an instance document that we know should be considered invalid, src/main/resources/invalidPerson.xml, which defines one person without the required age element:

<people>
<person>
<firstName>George</firstName>
<lastName>Costanza</lastName>
</person>
</people>

Now our build fails, citing:

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] While parsing /home/prystasj/workspace/prystasj/writing/maven-xsd/src/main/resources/invalidPerson.xml, at file:/home/prystasj/workspace/prystasj/writing/maven-xsd/src/main/resources/invalidPerson.xml, line 5, column 12: cvc-complex-type.2.4.b: The content of element 'person' is not complete. One of '{age}' is expected.

Finally, we can add the Remote Resources Plugin to create a resource bundle for clients to take advantage of. We do have the JAR that is built that contains both the schema and example documents that client can unpack using the Dependency Plugin, but providing a resource bundle makes things easier, as the contents of the bundle are unpacked as a result of a depending on the bundle. An example use case for a client would be ensuring the creation of messages to be sent are valid.

The resource bundle can be created by adding the plugin:

      <plugin>
<artifactId>maven-remote-resources-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<goals>
<goal>bundle</goal>
</goals>
<configuration>
<includes>
<include>**/*.xml</include>
<include>**/*.xsd</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>

The build will now create a file describing the resources to be included in the bundle:

<?xml version="1.0" encoding="UTF-8"?>
<remoteResourcesBundle xsi:schemaLocation="http://maven.apache.org/plugins/maven-remote-resources-plugin/remote-resources/1.1.0 http://maven.apache.org/plugins/maven-remote-resources-plugin/xsd/remote-resources-1.1.0.xsd"
xmlns="http://maven.apache.org/plugins/maven-remote-resources-plugin/remote-resources/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<remoteResources>
<remoteResource>twoPeople.xml</remoteResource>
<remoteResource>people.xsd</remoteResource>
<remoteResource>onePerson.xml</remoteResource>
</remoteResources>
</remoteResourcesBundle>

A client can now depend on the bundle using the same plugin:

      <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-remote-resources-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<resourceBundles>
<resourceBundle>org.prystasj.schemas:people:1.0-SNAPSHOT</resourceBundle>
</resourceBundles>
</configuration>
</execution>
</executions>
</plugin>

With the addition of the plugin in the client build, the schema and example documents will be available on the classpath:

target/maven-shared-archive-resources/twoPeople.xml
target/maven-shared-archive-resources/people.xsd
target/maven-shared-archive-resources/onePerson.xml
target/classes/twoPeople.xml
target/classes/people.xsd
target/classes/onePerson.xml

Hope this helps demonstrate some potential uses for Maven that might be a little outside-the-box, but useful none the less.

Monday, November 8, 2010

Griffon: Signing an Application

Last week, I tried using Griffon to help investigate the possibility of creating a Java Web Start application. Everything went smoothly with the exception of some trouble I had signing the application for the "production" environment. To help those in a similar situation, I'll summarize the steps I needed to get things singed below signed below.

Note:I used this thread on Markmail to and the Griffon Quick Start guide to help me out.

I created my application as the guide suggests with:

  $ griffon create-app DemoConsole

Now the guide offers some suggestions for making the resulting application more useful, but here I'm just going to do the bare minimum I know to get the application deployed.

Next, in order to sign the jars in the application for production use, we need to create a keystore:

  $ keytool -genkey -alias GriffonKey

Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]: John Prystash
...
Enter key password for <GriffonKey>
(RETURN if same as keystore password):

I used prystasj for both the keystore and key password, this being information we'll need to know later. The resulting keystore file was created at: /home/prystasj/.keystore

The signing and key information is determined from the file griffon-app/conf/BuildConfig.groovy. Below is the relevant file information in its original form:

// key signing information
environments {
//...
production {
signingkey {
params {
sigfile = 'GRIFFON'
keystore = 'CHANGE ME'
alias = 'CHANGE ME'
// NOTE: for production keys it is more secure to rely on key prompting
// no value means we will prompt //storepass = 'BadStorePassword'
// no value means we will prompt //keypass = 'BadKeyPassword'
lazy = false // sign, regardless of existing signatures
}
}

griffon {
jars {
sign = true
pack = true
destDir = "${basedir}/staging"
}
webstart {
codebase = 'CHANGE ME'
}
}
}
}

We'll need to point the production configuration at the keystore we created earlier and set the alias to GriffonKey. We'll also add the password we gave to the keytool program, which is prystasj in both cases. Finally, we'll be publishing the app to a webserver, whose location we set in the codebase property:

// key signing information
environments {
//...
production {
signingkey {
params {
sigfile = 'GRIFFON'
keystore = '/home/prystasj/.keystore'
alias = 'GriffonKey'
storepass = 'prystasj'
keypass = 'prystasj'
lazy = false // sign, regardless of existing signatures
}
}

griffon {
jars {
sign = true
pack = true
destDir = "${basedir}/staging"
}
webstart {
codebase = 'http://myhost.org/prystasj/democonsole/'
}
}
}
}

Now we can build the app with:

  $ griffon prod package webstart

The result is a zip file at dist/webstart. Taking this zip we can unpack on the webserver at the location we set in the codebase property.

Next, we can download the application and run it from: http://myhost.org/prystasj/democonsole/application.jnlp.

Hope this helps anyone else looking to get a Griffon application signed.

Wednesday, October 20, 2010

Groovy: Retrieving the Value of Multiple XML Elements

Yesterday, I ran into an interesting case at work where some code was parsing XML using Groovy's XmlSlurper to retrieve the value of an element and treat it as a String. Something along the likes of:

  def xml = "<xml><character>a</character></xml>"
def node = new XmlSlurper().parseText(xml)
String result = node.character
println result

Which simply prints out a. The code was expected to only return value of the first element found, but when a second element is added:

  def xml = "<xml><character>a</character><character>b</character></xml>"
def node = new XmlSlurper().parseText(xml)
String result = node.character
println result

The resulting output is ab, which broke a new test case.

One solution to the issue is to grab the first element with:

  String result = node.character[0]

Another interesting point is that the result of node.character[0] is a NodeChild, not a String. Since the type of the result variable is declared, the right side of the assignment was coerced to a String. If that were not the case, and we had:

  def result = node.character[0]
println result.getClass().getName()
println result

The output would be:

  groovy.util.slurpersupport.NodeChildren
a

Note:We need to use getClass() as result.class would return the class node and not the Class object itself.

Alternatively, we can use the text() method of NodeChildren (which also exists for NodeChild) to ensure we get a String:

  def result = node.character[0].text()
println result.getClass().getName()
println result

Giving us:

  java.lang.String
a

To summarize, putting all the methods discussed so far to use with:

  def xml = "<xml><character>a</character><character>b</character></xml>"
def node = new XmlSlurper().parseText(xml)

println node.character
println node.character.getClass().getName()
println()

println node.character.text()
println node.character.text().getClass().getName()
println()

println node.character[0]
println node.character[0].getClass().getName()
println()

println node.character[0].text()
println node.character[0].text().getClass().getName()
println()

We get:

  ab
groovy.util.slurpersupport.NodeChildren

ab
java.lang.String

a
groovy.util.slurpersupport.NodeChild

a
java.lang.String

While the original gotcha might not be all that hard to resolve, hopefully this gives a little insight to those who might explore things a little bit further.

Tuesday, October 19, 2010

Bash: Learning about Arrays

Last year, I wrote about a little Groovy script to help me validate XML documents against a schema. A ended up with a little script to call to help me from having to recall how to run it using Maven. The script takes a schema location and an example instance document as arguments:

  #!/bin/bash
mvn -o exec:java -Dexec.mainClass=Validator -Dexec.args="$1 $2"

Which I can call with:

  $ ./validate.sh order.xsd order.xml

We store example XML documents along with our internal schemas, and sometimes I find myself running the same script multiple times, once for each document in the project, so I thought having a script to loop through the documents in a directory and report the results would also be helpful.

In order to report both the 'good' instances and the 'bad' instances at the end of the scripts run, I needed to learn a little about bash arrays.

In found that in bash, there are multiple ways to create an array. You can start by simply assigning a value to a yet unused array:

  #!/bin/bash
good[0]="my.xml"
echo ${good[0]}
Or you can declare an array using:
  #!/bin/bash
declare -a good = ("my.xml", "your.xml")
echo ${good[0]}
echo ${good[1]}

In my case, I will be iterating through the files and adding them to the appropriate array, so I won't be able to declare the array or its size up front. I used this method to first check if the array is empty in an if statement, and if so declare and initialize it. If the array does in fact exist, I append an element to in the else clause, by using @ to get the length of the array:

  #!/bin/bash
if [ ${#bad[0]} -eq 0 ]; then
declare -a bad=("$i")
else
bad=("${bad[@]}", $i)
fi

To roll everything up, my new scripts takes the schema location and a directory as arguments. For every, directory listing in the directory, I run the Groovy code against it. If the validation failed, setting $? to 1, I add the file to the 'bad' list. Otherwise, it goes to the good list:

  #!/bin/bash
xsd=$1
dir=$2

for i in `ls $dir`
do
echo -n "$i..."
mvn -o exec:java -Dexec.mainClass=Validator -Dexec.args="$1 $2/$i"

if [ $? -eq 1 ]; then
if [ ${#bad[0]} -eq 0 ]; then
declare -a bad=("$i")
else
bad=("${bad[@]}", $i)
fi
else
if [ ${#good[0]} -eq 0 ]; then
declare -a good=("$i")
else
good=("${good[@]}", $i)
fi
fi
done

echo
echo "Good files: "
echo "${good[@]}"
echo
echo "Failed files: "
echo ${bad[@]}
echo

At the end of script, I simply echo both lists. An example of the output for the report:

  Good files: 
good1.xml good2.xml good3.xml

Failed files:
bad1.xml bad2.xml bad3.xml

I'm sure I could remove some of the duplication, but I since I have only two arrays and this is just a helper, I think I'll leave things be for me. If you have any more array advice, please leave a comment!

Friday, October 1, 2010

Trang: Creating Schemas from XML

Does anyone like writing XML schemas? Sometimes they can be frustrating, and yet always ends up feeling simple when you're done. When given the choice, it always feels good to me to start writing a schema from an example instance document, and of course there are plenty of tools to help.

While a lot of tools are available for a price, Trang however is free, and helps me with the writer's block I tend to get when I'm handed an XML document and asked to make a schema from scratch.

Trang is a Java app that can be downloaded in zip form. Luckily (for me at least), it was available in the Ubuntu package repositores:

$ sudo apt-get install trang

Now that we have Trang installed, let's generate a schema from a simple XML document, languages.xml:

<?xml version='1.0' encoding='UTF-8'?>
<languages>
<language>
<name>Groovy</name>
<platform>JVM</platform>
<appeared>2003</appeared>
</language>
<language>
<name>Scala</name>
<platform>JVM</platform>
<appeared>2003</appeared>
</language>
<language>
<name>Boo</name>
<platform>CLR</platform>
<appeared>2003</appeared>
</language>
</languages>

Now let's tell Trang, gratefully, to make us a schema:

$ trang languages.xml languages.xsd

And a schema is generated for us:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="languages">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" ref="language"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="language">
<xs:complexType>
<xs:sequence>
<xs:element ref="name"/>
<xs:element ref="platform"/>
<xs:element ref="appeared"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="name" type="xs:NCName"/>
<xs:element name="platform" type="xs:NCName"/>
<xs:element name="appeared" type="xs:integer"/>
</xs:schema>

This is a real good start, but I feel like I should make a couple changes. To make things a little easier to understand for consumers, I think I'll change the uses of NCName to string:

<xs:element name="name" type="xs:NCName"/>
<xs:element name="platform" type="xs:NCName"/>

And require at least one language element for the document to be valid:

<xs:element minOccurs="1" maxOccurs="unbounded" ref="language"/>

My edited schema becomes:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="languages">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="unbounded" ref="language"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="language">
<xs:complexType>
<xs:sequence>
<xs:element ref="name"/>
<xs:element ref="platform"/>
<xs:element ref="appeared"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="name" type="xs:string"/>
<xs:element name="platform" type="xs:string"/>
<xs:element name="appeared" type="xs:integer"/>
</xs:schema>

Even with this simple example, Trang has saved me a lot of typing. Trang also has the options to also create RELAX NG and DTD documents if you need them.

Tuesday, September 21, 2010

Perl & Bash: Mass Substitutions from the Command Line

Today I found myself in a situation where I had to do the same global substitution for every file in a directory. I considered writing a script to handle it, but I wanted to try using a Perl one-liner to get the job done.

Given a directory containing a file with the following contents:

  This is a XML file
This is a XML file
I can substitute every occurence of XML with text using:
  $ perl -i -pe 's/XML/text/g' file1.xml
Afterwards, the file now reads:
  This is a text file
This is a text file
If I wanted to do the substitution for every file in the current directory, I can wrap the command in a bash for loop:
  $ for i in `ls`; do perl -i -pe 's/XML/text/g' $i; done

This isn't revolutionary by any means, but its harder for me to forget this technique if I post it here.