Under the Hood: Using Spring in Grails

Burt Beckwith
SpringSource




                                                                 CONFIDENTIAL
                                   © 2010 SpringSource, A division of VMware. All rights reserved
Who Am I


 Java developer for over 13 years

 Background in Spring, Hibernate, Spring Security

 Grails developer for 5 years

 SpringSource employee on the Grails team

 Created or reworked over 40 Grails plugins

 http://burtbeckwith.com/blog/

 https://twitter.com/#!/burtbeckwith
                         CONFIDENTIAL               2
Spring Overview
 Main functions of Spring
 • Bean container: ApplicationContext and BeanFactory
 • Dependency Injection (DI) and Inversion of Control (IoC)
 • Proxies
   • Transactions
   • Security
   • Caching
 • Event publishing and listening
 • Exception conversion
                             CONFIDENTIAL                     3
Grails Services




      CONFIDENTIAL   4
Grails Services


 What is a Grails Service?




                              CONFIDENTIAL   5
Grails Services


 What is a Grails Service?

 • Groovy class in grails­app/services




                              CONFIDENTIAL   6
Grails Services


 What is a Grails Service?

 • Groovy class in grails­app/services

 • Great place for business logic




                              CONFIDENTIAL   7
Grails Services


 What is a Grails Service?

 • Groovy class in grails­app/services

 • Great place for business logic

 • A Spring bean, by default singleton scope




                              CONFIDENTIAL     8
Grails Services


 What is a Grails Service?

 • Groovy class in grails­app/services

 • Great place for business logic

 • A Spring bean, by default singleton scope

 • Automatically transactional unless configured otherwise



                              CONFIDENTIAL                   9
Grails Services


 What is a Grails Service?

 • Groovy class in grails­app/services

 • Great place for business logic

 • A Spring bean, by default singleton scope

 • Automatically transactional unless configured otherwise

 • Proxied, sometimes multiple times
                              CONFIDENTIAL               10
Manually Wiring




      CONFIDENTIAL   11
Grails Services
                    src/java/test/UserManager.java
  package test;

  import other.LoggingService;
  import auth.User;

  public class UserManager {

      private LoggingService loggingService;

      public void setLoggingService(LoggingService service) {
         loggingService = service;
      }

      public User createUser(String username) {
         User user = new User();
         user.setUsername(username);
         user.save();
         loggingService.logMessage(
              "Created user with username " + username);
         return user;
      }
  }

                                  CONFIDENTIAL                  12
Grails Services

                   grails-app/conf/spring/resources.groovy


  import test.UserManager

  beans = {
     userService(UserManager) {
        loggingService = ref('loggingService')
     }
  }




                                  CONFIDENTIAL               13
Grails Services

                   grails-app/conf/spring/resources.groovy


  import test.UserManager

  beans = {
     userService(UserManager) { bean ->
        bean.autowire = 'byName'
     }
  }




                                  CONFIDENTIAL               14
How to Make it Transactional?




             CONFIDENTIAL       15
Grails Services

                          grails-app/conf/spring/resources.groovy

import org.codehaus.groovy.grails.orm.support.GroovyAwareNamedTransactionAttributeSource
import org.springframework.transaction.interceptor.TransactionProxyFactoryBean

import test.UserManager

beans = {

    userService(TransactionProxyFactoryBean) { bean ->
       bean.lazyInit = true

        target = { innerBean ->
           innerBean.lazyInit = true
           innerBean.autowire = 'byName'
           innerBean.beanClass = UserManager
        }

        proxyTargetClass = true

        def props = ['*': 'PROPAGATION_REQUIRED'] as Properties
        TransactionAttributeSource =
            new GroovyAwareNamedTransactionAttributeSource(transactionalAttributes: props)

        transactionManager = ref('transactionManager')
    }
}

                                          CONFIDENTIAL                                 16
or




CONFIDENTIAL   17
Grails Services
                    src/java/test/UserManager.java
  package test;

  import org.springframework.transaction.annotation.Transactional;
  import other.LoggingService;
  import auth.User;

  @Transactional
  public class UserManager {

      private LoggingService loggingService;

      public void setLoggingService(LoggingService service) {
         loggingService = service;
      }

      public User createUser(String username) {
         ...
      }
  }




                                  CONFIDENTIAL                       18
Grails Services

                    grails-app/conf/spring/resources.groovy

import test.UserManager

beans = {

    userService(UserManager) { bean ->
       bean.lazyInit = true
       bean.autowire = 'byName'
    }
}




                                   CONFIDENTIAL               19
How to be sure it's 
 Transactional?




        CONFIDENTIAL   20
Grails Services
                            src/java/test/UserManager.java
package test;

import     org.springframework.transaction.TransactionStatus;
import     org.springframework.transaction.annotation.Transactional;
import     org.springframework.transaction.interceptor.TransactionAspectSupport;
import     org.springframework.transaction.support.TransactionSynchronizationManager;

    ...

    public User createUser(String username) {

        if (TransactionSynchronizationManager.isSynchronizationActive()) {
           TransactionStatus status = TransactionAspectSupport.currentTransactionStatus();
           System.out.println("TX is active; isRollbackOnly: " + status.isRollbackOnly());
        }
        else {
           System.out.println("Uh-oh, TX not active");
        }

        ...
    }
}




                                            CONFIDENTIAL                                 21
Grails Services
                  grails-app/services/test/UserService.groovy

  package test

  import auth.User

  class UserService {

      def loggingService

      User createUser(String username) {
         def user = new User(username: username)
         user.save()
         loggingService.logMessage(
              "Created user with username $username")
         user
      }
  }




                                    CONFIDENTIAL                22
Transaction Helper Methods




           CONFIDENTIAL      23
Utility Methods




  import o.s.t.interceptor.TransactionAspectSupport
  import o.s.t.support.TransactionSynchronizationManager

  for (sc in grailsApplication.serviceClasses) {
     def metaClass = sc.clazz.metaClass

     …
  }




                           CONFIDENTIAL                    24
Utility Methods




  // returns TransactionStatus
  metaClass.getCurrentTransactionStatus = { ­>
     if (!delegate.isTransactionActive()) {
         return null
     }
     TransactionAspectSupport.currentTransactionStatus()
  }




                           CONFIDENTIAL                    25
Utility Methods




  // void, throws NoTransactionException
  metaClass.setRollbackOnly = { ­>
     TransactionAspectSupport.currentTransactionStatus()
           .setRollbackOnly()
  }




                           CONFIDENTIAL                    26
Utility Methods




  // returns boolean
  metaClass.isRollbackOnly = { ­>
     if (!delegate.isTransactionActive()) {
         return false
     }
     delegate.getCurrentTransactionStatus().isRollbackOnly()
  }




                           CONFIDENTIAL                        27
Utility Methods




  // returns boolean
  metaClass.isTransactionActive = { ­>
     TransactionSynchronizationManager
           .isSynchronizationActive()
  }




                           CONFIDENTIAL   28
Dependency Injection




        CONFIDENTIAL   29
Inversion of Control and Dependency Injection


  def userService




                             CONFIDENTIAL       30
Inversion of Control and Dependency Injection


  def userService




  private Object userService

  void setUserService(Object userService) {
     this.userService = userService
  }

  Object getUserService() {
     return userService
  }




                               CONFIDENTIAL     31
Complex dependency 
configuration using Spring 
           SpEL


           CONFIDENTIAL       32
Complex dependency configuration using Spring SpEL




       beans = {
          bar(Bar)

          foo(Foo) {
             name = '#{bar.name}'
          }
       }




                           CONFIDENTIAL              33
Complex dependency configuration using Spring SpEL




       beans = {
          bar(Bar)

          foo(Foo) {
             name = '#{bar.resourceName()}'
          }
       }




                           CONFIDENTIAL              34
Manually injecting 
dependencies at runtime




          CONFIDENTIAL    35
Manually injecting dependencies at runtime


  import org.springframework.beans.factory.config.AutowireCapableBeanFactory

  ...

  def grailsApplication

  ...

  def instance = new XXX(...)

  def ctx = grailsApplication.mainContext
  ctx.beanFactory.autowireBeanProperties(
     instance,
     AutowireCapableBeanFactory.AUTOWIRE_BY_NAME,
     false)




                                   CONFIDENTIAL                                36
Bean Scopes




    CONFIDENTIAL   37
Bean Scopes

 By default Spring beans are singletons




                             CONFIDENTIAL   38
Bean Scopes


 Other scopes

 • prototype

 • session

 • request




                 CONFIDENTIAL   39
Bean Scopes


 You can even create your own custom scope:

 • implement 

  org.springframework.beans.factory.config.Scope

 • register: ctx.beanFactory.registerScope 

  'myScope', new MyScope()




                            CONFIDENTIAL           40
Bean lifecycles and interfaces




             CONFIDENTIAL        41
Bean lifecycles and interfaces

 Set the initMethod and/or the destroyMethod names 
 when registering beans


 import com.mycompany.myapp.LdapAuthenticationManager

 ...

 authenticationManager(LdapAuthenticationManager) { bean ­>
    serverUrl = '...'
    password = '...'
    bean.initMethod = 'init'
    bean.destroyMethod = 'destroy'
 }




                                 CONFIDENTIAL                 42
Bean lifecycles and interfaces

 Implement the
 org.springframework.beans.factory.InitializingBean

 interface and its afterPropertiesSet method and/or the
 org.springframework.beans.factory.DisposableBean
 interface and its destroy method




                                 CONFIDENTIAL             43
Bean lifecycles and interfaces


 package com.mycompany.myapp

 import org.springframework.beans.factory.DisposableBean
 import org.springframework.beans.factory.InitializingBean

 class LdapAuthenticationManager implements 
 InitializingBean, DisposableBean {

    ...

    void afterPropertiesSet() {
       // initialization work
    }

    void destroy() {
       // shutdown work
    }
 }

                                 CONFIDENTIAL                44
Bean PostProcessors




        CONFIDENTIAL   45
Bean PostProcessors


 o.s.b.factory.config.BeanPostProcessor
 • Object postProcessBeforeInitialization(Object bean, 

  String beanName)

 • Object postProcessAfterInitialization(Object bean, 

  String beanName)




                           CONFIDENTIAL                   46
Bean PostProcessors


 o.s.b.factory.config.BeanFactoryPostProcessor
 • void postProcessBeanFactory(ConfigurableListableBeanFactory 

  beanFactory)




                            CONFIDENTIAL                     47
Bean PostProcessors


 o.s.b.factory.support.BeanDefinitionRegistryPostProcessor
 • extends BeanFactoryPostProcessor

 • void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry 

  registry)




                               CONFIDENTIAL                         48
Cloud Support Plugin (cloud­foundry, heroku)




   dataSourceBean.driverClassName = 
   updatedValues.driverClassName

   dataSourceBean.url = updatedValues.url + suffix

   dataSourceBean.username = updatedValues.userName

   dataSourceBean.password = updatedValues.password




                             CONFIDENTIAL             49
Alternate approach to BeanDefinition modification



  def doWithSpring = {

     def mybeanDef = delegate.getBeanDefinition('mybean')

     mybeanDef.beanClass = NewClass

     mybeanDef.propertyValues.add("order",
           application.config.plugin?.rendering?.order ?: 42)
  }



  ●   Use loadAfter = ['plugin1', 'plugin2'] to
  ensure the bean is loaded
  ●   Only valid in a plugin, not the app's resources.groovy

                               CONFIDENTIAL                    50
Bean Aliases




   CONFIDENTIAL   51
Aliases


 As of Grails 2.1 aliases work fully

 • You can create aliases pre­2.1 but only if defined in the same 

  resources.groovy or plugin (doWithSpring)


   beans = {
      springConfig.addAlias 'alias', 'realBean'
   }




                              CONFIDENTIAL                       52
Aliases


 The cache plugin registers the alias cacheOperationSource for 

 the bean registered as 
 org.springframework.cache.annotation.AnnotationCacheOperationSource#0



 Can use to have multiple implementations of a bean and choose 

 one via configuration at startup, e.g. per­environment or some 

 other rule




                                 CONFIDENTIAL                            53
Aliases

 import grails.util.Environment

 beans = {

    String realBeanName

    switch (Environment.current) {

       case Environment.TEST:
          realBeanName = 'testCardProcessingService'; break

       case Environment.PRODUCTION:
          realBeanName = 'productionCardProcessingService'; break

       default: // Environment.DEVELOPMENT, custom envs
          realBeanName = 'mockCardProcessingService'; break
    }

    springConfig.addAlias 'cardProcessingService', realBeanName
 }


                                  CONFIDENTIAL                      54
Internationalization




       CONFIDENTIAL    55
Internationalization


 First­class support in Grails via .properties files in grails­app/i18n

 All generated controllers, GSPs, and layouts are fully 

  internationalized with no hard­coded strings

 Domain class validation errors are internationalized the same way

 Enabled under the hood by the messageSource bean 

  (org.springframework.context.MessageSource)



                                  CONFIDENTIAL                             56
Internationalization


 Use the <g:message> tag in GSPs, message method in 

 controllers

 Or use dependency injection like with any Spring bean

 • def messageSource




                               CONFIDENTIAL               57
Spring MVC Controllers




         CONFIDENTIAL    58
Spring MVC


 New in Grails 1.2

 Annotate src/java or src/groovy classes with @Controller

 Add all packages to the grails.spring.bean.packages list in

 Config.groovy

 • e.g.grails.spring.bean.packages = ['gr8conf.testapp.foo']




                            CONFIDENTIAL                        59
Spring MVC


 Annotate methods with
 @o.s.w.bind.annotation.RequestMapping



   @RequestMapping("/mvc/hello.dispatch")
   public ModelMap handleRequest() {
      return new ModelMap()
         .addAttribute("text", "some text")
         .addAttribute("cost", 42)
         .addAttribute("config",
             grailsApplication.getConfig().flatten()));
   }




                          CONFIDENTIAL                    60
Spring MVC


 @RequestMapping URI value must end in .dispatch
 Add entries in UrlMappings to create more natural URLs

   class UrlMappings {

      static mappings = {
         …

         "/mvc/hello"(uri:"/mvc/hello.dispatch")

         "/mvc/other"(uri:"/mvc/other.dispatch")
      }
   }



                              CONFIDENTIAL                 61
Spring MVC


 Use @Autowired for dependency injection (on fields in Groovy 
 classes, on setters or constructors in Java)


   private GrailsApplication grailsApplication;

   @Autowired
   public void setGrailsApplication(GrailsApplication app) {
      grailsApplication = app;
   }




                                 CONFIDENTIAL                     62
Hibernate Integration




        CONFIDENTIAL    63
Hibernate Integration

 org.springframework.orm.hibernate3. 

 LocalSessionFactoryBean factory bean configures 

 org.hibernate.SessionFactory instances




                           CONFIDENTIAL             64
Hibernate Integration

 org.springframework.orm.hibernate3. 

 HibernateTransactionManager implements 

 org.springframework.transaction. 

 PlatformTransactionManager to abstract away the details of 

 transaction management




                           CONFIDENTIAL                        65
Thread­local holders


 Cumbersome to explicitly open a Hibernate Session or start a

 transaction and have to pass one or more related objects from

 method to method




                              CONFIDENTIAL                       66
Thread­local holders


 Spring uses ThreadLocal scope since web requests are handled

 per-thread




                            CONFIDENTIAL                     67
Thread­local holders


 See org.springframework.transaction.support. 

 TransactionSynchronizationManager and

 org.springframework.orm.hibernate3. 

 SessionFactoryUtils helper classes




                         CONFIDENTIAL             68
Thread­local holders


 Further helped by

 org.codehaus.groovy.grails.orm.hibernate.support. 

 GrailsOpenSessionInViewInterceptor




                       CONFIDENTIAL               69
Thread­local holders


 Opens a Hibernate Session at the start of all controller requests

 and registers it in thread-local scope

 For the duration of the request, there is always an active session

 After the request it flushes and closes the Session




                                CONFIDENTIAL                           70
Thread­local holders


 Hibernate implementation of GORM uses a

 org.springframework.orm.hibernate3. 

 HibernateTemplate under the hood to execute most queries




                             CONFIDENTIAL                   71
Thread­local holders


 HibernateTemplate uses SessionFactoryUtils.getSession() to

 find or create a Session




                            CONFIDENTIAL                   72
Thread­local holders


 Plugins that enable asynchronous processing (Quartz, Gpars,

 Executor) all implement patterns similar to OpenSessionInView




                              CONFIDENTIAL                       73
Other Cool Stuff




      CONFIDENTIAL   74
Other Cool Stuff


 Standard and Custom Events

 Resources

 Data Binding and Validation

 Marshalling XML using O/X Mappers

 JMS (see Grails jms and ActiveMQ plugins)

 EJBs


                                CONFIDENTIAL   75
Other Cool Stuff


 Remoting (see the Grails remoting plugin)
  • Remote Method Invocation (RMI)
  • Hessian (Caucho's lightweight binary HTTP­based protocol)
  • Burlap (another protocol from Caucho which uses XML)
  • Spring's HTTP invoker

 JMX (see the Grails jmx plugin)

 Email (see the Grails mail plugin)




                                CONFIDENTIAL                    76
Other Cool Stuff

 Other persistence support

 • HibernateTemplate

 • JdbcTemplate

 • Other database support

   • JDO

   • JPA

   • iBATIS (2.x) SQL Maps

                              CONFIDENTIAL   77
Want More Information?




         CONFIDENTIAL    78
Thank You




  CONFIDENTIAL   80

Under the Hood: Using Spring in Grails