Using pagination with Controller action other than the default list

By default ‘list’ pages are generated to use the <paginate> tag, and this invokes the list action to retrieve the next set of results.

If you are using an action other than the default list action, include the action="" attribute on the tag to use an alternative action method:

<code>&lt;g:paginate action="${listType}" total="${Purchase.count()}" /&gt;</code>

In the above example the action method is the result of a value set from the previous action method to indicate which action method is to be used.

Creating a deployable WAR file

When running ‘grails’ from the command line, the list of available targets does not list the target for creating a deployable WAR file.

‘grails war’ creates a packaged WAR file that can be deployed to a servlet container.

NoSuchMethodErrror StringUtils.isBlank() when deploying Grails WAR to JBoss 4.0.1

Deploying a Grails 0.1 WAR to JBoss 4.0.1 I get this exception:

<code>
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'grailsApplication' defined
in ServletContext resource [/WEB-INF/applicationContext.xml]: 
Initialization of bean failed; nested exception is java.lang.NoSuchMethodError: 
org.apache.commons.lang.StringUtils.isBlank(Ljava/lang/String;)Z
	org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:370)
	org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:226)
	org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:147)
	org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:269)
	org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:320)
	org.springframework.web.context.support.AbstractRefreshableWebApplicationContext.refresh(AbstractRefreshableWebApplicationContext.java:134)
	org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:246)
	org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:184)
	org.springframework.web.context.ContextLoaderServlet.init(ContextLoaderServlet.java:83)
	javax.servlet.GenericServlet.init(GenericServlet.java:211)
</code>

This seems to be a Commons Lang jar issue, and does not occur if deploying Grails web apps to JBoss 4.0.4+

Update: This is an issue with the JBoss Unified ClassLoader when deploying to a JBoss server with other webapps including other possibly different versions of the same jars. The solution is to disable the Unified ClassLoader.

Disabling the use of the JBoss Unified Class Loader in this file:

/jboss/server/default/deploy/jbossweb-tomcat50.sar/jboss-service.xml

changing this line:

<code>
&lt;attribute name="UseJBossWebLoader"&gt;true&lt;/attribute&gt;
</code>

to

<code>
&lt;attribute name="UseJBossWebLoader"&gt;false&lt;/attribute&gt;
</code>

Securing a Grails application using Spring and Acegi Security

Since a Grails application uses Spring, it’s possible to configure an application using Grails to use Acegi Security. This example shows what is required to provide role-based URL security for a Grails application.

web.xml config

I found that the ContextLoaderServlet did not work with Acegi Security – it didn’t find the applicationContext file. Replacing it with the Listener configuration did work:

  • Comment out the ContextLoaderServlet in web-app/web.template.xml
  • Add the following lines:
    <code>
    &lt;context-param&gt;
      &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
      &lt;param-value&gt;/WEB-INF/applicationContext.xml&lt;/param-value&gt;
    &lt;/context-param&gt;
    
    &lt;listener&gt;
      &lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;
    &lt;/listener&gt;
    </code>
    

Add the following filter configuration and mapping to wire in the Acegi Security beans to the web app:

<code>
&lt;filter&gt;
    &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;
    &lt;filter-class&gt;
	  org.acegisecurity.util.FilterToBeanProxy
    &lt;/filter-class&gt;
    &lt;init-param&gt;
	  &lt;param-name&gt;targetClass&lt;/param-name&gt;
	  &lt;param-value&gt;
		org.acegisecurity.util.FilterChainProxy
	  &lt;/param-value&gt;
    &lt;/init-param&gt;
&lt;/filter&gt;
      
&lt;filter-mapping&gt;
  &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;
  &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;

</code>

This filter mapping configuration will send all requests through the Acegi filter to check authentication if needed (depending on URL patterns configured in the next section.

Acegi Bean Configuration

Add the following bean definitions to the applicationContext.xml file in web-app/WEB-INF:

<code>
   &lt;bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"&gt;
     &lt;property name="providers"&gt;
       &lt;list&gt;
         &lt;ref bean="daoAuthenticationProvider"/&gt;
       &lt;/list&gt;
     &lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"&gt;
  &lt;property name="userDetailsService"&gt;&lt;ref bean="inMemoryDaoImpl"/&gt;&lt;/property&gt;
&lt;/bean&gt;

   &lt;bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl"&gt;
     &lt;property name="userMap"&gt;
       &lt;value&gt;
           admin=password,ROLE_ADMIN
       &lt;/value&gt;
     &lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"&gt;
  &lt;property name="authenticationManager"&gt;&lt;ref bean="authenticationManager"/&gt;&lt;/property&gt;
  &lt;property name="authenticationFailureUrl"&gt;&lt;value&gt;/acegilogin.jsp?login_error=1&lt;/value&gt;&lt;/property&gt;
  &lt;property name="defaultTargetUrl"&gt;&lt;value&gt;/&lt;/value&gt;&lt;/property&gt;
  &lt;property name="filterProcessesUrl"&gt;&lt;value&gt;/j_acegi_security_check&lt;/value&gt;&lt;/property&gt;
&lt;/bean&gt;

&lt;!-- prior to ACEGI RC2, was org.acegisecurity.intercept.web.SecurityEnforcementFilter --&gt;
&lt;!--
RC2 notes:
org.acegisecurity.intercept.web.SecurityEnforcementFilter has moved to a new location and name, 
org.acegisecurity.ui.ExceptionTranslationFilter. In addition, the "filterSecurityInterceptor" property on the old 
SecurityEnforcementFilter class has been removed. This is because SecurityEnforcementFilter will no longer 
delegate to FilterSecurityInterceptor as it has in the past. Because this delegation feature has been 
removed (see SEC-144 for a background as to why), please add a new filter definition for FilterSecurityInterceptor 
to the end of your FilterChainProxy. Generally you'll also rename the old SecurityEnforcementFilter entry in your FilterChainProxy 
to ExceptionTranslationFilter, more accurately reflecting its purpose. If you are not using FilterChainProxy 
(although we recommend that you do), you will need to add an additional filter entry to web.xml and use 
FilterToBeanProxy to access the FilterSecurityInterceptor.
--&gt;
&lt;bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"&gt;
    &lt;property name="authenticationEntryPoint"&gt;
        &lt;ref bean="authenticationEntryPoint"/&gt;
    &lt;/property&gt;
&lt;/bean&gt;

      &lt;bean id="httpSessionIntegrationFilter"
            class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"&gt;
            &lt;property name="context"&gt;
                  &lt;value&gt;
                        org.acegisecurity.context.SecurityContextImpl
                  &lt;/value&gt;
            &lt;/property&gt;
      &lt;/bean&gt;
      
&lt;bean id="authenticationEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;
  &lt;property name="loginFormUrl"&gt;&lt;value&gt;/acegilogin.jsp&lt;/value&gt;&lt;/property&gt;
  &lt;property name="forceHttps"&gt;&lt;value&gt;false&lt;/value&gt;&lt;/property&gt;
&lt;/bean&gt;
      
&lt;bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter"/&gt;

&lt;bean id="accessDecisionManager" class="org.acegisecurity.vote.UnanimousBased"&gt;
    &lt;property name="allowIfAllAbstainDecisions"&gt;
        &lt;value&gt;false&lt;/value&gt;
    &lt;/property&gt;
    &lt;property name="decisionVoters"&gt;
        &lt;list&gt;
           &lt;ref local="roleVoter"/&gt;
        &lt;/list&gt;
    &lt;/property&gt;
&lt;/bean&gt;

&lt;bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;
    &lt;property name="authenticationManager"&gt;
        &lt;ref bean="authenticationManager"/&gt;&lt;/property&gt;
    &lt;property name="accessDecisionManager"&gt;
        &lt;ref bean="accessDecisionManager"/&gt;&lt;/property&gt;
    &lt;property name="objectDefinitionSource"&gt;
        &lt;value&gt;
            CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
           PATTERN_TYPE_APACHE_ANT
            /secure/**=ROLE_ADMIN
            /item/create=ROLE_ADMIN
            /item/delete/*=ROLE_ADMIN
            /item/edit/*=ROLE_ADMIN
            /category/create=ROLE_ADMIN
            /category/delete/*=ROLE_ADMIN
            /category/edit/*=ROLE_ADMIN
            
        &lt;/value&gt;
    &lt;/property&gt;
&lt;/bean&gt;

      &lt;bean id="filterChainProxy"
            class="org.acegisecurity.util.FilterChainProxy"&gt;
            &lt;property name="filterInvocationDefinitionSource"&gt;
                  &lt;value&gt;
                        CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                        PATTERN_TYPE_APACHE_ANT
                        /**=httpSessionIntegrationFilter,authenticationProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
                  &lt;/value&gt;
            &lt;/property&gt;
      &lt;/bean&gt;
</code>

This configuration uses the org.acegisecurity.userdetails.memory.InMemoryDaoImpl ‘in memory’ configuration and defines one user, ‘admin’, with password ‘password’, and a role of ‘ROLE_ADMIN’.

The org.acegisecurity.ui.webapp.AuthenticationProcessingFilter bean defines the HTML form to be used to perform the authentication, using the page acegilogin.jsp.

The FilterSecurityInterceptor bean ties together all the configuration beans, and allows you to specify URL patterns and roles that are allowed to access those URLs. The URLs are relative to the Grails app. In this example, roles are assigned to Item and Category controllers, and various CRUD actions on those controllers.

Example login page

This is the simplest case HTML page with a form for the logon. Create this file, acegilogin.jsp in the web-app directory:

<code>
&lt;html&gt;

&lt;body&gt;
&lt;h3&gt;Logon&lt;/h3&gt;
&lt;form action="j_acegi_security_check" method="POST"&gt;
&lt;p&gt;UserID: &lt;input type="text" name="j_username"&gt;
&lt;p&gt;Password: &lt;input type="text" name="j_password"&gt;
&lt;p&gt;&lt;input type="submit" value="Logon"&gt;
&lt;/form&gt;

&lt;/body&gt;

&lt;/html&gt;
</code>