Search

Dark theme | Light theme

August 28, 2009

Grassroots Groovy: the Spring Route (part 2)

We can introduce Groovy into our projects at grassroots level. In a previous post we learned how to invoke Groovy code from Spring configured applications. In this post we learn how to create the Spring configuration files with Groovy. We are using the BeanBuilder from Grails to achieve this. The builder implements a Spring DSL to configure beans. We can define beans and wire beans together using Groovy code, so this means we can add logic to the configuration. For example we could check an environment variable and depending on the value we define certain beans.

To define the beans we use Groovy, but we can still use Java code to read the configuration and get a valid Spring ApplicationContext. This way the impact on the Java code is minimal, but we can create the Spring configuration more dynamically. First we start with the Spring BeanBuilder file SpringBeansConfig.groovy:

import com.mrhaki.blog.*
import org.apache.commons.dbcp.*

beans {
    // We can use namespaces in the builder.
    xmlns util:"http://www.springframework.org/schema/util"
    
    // We can write code in this configuration file and reference
    // binding variables.
    def bindingEnv = env
    util.list(id: 'runningMode') {
        value new Date().format('yyyy-MM-dd')
        value bindingEnv
    }
    
    // Abstract bean definition with custom destroy-method.
    abstractDataSource(BasicDataSource) { bean ->
        bean.destroyMethod = 'close'
        driverClassName = 'com.mysql.jdbc.Driver'
        username = ''
        password = ''
    }
    
    // Create bean definitions dynamically and use abstract parent.
    def urls = [
        dev:  'jdbc:mysql://localhost/db',
        test: 'jdbc:mysql://localhost/test',
        prod: 'jdbc:mysql://prod'
    ]
    urls.each { key, value ->
        "${key}DataSource"(BasicDataSource) { bean -> 
            bean.parent = abtractDataSource
            url = value
        }
    }
    
    // Spring bean definitions for bean with id personDao
    // and class PersonDaoImpl. Sample switch statement to show
    // we can add Groovy code in this file.
    personDao(PersonDaoImpl) {
        switch (env) {
            case 'dev':  dataSource = devDataSource; break
            case 'test': dataSource = testDataSource; break
            case 'prod': dataSource = ref(prodDataSource); break
        }
    }
    
    // Equivalent of
    // <bean id="personService"
    //    class="com.mrhaki.blog.PersonServiceJavaImpl">
    //    <property name="dao" ref="personDao"/>
    // </bean>
    personService(PersonServiceJavaImpl) {
        dao = ref(personDao)
    }
}

We have now defined the following beans: runningMode, devDataSource, testDataSource, prodDataSource, abstractDataSource, personService, personDao and wired some together. With the following Java application we read this file and use the Spring ApplicationContext:

package com.mrhaki.blog;

import java.io.IOException;
import java.util.List;
import java.util.Date;
import java.text.SimpleDateFormat;
import grails.spring.BeanBuilder;
import groovy.lang.Binding;
import org.springframework.context.ApplicationContext;
import org.apache.common.dbcp.BasicDataSource;

public class SpringBuilderApp {
    public static void main(String[] args) throws IOException {
        // Create a binding to get "env" into the configuration script.
        final Binding binding = new Binding();
        binding.setProperty("env", "dev");
        
        // Create a builder and find all files on the classpath
        // ending with BeansConfig.groovy.
        final BeanBuilder builder = new BeanBuilder();
        builder.setBinding(binding);
        builder.loadBeans("classpath:**/**BeansConfig.groovy");
        
        // Make the Spring ApplicationContext.
        final ApplicationContext context = builder.createApplicationContext();
        
        assert 7 == context.getBeanDefinitionNames().length;
        assert context.containsBean("prodDataSource");
        assert context.containsBean("testDataSource");
        assert context.containsBean("devDataSource");
        assert context.containsBean("abstractDataSource");
        assert context.containsBean("personDao");
        assert context.containsBean("personService");
        assert context.containsBean("runningMode");

        final List mode = (List) context.getBean("runningMode");
        final Date now = new Date();
        assert new SimpleDateFormat("yyyy-MM-dd").format(now).equals(mode.get(0));
        assert "dev".equals(mode.get(1));
        
        final BasicDataSource ds = (BasicDataSource) context.getBean("testDataSource");
        assert "jdbc:mysql://localhost/test".equals(ds.getUrl());
        assert "com.mysql.jdbc.Driver".equals(ds.getDriverClassName());
        assert "".equals(ds.getUsername());
        assert "".equals(ds.getPassword());
    }
}

We can run this application with assertions enabled and we get no errors. We must include the grails-spring-<version>.jar and grails-bootstrap-<version>.jar, the Commons DBCP libraries and the Groovy libraries to the classpath. Now we can define the Spring context with Groovy and we can run Groovy code from Spring. More and more Groovy creepes into our Java projects.