Monday, December 13, 2010

Grails AS400 RPG Connection Pool

In my last post I said: "For the most part, I did not have to do anything overly special to work with the AS400" in the development of the AS400-based Kettler USA retail site. The one area that took a bit of Grails-magic was some direct calls to RPG and some indirect calls to RPG via messages. The direct calls were done via JDBC stored procedures, so the standard JDBC connection pool set up by the Grails DataSource.groovy configuration worked fine. But for the message-base communication, I used AS400 data queues -- which are not supported by JDBC. So I needed to create an AS400 connection object. You really don't want to create an AS400 connection object for each request. Instead you want to use IBM's AS400 Connection Pool.

Once the AS400 Connection Pool is available, I'm able to use it in my service class like so:

class PreAuthorizationService implements Serializable {
def as400ConnPool
String preAuth(Cart cart){
AS400 conn = getConnection()
...

But where do you create the AS400 Connection Pool and when? Grails makes that easy.

In grails-app/conf/spring/resources.groovy place the following code:

import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH
beans = {
if (CH.config.dataSource.driverClassName ==
'com.ibm.as400.access.AS400JDBCDriver') {
as400ConnPool(AS400ConnPoolFactory) {bean ->
ip = CH.config.iseriesIPAddress
userId = CH.config.iseriesUserId
password = CH.config.iseriesPwd
}
}
}

I put the IP, username, and password in Config.groovy (hence the use of ConfigurationHolder. Also note that, because I often use a local MySQL-based AS400-connection-less test environment, I predicate the creation of the AS400 connection so it is only created if the IBM driver was active. The IBM AS400 Connection Pool needs a bit of Spring infrastructure, which I coded in my AS400ConnPoolFactory wrapper class:

import org.springframework.beans.factory.FactoryBean
import org.springframework.beans.factory.InitializingBean

import com.ibm.as400.access.AS400
import com.ibm.as400.access.AS400ConnectionPool

class AS400ConnPoolFactory implements FactoryBean {
String ip
String userId
String password

public Object getObject() throws Exception {
// note: try/catch removed for brevity
AS400ConnectionPool as400ConnPool = new AS400ConnectionPool()
as400ConnPool.setMaxConnections(128)
as400ConnPool.fill(ip, userId, password, AS400.COMMAND, 5)
return as400ConnPool
}
public Class getObjectType() {AS400ConnectionPool.class}
public boolean isSingleton() {true}
}


It's all pretty simple. AS400 connection pools made easy by Grails!

Thursday, December 9, 2010

Grails 400 Experiences

I received a number of responses to my post about the launch of the retail site www.kettlerusa.com. I was happy to hear that there are folks out there that are either considering or using Grails on the AS400 (a.k.a. iSeries, System-i.) I will be doing a series of posts on my experiences Grails and the AS400.

For the most part, I did not have to do anything overly special to work with the AS400. DB2/400 supports all the features that Grails (via Hibernate) uses. The biggest issue was working with tables that have composite keys. Note that, although Grails has facilities for working with composite keys, rather than keying the required GORM code, I used the Systemi Grails Domain Plugin. This plugin has a script that takes an AS400 table and generates the Grails domain class. I have used that plugin (which I wrote with help from Mike Brown) to generate hundreds of Grails domain classes for tables on about a dozen different AS400s (all at different companies.) You can read more about the tool in my article Rapid IBM i Web Dev with Open Source Tools (note that article will require a web subscription to www.systeminetwork.)

KETTLER USA was deployed to a 64-bit Windows server that is running on Tomcat. The Tomcat server is behind a firewall with an Apache server running outside the firewall as a proxy server. The Tomcat server is able to connect to the AS400 via JDBC. The Tomcat server can only be accessed from the Apache server so it is a secure AS400 connection.

Most of the application is written using Grails domain classes to access DB2/400, but there are some points where the application does direct calls to RPG via JDBC stored procedures or indirect RPG calls via AS400 data queues. I will be elaborating on this in a future post.

I've been coding 12x7 for so many months getting the Kettler retail site up that I've forgotten some of the slick things that I was able to do with Grails (where coding the solution in Java would have been far more difficult.) But one thing, off the top of my head, that I loved being able to do was to put the Grails domain classes in their own Grails project and pull that project into the UI project as a Grails plugin. The reason why I needed to do this was that I had written a Business-to-business Grails application for Kettler last year. And the retail application used many of the same Grails domain classes. As they say "there shall be no duplicate code." And I certainly didn't want to maintain the 85 domain classes required for Kettler in two places. With one of the more recent versions of Grails (we are on 1.3.5 now) Grails made using your own project Grails plugins very easy. Instead of having to generate a Grails zip archive and import it to the project that requires that plugin -- everytime that supporting project is changed -- you simply put a reference to that project in your grails-app/conf/BuildConfig.groovy:

grails.plugin.location.'shared-domain-plugin' = "../SharedDomainPlugin"


Again, I will be added more Grails400 posts in the future.

Thursday, December 2, 2010

Grails 400 Retail Site Now Online

I haven't posted in months because I was developing a Grails-based retail web site www.kettlerusa.com. I've been primarily developing Grails applications for the past 4 years now and I'm still impressed with Grails. This is the first retail site that I've developed -- all my prior sites have been internal, or business-to-business password based applications that I couldn't easily share. The back-end database is DB2/400 running on an iSeries/AS400 box. The application has some direct integration with RPG via stored procedures and data queues but otherwise it uses the Grails domain classes and GORM to work with the DB2/400 database.

If you are interested in Fitness, Bikes, Toys, Patio, or Table Tennis, check out www.kettlerusa.com.

In future posts I plan to blog about issues I had in the development of this application that were elegantly solved with Groovy and/or Grails.

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