Monday, February 11, 2008

Plugins for local needs

The Grails plugin system is great not only for creating all kinds of useful functionality available publicly, but also is a perfect framework to extract bits and pieces of reusable logic suitable only for internal corporate projects.

At our organization we started finally using Grails for internal projects. The first small web application was done in a matter of a few days. But then when it was time to package it up as a 'war', we had a little dilemma and that's where Grails plugin system came to the rescue.

In a nutshell, we use Spring Security (formerly known as ACEGI) together with JA-SIG Central Authentication Service, for all our auth/authz needs. For Grails applications, I did not choose Grails Acegi plugin as it does not seem to provide CAS authentication support (yet?). So we reverted back to good 'ol Spring resources.xml and dropped all the verbose Acegi bean definitions there (hopefully Spring Security 2 should reduce the 'configuration chaos' dramatically). Works just fine. But we have 3 "String" bean definitions there pertaining to CAS properties, such as 'casServiceUrlPrefix', 'casLoginUrl', and 'casValidateUrl'. So, for the typical Spring web app., we define those as JNDI-bound resources, like so:


<bean id="casServiceUrlPrefix" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>java:comp/env/casServiceUrlPrefix</value></property>
</bean>

<bean id="casLoginUrl" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>java:comp/env/casLoginUrl</value></property>
</bean>

<bean id="casValidateUrl" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>java:comp/env/casValidateUrl</value></property>
</bean>
So, when packaging application as a 'war' and deploying it to Tomcat, this is fine, but in the 'development' mode, in embedded Jetty, I would have liked to put this config somewhere else. Like conf/Config.groovy for example. That's where Grails plugin system came in, and with its wonderful SpringBeanBuilder I was able easily dynamically create those 3 bean definitions during assembly phase of the ApplicationContext, depending on the environment the application was running in.

Here's the plugin:


import grails.util.GrailsUtil as GU
import org.springframework.jndi.JndiObjectFactoryBean

class CasPropertiesResolverGrailsPlugin {
def version = 0.1
def author = "Dmitriy Kopylenko"
def title = "This plugin is used to provide cas related properties to the Spring beans used for Spring Security config"
def description = ''' The plugin participates in the creation phase of the Spring application context
which is used to configure Spring Security related beans and dynamically provides
'configuration beans' namely 'casServiceUrlPrefix', 'casLoginUrl', and 'casValidateUrl'
depending on the environment in which Grails application is preparing to run.

More specifically, if the environment is 'development' then the value is sourced from
Grails Config instance. On the other hand, if the environment is 'production', the value
is sourced from standard ess JNDI namespace.
'''

def dependsOn = [:]

def doWithSpring = {
switch (GU.environment) {
case 'development':
casServiceUrlPrefix(java.lang.String, application.config.casServiceUrlPrefix)
casLoginUrl(java.lang.String, application.config.casLoginUrl)
casValidateUrl(java.lang.String, application.config.casValidateUrl)
break
case 'production':
casServiceUrlPrefix(JndiObjectFactoryBean) {
jndiName = 'java:comp/env/casServiceUrlPrefix'
}
casLoginUrl(JndiObjectFactoryBean) {
jndiName = 'java:comp/env/casLoginUrl'
}
casValidateUrl(JndiObjectFactoryBean) {
jndiName = 'java:comp/env/casValidateUrl'
}
break
}
}
...

I'm sure there are other ways to do this, but that was fun nevertheless :-)

Later...

4 comments:

Anonymous said...

actually there is a cas client plugin which is accessible from grails plugin repository.

Dmitriy Kopylenko said...

Yeah, I'm aware of that, but we use Acegi in combination with CAS.

Shawn Hartsock said...

You have any notes on doing Acegi with CAS in Grails?

Dmitriy Kopylenko said...

For us it's just plain old acegi xml. Now that Spring Security 2.0 is out, I want to try the simplified config.