Saturday, February 26, 2011

Spring JMS & Groovy: Sending and Receiving Messages

I recently wrote some code using Spring JMS to send a JMS text message to a queue and to receive a response. The examples in the Spring documentation use Java of course to send and receive the messages. Here I wanted to demonstrate how the code can be simplified using Groovy .

In order to send and receive the messages, a JmsTemplate object is required. In a typical case, the template would be configured thru Spring and injected. Here, I'm going to omit the creation process here for brevity's sake, especially given any example would be specific to a particular JMS provider, but the documentation does a great job explaining how to accomplish the creation of a template using Spring.

When sending a text message, we'll need a list of parameters:

  1. The text the message should contain.
  2. The name of the queue to place the request on.
  3. The name of the queue to have the response sent to.
  4. A correlation ID so that we can receive the response created for the message being sent.

Since its simple to get out of the way here, a correlation ID can be created by creating a unique String using:

  def createCorrelationId() { UUID.randomUUID().toString() }

We'll also need a method to create a MessageCreator to give to the JmsTemplate when sending the mesage. A Java example of doing so my look like this using an inner class:

    void sendMessage(JmsTemplate jmsTemplate, String text, String requestQueue, String responseQueue, String correlationId) throws JMSException { 
jmsTemplate.send(requestQueue, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
TextMessage message = session.createTextMessage(text);
return message;
}
});
}

This example can be made easier on the eyes by using as in Groovy since MessageCreator is an interface (we also get the benefit of not needing the throws declarations):

    def sendMessage(jmsTemplate, text, requestQueue, responseQueue, correlationId) { 
jmsTemplate.send(requestQueue, { session ->
session.createTextMessage(text)
} as MessageCreator)
}

Or we can store the MessageCreator in a variable:

    def sendMessage(jmsTemplate, text, requestQueue, responseQueue, correlationId) { 
def messageCreator = { session ->
session.createTextMessage(text)
} as MessageCreator

jmsTemplate.send(requestQueue, messageCreator)
}

In order to set the response queue and correlationID though, we'll need access to the text message created by the Session provided by the JmsTemplate:

    def correlationId = createCorrelationId() 

def sendMessage(jmsTemplate, text, requestQueue, responseQueue, correlationId) {
def messageCreator = { session ->
session.createTextMessage(text)
message.with {
setText request
setJMSCorrelationID correlationId
setJMSReplyTo session.createQueue(responseQueue)
}

} as MessageCreator

jmsTemplate.send(requestQueue, messageCreator)
}

Since the MessageCreator is defined by a closure, we can extract a method out that the closure will have access to when its executed to create the text message:

    def correlationId = createCorrelationId() 

def createMessage(session, text, responseQueue, correlationId) {
def message = session.createTextMessage(text)
message.with {
setText text
setJMSCorrelationID correlationId
setJMSReplyTo session.createQueue(responseQueue)
}
message
}

def sendMessage(jmsTemplate, text, requestQueue, responseQueue, correlationId) {
def messageCreator = { session ->
createMessage(session, request, responseQueue, correlationId)
} as MessageCreator

jmsTemplate.send(requestQueue, messageCreator)
}

Now that we have a method for sending the request message, we need a way to grab the response. A Java example:

    void receiveResponse(JmsTemplate jmsTemplate, String responseQueue, String correlationId) throws JMSException {
String selector = "JMSCorrelationID='" + correlationId + "'";
Message message = (TextMessage) jmsTemplate.receiveSelected(parameters.getRetrieveFromQueue(), selector);
String response;
if (message != null) {
response = message.getText();
}
return response;
}

A Groovier way:

  def receiveResponse(jmsTemplate, responseQueue, correlationId) { 
def selector = "JMSCorrelationID='$correlationId'"
def message = (TextMessage)jmsTemplate.receiveSelected(responseQueue, selector)
message?.getText()
}

In both reception examples, the fact we may not get a response or text in the response. This is something the caller can deal with or we can modify the examples to throw an exception:

   def receiveResponse(jmsTemplate, responseQueue, correlationId) {
def selector = "JMSCorrelationID='$correlationId'"
def message = jmsTemplate.receiveSelected(responseQueue, selector) as TextMessage
def response = message?.getText()

if (!response) {
throw new Exception("Got nothing!") // in practice would likely throw a more apt or custom exception
}

response
}

To put it all together, the code examples above can be aggregated to a class:

class JmsMessageSender {

def createCorrelationId() { UUID.randomUUID().toString() }

def createMessage(session, text, responseQueue, correlationId) {
def message = session.createTextMessage(text)
message.with {
setText text
setJMSCorrelationID correlationId
setJMSReplyTo session.createQueue(responseQueue)
}
message
}

def sendMessage(jmsTemplate, text, requestQueue, responseQueue, correlationId) {
def messageCreator = { session ->
createMessage(session, request, responseQueue, correlationId)
} as MessageCreator

jmsTemplate.send(requestQueue, messageCreator)
}

def createSelectorFrom(correlationId) {
"JMSCorrelationID='$correlationId'"
}

void receiveResponse(jmsTemplate, responseQueue, correlationId) {
def selector = createSelectorFrom(correlationId)
def message = jmsTemplate.receiveSelected(responseQueue, selector) as TextMessage
def response = message?.getText()

if (!response) throw new Exception("Got nothing!")

response
}
}

A client code example using the above class through three calls:

    JmsTemplate jmsTemplate // injected
def correlationId = messageSender.createCorrelationId()
messageSender.sendMessage(jmsTemplate, text, requestQueue, responseQueue, correlationId)
def response = messageSender.receiveResponse(jmsTemplate, responseQueue, correlationId)

Thanks for reading!

Friday, January 28, 2011

Perl: Retrieve URLs with LWP and LWP::Simple

With Perl there are many ways to make requests over the web. One method is to use the LWP module. Below is an example of using it grab the contents of a web page:

use Carp;
use LWP;

my $url = 'http://prystash.blogspot.com';
my $contents = get_contents_from($url);

print $contents;

sub get_contents_from {
my ($url) = @_;

my $agent = LWP::UserAgent->new;
my $request = HTTP::Request->new(GET => $url);
my $response = $agent->request($request);

if (!$response->is_success) {
croak "Could not get URL '$url'";
}

return $response->content
}
Another simpler method is to use the LWP::Simple module:
use Carp;
use LWP::Simple;

my $url = 'http://prystash.blogspot.com';
my $contents = get_contents_from($url);

sub get_contents_from {
my ($url) = @_;
my $contents = get($url) or croak "Could not get URL '$url'";
return $contents;
}

Thursday, January 27, 2011

Removing Files Older than a Certain Number of Days

Using the find command, we can remove files that have not been modified in a certain number of days old using the mtime option:
  -mtime n
File's data was last modified n*24 hours ago. See the comments for -atime
to understand how rounding affects the inter‐pretation of file modification times.
To delete files that are older than 6 months, we can use:
  $ find . -type f -mtime +180 | xargs rm
Or alternatively:
  $ find . -type f -mtime +180 -exec rm {} \;

Tuesday, January 25, 2011

GMaven: A Couple Early Problems Building a Plugin

My first attempt to build a Maven plugin using GMaven got off to a rough start. I don't want to use this space to complain by any means, but I would to share what I learned in case anyone else runs into something similar.

My first problem had to deal with my use of a newer version of GMaven:

      <plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.3</version>
<configuration>
<providerSelection>1.7</providerSelection>
</configuration>
<extensions>true</extensions>
<inherited>true</inherited>
<executions>
<execution>
<goals>
<goal>generateStubs</goal>
<goal>compile</goal>
<goal>generateTestStubs</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>

During the build of the plugin, I was given a deprecation warning stating that no mojo descriptors were found in the project:

[WARNING] Deprecation Alert:
[WARNING] No mojo descriptors were found in this project which has a packaging type of maven-plugin.

I found that reason for the warning was that the stub generation was not retaining the Javadoc annotations used to mark a Mojo. By downgrading to version 1.2 of GMaven and changing the providerSelection to 1.6, the warning went away.

Next when trying to use the plugin in another build, I was present with something ilke:

This realm = plexus.core
urls[0] = file:/opt/apache-maven-2.2.1/lib/maven-2.2.1-uber.jar
Number of imports: 10
import: org.codehaus.classworlds.Entry@a6c57a42
import: org.codehaus.classworlds.Entry@12f43f3b
import: org.codehaus.classworlds.Entry@20025374
import: org.codehaus.classworlds.Entry@f8e44ca4
import: org.codehaus.classworlds.Entry@92758522
import: org.codehaus.classworlds.Entry@ebf2705b
import: org.codehaus.classworlds.Entry@bb25e54
import: org.codehaus.classworlds.Entry@bece5185
import: org.codehaus.classworlds.Entry@3fee8e37
import: org.codehaus.classworlds.Entry@3fee19d8
-----------------------------------------------------
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Internal error in the plugin manager executing goal 'org.prystasj.plugins:jms-testing:1.0-SNAPSHOT:hello': Unable to find the mojo 'hello' (or one of its required components) in the plugin 'org.prystasj.plugins:jms-testing'
org.codehaus.groovy.runtime.GroovyCategorySupport.getCategoryNameUsage(Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicInteger;

I found the solution to this problem was to exclude the groovy-all-minimal jar, version 1.5.7 with:

   <dependency>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-mojo</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all-minimal</artifactId>
</exclusion>
</exclusions>
</dependency>

I also had another that was similar error related to class CallSiteArray that was alleviated by ensuring I was using Groovy 1.6 everywhere.

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.