links for 2008-07-17

Thursday, July 17th, 2008 10:32am

links for 2008-07-09

Wednesday, July 09th, 2008 10:34am

links for 2008-07-08

Tuesday, July 08th, 2008 10:33am

links for 2008-06-20

Friday, June 20th, 2008 10:33am

links for 2008-06-10

Tuesday, June 10th, 2008 10:34am

An updated Grails Acegi plugin

Monday, April 14th, 2008 12:48am

I’ve implemented Acegi Security (now Spring Security) in a few Spring apps so I greatly appreciate how simple the Grails Acegi plugin makes securing an application. It only takes a few minutes to install and configure and you get to avoid working witht Acegi’s notoriously large XML config (and steep learning curve).

A coworker was asking if it’d be possible to use LDAP instead of a database and this got me thinking about how a lot of the default configuration in the plugin isn’t modifyable. In addition to alternate data stores I could see needing to add in a LogoutHandler or even one or more extra Filters but you’d need to edit the plugin code, which makes upgrading a pain.

Also, I’ve been following the progress of Acegi migrating to Spring Security 2.0 and their new simpler configuration options. I’ve only implemented a small Grails app so far but we’re working on a much more extensive one and I want to avoid having to downgrade to traditional Acegi, so I spent a weekend upgrading and extending the plugin’s configurability and sent it off to the original developers to see if they’d like to incorporate the changes. They’re pretty busy but are reviewing the new code.

Along the way I found and fixed a couple of Hibernate Session-handling bugs
and it turned out to be good timing - just after I fixed them a couple of people complained about them on the user mailing list. If you’re affected by the bugs but don’t want to risk using this update you can replace the 3 fixed files attached to the Jira issue.

It may take a while to get things incorporated into the official plugin, so if any of this is interesting to you, feel free to download the updated plugin (I called it spring-security-0.1 but it’ll most likely be acegi-security-0.3 for consistency), and of course let me know if there are any issues.


The plugin is configured using a default script (DefaultAcegiConfig) and a user-defined script (AcegiConfig). Many options are configurable, and I added a few more:

  • filterNames - a list of filter bean names. If the list is specified in the
    user’s config, the specified filters will be used in the requested order,
    otherwise the standard filters will be used
  • logoutHandlerNames - a list of logout handler bean names. If the list is
    specified in the user’s config, the specified logout handlers will be used in
    the requested order, otherwise the standard handlers will be used
  • decisionVoterNames - a list of voter bean names. If the list is specified in
    the user’s config, the specified voters will be used in the requested order,
    otherwise the standard voters will be used
  • providerNames - a list of authentication provider bean names. If the list is
    specified in the user’s config, the specified authentication providers will
    be used in the requested order, otherwise the standard authentication
    providers will be used

So for example, if you wanted to integrate an SSO solution, you could replace one or more of the default filters and/or add extra filters by overriding the list of filter names, and defining the SSO-specific filters in resources.groovy or resources.xml. The same goes for logout handlers, voters, and providers.

I also made a few string properties customizable:

  • realmName - allows the user to choose the realm name instead of the
    default ‘Grails Realm’ (no idea if this is useful)
  • rememberMeKey - allows the user to choose the
    rememberMeServices/rememberMeAuthenticationProvider key instead of the
    default ‘grailsRocks’
  • afterLogoutUrl - allows the user to choose the logoutFilter
    logoutSuccessUrl instead of the default ‘/’

I tried to keep things backwards compatible for the users, but made a few small
changes. One was to change the ’show_mail’ parameter
to ’showMail’ (in the user domain class) and another was to change the ‘loadAcegi’ config attribute to ‘active’ (in AcegiConfig). Also, I renamed DefaultAcegiConfig and AcegiConfig to DefaultSecurityConfig and SecurityConfig respectively.

To make working with LDAP or other authentication stores easier, I reworked GrailsUser and GrailsDaoImpl a bit. GrailsUser is now an interface (extending UserDetails and adding a getter for the user domain object) and GrailsUserImpl is the default concrete implementation. So you can use your own implementation of GrailsUser by subclassing GrailsDaoImpl and overriding createUserDetails() or replace the whole bean by defining your own implementation of UserDetailsService in resources.groovy/resources.xml (it turns out that any beans that you define replace plugins’ beans with the same name/id).

I also moved the artifacts (AuthBase, AuthenticateService, and AuthorizeTagLib) into packages so projects that use packages can access them.


There were some relatively minor issues:

  • org.acegisecurity.annotation.SecurityAnnotationAttributes was apparently
    moved to org.springframework.security.annotation.SecurityAnnotationAttributes
    but is no longer available in the code. I couldn’t figure out how to
    implement annotation-based security without this (there’s no 2.0 documenation
    yet) so I re-implemented the Acegi code in the plugin
    (org.codehaus.groovy.grails.plugins.springsecurity.SecurityAnnotationAttributes)
    and it works fine. This should be replaced with whatever approach is typical
    in 2.0
  • I’m using Java 5 features since the existing annotation support had already broken support for
    Java 1.4. If this is a problem it would be simple to revert
  • I tested as much as I could but haven’t used the email, Ajax login, or
    Switch User features so those might be broken (unlikely though)
  • apparently there’s a bug in the Grails plugin.xml generation code when a
    plugin uses packages, so the elements are broken (I doubt this
    affects anything)

Additionally, Stephan February emailed the Grails user list
around the same time announcing that he’d made a custom version of the plugin with ACL support added, so there’s plenty of merging to be done :)

A Gant Script to Call Hibernate’s SchemaExport

Monday, March 31st, 2008 8:24pm

Being new to Grails, I've been closely following the User and Dev mailing lists for tips and there was a recent discussion on the User list about accessing or generating the Hibernate schema script. The schema that Hibernate builds is good but typically DBAs need to customize things and the Hibernate output is a good starting point. It's also useful to view the DDL between builds to monitor database changes.

In a traditional Spring/Hibernate app I use the schemaexport Ant task that comes with Hibernate Tools (this is a wrapper for SchemaExport), or by calling it directly if I need more control than the Ant task provides.

Running SchemaExport programmatically is pretty straightforward - you just have to build a Configuration from your domain classes (using hbm.xml files or annotated classes). But domain classes in Grails are (typically) .groovy files; I looked at the Grails/GORM code but couldn't figure out how to properly build the Application, so the domain classes weren't found and the Configuration was empty.

I've been reading the Grails source code trying to figure out how things work and I was looking at the code for the command-line scripts (e.g. "grails war", "grails test-app", etc.) and realized that I could borrow some setup code from there and get SchemaExport to work. It took a few hours to get going and a few more to distill it down to proper idiomatic Groovy (I am still a Java programmer ...).

The code is below but you can download the script here. Some usage notes:

  • Usage: grails [environment] schema-export [generate | export] [stdout] [filename]
  • The action (generate or export), stdout option, and the filename are optional; if ommitted the action is generate, the filename defaults to ddl.sql in the project base dir, and ddl is only sent to stdout if the stdout option is present
  • Example 1: 'grails schema-export' generates ddl.sql for the default configuration
  • Example 2: 'grails prod schema-export stdout' generates ddl.sql for the 'prod' configuration, sending ddl to stdout in addition to the default file
  • Example 3: 'grails dev schema-export c:/foo/bar/grails-app.sql' generates the file 'c:/foo/bar/grails-app.sql' for the 'dev' configuration
  • Example 4: 'grails prod schema-export export' exports the schema (drop and create) using the 'prod' configuration
  • Example 5: 'grails prod schema-export export stdout' exports the schema using the 'prod' configuration, echoing the ddl to stdout
import org.hibernate.dialect.Dialect
import org.hibernate.dialect.DialectFactory
import org.hibernate.tool.hbm2ddl.SchemaExport
import org.codehaus.groovy.grails.orm.hibernate.cfg.DefaultGrailsDomainConfiguration
import org.springframework.jdbc.datasource.DriverManagerDataSource
import org.springframework.jdbc.support.JdbcUtils

grailsHome = Ant.project.properties.'environment.GRAILS_HOME'
includeTargets <<new File("${grailsHome}/scripts/Bootstrap.groovy")

Properties props = new Properties()
String filename = "${basedir}/ddl.sql"
boolean export = false
boolean stdout = false

def configClasspath = {

  getClass().classLoader.rootLoader?.addURL(new File(classesDirPath).toURL())

  Ant.copy(todir: classesDirPath, file: "${basedir}/application.properties")
  Ant.copy(todir: classesDirPath, failonerror: false) {
    fileset(dir: "${basedir}/grails-app/conf",
           excludes: '*.groovy, log4j*, hibernate, spring')
    fileset(dir: "${basedir}/grails-app/conf/hibernate")
    fileset(dir: "${basedir}/src/java", excludes: '**/*.java')
  }
}

def configureFromArgs = {

  args = args ?: ''
  args.split('\n').each { arg ->
    arg = arg.trim()
    if (arg.length()> 0) {
      if (arg == 'export') {
        export = true
      }
      else if (arg == 'generate') {
        export = false
      }
      else if (arg == 'stdout') {
        stdout = true
      }
      else {
        // assume filename override
        filename = arg
      }
    }
  }
}

def populateProperties = {

  File dsFile = new File("${basedir}/grails-app/conf/DataSource.groovy")
  def dsConfig = null
  if (dsFile.exists()) {
    dsConfig = new ConfigSlurper(grailsEnv).parse(dsFile.text)
  }

  props.'hibernate.connection.username' = dsConfig?.dataSource?.username ?: 'sa'
  props.'hibernate.connection.password' = dsConfig?.dataSource?.password ?: ''
  props.'hibernate.connection.url' =
       dsConfig?.dataSource?.url ?: 'jdbc:hsqldb:mem:testDB'
  props.'hibernate.connection.driver_class' =
       dsConfig?.dataSource?.driverClassName ?: 'org.hsqldb.jdbcDriver'
  if (dsConfig?.dataSource?.dialect) {
    def dialect = dsConfig.dataSource.dialect
    if (dialect instanceof Class) {
      dialect = dialect.name
    }
    props.'hibernate.dialect' = dialect
  }
  else {
    println("WARNING: Autodetecting the Hibernate Dialect; "
    + "consider specifying the class name in DataSource.groovy")
    try {
      def ds = new DriverManagerDataSource(
          props.'hibernate.connection.driver_class',
          props.'hibernate.connection.url',
          props.'hibernate.connection.username',
          props.'hibernate.connection.password')
      String dbName = JdbcUtils.extractDatabaseMetaData(ds,
          'getDatabaseProductName')
      int majorVersion = JdbcUtils.extractDatabaseMetaData(ds,
          'getDatabaseMajorVersion')
      props.'hibernate.dialect' =
        DialectFactory.determineDialect(dbName, majorVersion).class.name
    }
    catch (Exception e) {
      println "ERROR: Problem autodetecting the Hibernate Dialect: ${e.message}"
      throw e
    }
  }
}

target('default': 'Run Hibernate SchemaExport') {
  depends(classpath, checkVersion, configureProxy, packageApp)

  configureFromArgs()

  File file = new File(filename)
  Ant.mkdir(dir: file.parentFile)

  configClasspath()
  loadApp()

  populateProperties()

  def configuration = new DefaultGrailsDomainConfiguration(
      grailsApplication: grailsApp,
      properties: props)

  def schemaExport = new SchemaExport(configuration)
    .setHaltOnError(true)
    .setOutputFile(file.path)
    .setDelimiter(';')

  String action = export ? "Exporting" : "Generating script to ${file.path}"
  println "${action} in environment '${grailsEnv}' using properties ${props}"

  if (export) {
    // 1st drop, warning exceptions
    schemaExport.execute(stdout, true, true, false)
    schemaExport.exceptions.clear()
    // then create
    schemaExport.execute(stdout, true, false, true)
  }
  else {
    // generate
    schemaExport.execute(stdout, false, false, false)
  }

  if (!schemaExport.exceptions.empty) {
    ((Exception)schemaExport.exceptions[0]).printStackTrace()
  }
}

Using Spring MVC Controllers in Grails

Thursday, March 27th, 2008 9:15pm

Groovy is slower than Java and sometimes dramatically slower. Realistically, this has little impact on a web application since response time is affected more by the database and network latency, so as long as the slowdown isn't too dramatic, the benefits of Groovy and Grails far outweigh these concerns. And Grails is still way faster than Rails :)

But having said that, I was wondering how to use a regular Java Spring MVC controller and JSP instead of a Grails controller and a GSP (both of which use Groovy). Turns out it's pretty easy:

  • Register the traditional Spring dispatcher servlet in web.xml (you'll need to have run grails install-templates). In this example the name (SpringMVC) isn't important, use whatever you want, and I've chosen to map *.action URLs to this controller and let Grails handle the rest:
<servlet>
   <servlet-name>SpringMVC</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
   <servlet-name>SpringMVC</servlet-name>
   <url-pattern>*.action</url-pattern>
</servlet-mapping>

  • Generate web-app/WEB-INF/SpringMVC-servlet.xml:
<?xml version='1.0' encoding='UTF-8'?>

<beans xmlns='http://www.springframework.org/schema/beans'
   xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
   xmlns:p='http://www.springframework.org/schema/p'
   xmlns:lang='http://www.springframework.org/schema/lang'
   xsi:schemaLocation='
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
      http://www.springframework.org/schema/lang
      http://www.springframework.org/schema/lang/spring-lang-2.5.xsd'
>

   <bean id='mvcHandlerMapping'
      class='org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping'
      p:order='1'>

      <property name='interceptors'>
         <list>
            <ref bean='openSessionInViewInterceptor' />
            <ref bean='localeChangeInterceptor' />
         </list>
      </property>
   </bean>

   <bean id='mvcViewResolver'
      class='org.springframework.web.servlet.view.UrlBasedViewResolver'
      p:viewClass='org.springframework.web.servlet.view.InternalResourceView'
      p:order='1'
      p:prefix='/WEB-INF/jsp/'
      p:suffix='.jsp'
   />

   <bean name='baseSimpleController' abstract='true' p:cacheSeconds='0'/>

   <bean name='jspController'
      class='com.foo.spring.controller.JspController'
      parent='baseSimpleController'
      abstract='true'
   />

   <!-- actions -->

   <bean name='/test.action'
      class='com.foo.spring.controller.TestController'
      parent='baseSimpleController'
      p:successView='test'
   />

   <bean name='/other.action' parent='jspController' p:successView='other' />

</beans>

And that's it. Some notes:

  • the handler mapping uses the id mvcHandlerMapping since Grails will create one using the standard name of handlerMapping
  • since handler mappings are auto-discovered by default, you need to set the order attribute to something lower than the Grails mapping's (which uses the default value of Integer.MAX_VALUE) so this mapping is accessed first
  • the HandlerInterceptors that are configured for the Grails mapping (OpenSessionInView, LocaleChange) won't be automatically available to this mapping, but it's simple to borrow them since they're registered as beans; you can also add other custom interceptors to the list
  • I've created an optional abstract parent controller bean (baseSimpleController) for simple controllers (single-page, i.e. not form or wizard controllers)
  • I've also created a simple controller that just shows a JSP – this is useful for pages that don't have any controller logic:
    package com.foo.spring.controller;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.AbstractController;

    public class JspController extends AbstractController {

       private String _successView;

       @Override
       protected ModelAndView handleRequestInternal(
             final HttpServletRequest request,
             final HttpServletResponse response) {

          return new ModelAndView(_successView);
       }

       public void setSuccessView(final String view) {
          _successView = view;
       }
    }

I've mapped two sample URLs – /test.action, which uses a controller, and /other.action, which uses JspController to just show other.jsp.

Note that it is possible to use JSPs with Grails; Grails looks for a GSP using the specified name, but if it doesn't find one it looks for a JSP (under /WEB-INF/grails-app/views/) and uses that if it exists. So another option is to use Grails controllers and JSP.

Big caveat: I haven't used this in production yet – I'm just prototyping so I'll have this available in the future just in case.

My First Grails App is Live

Monday, March 24th, 2008 10:59pm

Unfortunately it's unlikely you'll be able to see it, unless you're a college student in need of help verifiying your enrollment status to get a deal on a flight at Student Universe.

The core app is pretty small - it's a web-based IM client/live chat that allows a student to talk with a Students Only agent. The Grails part is there to persist everything to the database. However the actual app is fairly involved though, since I added an admin interface, lots of tests, and a bunch of other stuff in anticipation of re-using a lot of it in the new product that we're working on (stealth mode for now ...)

One of the things that attracted me to Students Only was their use of Groovy and Grails. I've played with Groovy off and on for years but never had a good reason to write a real app using it. But Grails is rapidly gaining momentum and it's certainly been a blast working with it.

I'm still partially on the fence about Grails vs a traditional Spring/Hibernate app because of the performance overhead of Groovy's dynamic dispatch. The stack traces are staggeringly large, and I'm not sure how much of a productivity boost I'm getting out of Groovy or Grails personally. The cool thing though is that Grails is flexible enough that you're not locked into anything - you can use JPA-annotated entities and DAOs instead of GORM, JSP instead of GSP, etc. if you find that the "Grails Way" isn't enough for your needs.

Of course the coolest thing about Grails is the plugins - there are a ton of them and more are announced all the time.

Crash

Tuesday, January 29th, 2008 11:46pm

Well this is a first - I've had to roll back a JDK update to the previous version due to crashing. Luckily I always keep the previous version in case I need to roll back but never have; I install the binary JDK into (in this case) /usr/local/jdk1.6.0_04 and symlink this to /usr/local/jdk so I don't need to change my path. So it was a simple matter to symlink to /usr/local/jdk1.6.0_03 and the problem went away.

The problem was that Eclipse started crashing recently and I blamed Eclipse and/or a bad plugin or combination of plugins instead of the JDK upgrade. So I reinstalled Eclipse, cleared out the workspace/.metadata caches, re-installed plugins a few at a time, re-imported projects and nothing - still crashed regularly. Big ugly JDK crashes with hs_err_pid.log files full of stuff like

#
# An unexpected error has been detected by Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00002b4516f0722a, pid=28013, tid=1085491536
#
# Java VM: Java HotSpot(TM) 64-Bit Server VM (10.0-b19 mixed mode linux-amd64)
# Problematic frame:
# V  [libjvm.so+0x1f122a]
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

I did some Googling but this is a new problem and only seems to affect 64-bit JDKs, so there's not much discussion going on. One guy mentioned that adding "-client" to eclipse.ini helped, but not for me. Turns out that there is no client JVM for 64-bin Linux (or Windows) JDKs (see the discussion here).

So this morning I switched back to u3 and everything seems to work fine now.

I'm also running Kubuntu on my shiny new workstation at my shiny new job and had the same problem, but didn't have the previous install to revert to. Luckily though Sun keeps archived previous versions and I was able to download u3 here.

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License.