Wednesday, July 28, 2010

Building a Browser for Amazon S3 with Grails

Are you using Amazon S3 and considering learning Grails? Or are you a Grails developer and beginning to look at Amazon S3?
Check out my Amazon article:
Building a Browser for Amazon S3 with Grails

Thursday, June 24, 2010

DRY with Grails namedQueries and a Super-Dynamic Search Strategy

I just finished developing a simple query engine with Grails 1.2.1 for one of my clients. As usually, Grails did most of the hard work. The requirement was to provide a query engine for customer-specific flat files that contained all information about transactions that occurred in the last year. Previously the flat files were delivered to the clients as Microsoft Access binary files. The problem was that the clients never took the initiative to write apps to search and aggregate the information. So I was brought in to use Grails to rapidly develop a web application that provided search and aggregation of the client-specific web flat files.

The resulting Grails application is hosted on Amazon EC2 with MySQL. Anyway, I wanted to share with you my use of Grails 1.2's namedQueries and my simple strategy to support a dynamic search in the standard list.gsp.

The application's list page shows all the columns of the table. To do that it needs to know the properties of the domain. Each client has a custom domain class specific to their flat file (which, for some reason, have different column names.) The domain classes have a getColumnProperties():
static List getColumnProperties() {
def columnProperties = []
CustDomainName.properties.declaredFields.each {prop ->
if (prop.modifiers == 2 &&
!NO_SHOW_COLS.find {it == prop.name} ) {
columnProperties << prop
}
return columnProperties
}
The column properties are passed to list.gsp as [properties:domain.columnProperties] so the page can list all columns:



<tbody>
<g:each in="${domainInstances}" status="i" var="domainObj">
<tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
<g:each in="${properties}" var="property">
<td>${domainObj[property.name]}</td>
</g:each>
</tr>
</g:each>
</tbody>


Each column has a search added to the the top of the table:


<tr>
<g:each in="${properties}" var="property">
<td>
<% if (property.type.name != 'java.util.Date') { %>
<input type="text" name="${property.name}" value="${(params[property.name])}"
<% if (property.type.name == 'java.lang.String') {%>
title="Enter a character prefix to filter ${property.name}"
<% } else { /* assume numeric */ %>
title="Enter a numeric value to filter ${property.name}"
<% } %>
/>
<% } else { %>
<richui:dateChooser format="MM/dd/yyyy" name="${property.name}" value="${(params[property.name])}" />
<% } %>
</td>
</g:each>
</tr>
<tr>
<g:each in="${properties}" var="property">
<td>
<% if (property.type.name == 'java.util.Date') { %>
<richui:dateChooser format="MM/dd/yyyy" name="${property.name}To" value="${(params[property.name+'To'])}" />
<% } else if (property.type.name != 'java.lang.String') {
def propOp = property.name+'_Op'
%>
<g:select name="${propOp}" value="${params[propOp]}"
from="${['eq', 'gt', 'ge', 'lt', 'le', 'ne']}"
valueMessagePrefix="critera.operator" />
<% } %>
</td>
</g:each>
</tr>


Note how dates have a from and to with the RichUI dateChooser and numerics have operators. Strings work as prefixes to the SQL like clause. The list action is then implemented as follows:


def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
params.offset = params.offset ? params.int('offset') : 0
params.sort = params.sort?:'id'
params.order = params.order?:'desc'

List domainInstances = domain.dynaCrit(params) // .list(params) ignors sort
.findAllByIdGreaterThan(0,
[max: params.max, offset:params.offset,
sort:params.sort, order:params.order])

int count = domain.dynaCrit(params).count()

[domainInstances: domainInstances, count:count,
properties:domain.columnProperties, params:params]
}


Prior to namedQueries (with Grails 1.2.) I've always had to copy-and-paste criterion logic from the search to the count query. This violated the Don't Repeat Yourself (DRY) principle. Now I use a named query. Note that I did have an issue with the sort facilities in the list method so I did a hack with the findAllByIdGreaterThan (perhaps this is fixed with Grails 1.3.) Finally, here's the namedQuery in the domain class:

static namedQueries = {
dynaCrit {params ->
params.each {filterProp ->
if (filterProp.value) {
def property = CustDomainName.columnProperties.find {it.name == filterProp.key}
if (property) {
if (property.type == String) {
ilike ("$property.name", filterProp.value+'%')
} else if (property.type == Date) {
between ("$property.name", filterProp.value, params[filterProp.key+'To'])
} else {
"${(params[filterProp.key+'_Op'])}" ("$property.name", property.type.newInstance(filterProp.value.trim()))
}
}
}
}
}
}

The application does provide a number of other handy features:
  • The ability on the list page to remove (and re-add) columns
  • Dynamic summary reports with prompt pages prompting for groupBy and sum columns along with filter criterion.
  • Excel download option of the user-defined report
  • The ability to save user-defined queries.
What I found amazing during the development of this application was how Grails made it ridiculously easy to develop dynamic reporting.

Thursday, June 10, 2010

Handling Select-Multiple with Grails

When a page sends multiple values with the same HTTP parameter name, Grails stuffs them into an ArrayList. That is very handy but there's an issue when the user only selects one value. When only one value is passed Grails does not put it into an array. And your code has to detect whether or not the parameter is an array.

Perhaps the best solution is to use a Command object. The following, for example, handles the user selection of one or multiple items (orders, qtys, etc.):


class BuildReturnItemsCommand {
String[] itemNo
int[] orderNo
int[] qty
BigDecimal[] unitPrice
String[] desc
}

[recent add: check out http://www.intelligrape.com/blog/2010/06/14/getting-params-attribute-as-list/ for another solution]

But, I'm lazy. If I have simple input where no validation is required, I don't want to take the time to create a command object. The following shows the preamble of a Controller closure that handles a single selection of a multiple-select list of regions:


def salesReport = {
params.regions = (params.regions)?[params.regions].flatten():null

Groovy to the rescue!
The ternary, if there is a regions parameter, stuffs the regions into a List. But, because the parameter might already be a list, it is flattened.

The following Groovy snippet tests this code:

def params = [:]
params.regions = 'West'
params.regions = (params.regions)?[params.regions].flatten():null

assert params.regions == ['West']

params.regions = ['East', 'North', 'South', 'West']
params.regions = (params.regions)?[params.regions].flatten():null

assert params.regions == ['East', 'North', 'South', 'West']

Thursday, May 6, 2010

Groovy wrapping of long table td text with no blanks

Subtitle: Groovy's a Wrap

Just today I had a client -- who had just tested a fairly complex GSP -- ask to have text in a table data element wrap. He had tested it with a long string of characters with no spaces. He wanted:

asldfjas;fjasodfsaodjfsaodfdsadfa;ljlj

To show as:

asldfjas;fj
asodfsaod
jfsaodfdsa
dfa;ljlj

And not just the first 12 characters that fit in the td.


Quick solution: Hey buddy, use spaces and the browser will wrap for you.
Groovy solution: Insert thespaces for them.

I took the Groovy option. What I did was extend the String class in BootStrap to have a wrapAt method:


class BootStrap {

def init = { servletContext ->
String.metaClass.wrapAt = {width ->
matcher = (delegate =~ /\w{1,$width}/);
def that = ''
matcher.each {
that += "${it.trim()} "
}
return that
}
}
def destroy = {
}
}


Then, in my GSP (where the em width was 12) I added:

<td>${option.value.wrapAt(12)}</td>


I tested the code with the following:


String.metaClass.wrapAt = {width ->
println delegate
matcher = (delegate =~ /\w{1,$width}/);
def that = ''
matcher.each {
that += "${it.trim()} "
}
return that
}

def str = 'one two thetimehascomeforalgoodmentocometotheaidoftheircountry'
assert "one two thetimehas comeforalg oodmentoco metotheaid oftheircou ntry " == str.wrapAt(10)


Sure, not a perfect solution, but all the text the user keyed is viewable on the page (and the optional PDF that is generated from that page.) It looks like this:

one two
thetimehasco
meforalgoodm
entocometoth
eaidoftheirc
ountry

Also, it gave me a use for Groovy meta programming

Friday, April 16, 2010

Kindle for Technical Content

I'm a reader. I started reading novels at a very young age. As a developer and reader, I'm addicted to reading technical books. I am also a technical writer so I'm constantly buying and reading books. And I'm not a fan of reading on-line. When I sit in front of my PC, I expect the instant gratification of coding or writing. I find it difficult to read a PDF book cover-to-cover (or should that be byte-to-byte?) Case in point: To save money, I opted for the PDF version of Venkat Subramaniam's "Programming Groovy: Dynamic Productivity for the Java Developer." Great book, by the way. But I ended up getting frustrated and printed the book -- which ended up costing me more in paper and ink than that print version would have.

A month ago I was at Barnes and Nobles and I became intrigued by the Nook. The Nook is B&N's book reader. I almost bought it. Luckily I backed out of the store and began researching the Nook and compared it with Amazon's Kindle.

The Nook is pretty neat but, unlike the Kindle, it does not have a keyboard. With the Nook, you can bookmark your documents -- but you can't highlight text or add notes. The Kindle has an integrated keyboard and you can highlight and add notes.

I got my Kindle yesterday. And, so far, I love it.

Book Availability:

I am currently researching Cloud Computing for use in Grails applications using both Amazon Web Services and Google App Engine. To that end I've been wanting to buy two books: "Programming Amazon Web Services" and "Programming Google App Engine" (both from O'Reilly.) Curiously, the Amazon book was not available for Kindle at Amazon's site but the Google book was. So I bought the GAE book from Amazon and went to the O'Reilly site for the AWS book. At O'Reilly I found that they do make an eBook download available that is Kindle compatible (.mobi.) O'Reilly gives you both the Kindle and PDF versions of the book. To get the AWS book's mobi file on my Kindle I simply plugged in the Kindle's USB and copied the mobi file from my PC to the Kindle's documents directory.

My next issue was that I wanted to get some of my PDF books on the Kindle. All I had to do was plug in the USB and copy the PDFs from the PC to my Kindle. There are two issues with PDF on the Kindle: 1) It shows the entire page on the screen, which requires a very small font (but it is readable). 2) While you can add bookmarks to a PDF, you can't add notes or highlight text. Note that I believe there are PDF to mobi converters available. But, for right now, I'm happy with Kindle's PDF reader.

Reading Experience:

I was reticent to get an electronic reading device because I thought I needed the spacial context of a printed book. When I first get a printed copy of technical book, I put a half-dozen sticky notes inside the front cover. Then I hook a highlighter and a mechanical pencil on the back cover. I use the sticky notes to bookmark important sections and my current page. Those sticky notes stay in the book forever. When I go to my book shelve, I usually find what I'm looking for by going to the various bookmarked pages.

As I read my print books, I use the highlighter on important sections and I use the pencil to make notes. If I'm doing research for an article, I also write notes on the front cover with page references.

The Kindle does all that for me only in a cleaner, greener fashion. First off, I love that when I put the Kindle down (which has been difficult) and power it off and then later return to the Kindle and power it back on, it goes right to the page of the document that I was last reading. I also love that I can make bookmarks with notes and then Kindle shows me my bookmarks, notes, and highlights in an easy to read index. For article research, the notes are invaluable. For programming, the bookmarks and highlights are extremely useful.

I was also concerned about the display of tables and code on the Kindle. But I discovered that the Kindle allows you to change the view from portrait to landscape (a feature the Nook does not provide.) The Kindle also allows you to change your font size (which the Nook allows as well.)

At any rate... I'm loving my Kindle

Monday, February 8, 2010

Free Book on Grails

Three years ago, when I first began developing with Grails, I benefited from a book by Jason Rudolph called "Getting Started with Grails" The book was well written, short, and available as a free PDF. I recommended it to everyone interested in Grails. But, as Grails rapidly improved, the book began to become outdated.

No longer.

Jason Rudolph teamed up with Scott Davis (author of "Groovy Recipes") to update the book. It now covers Grails 1.2 and, once again, I highly recommend it.
You can download the book from:
http://www.infoq.com/minibooks/grails-getting-started
But note that InfoQ will ask you to register with their site before you can download the PDF. But it is well worth it as InfoQ is a great site with lots of technical information, in form of articles and videos, on application development.

Friday, February 5, 2010

Comment on IE

Half my work is creating Web solutions for my clients.
The other half of my work is getting it to work on Internet Explorer....