Grails 3.3 with Servlet Filter

I had a scenario (I don’t really want to get into it) where I needed to implement a proper servlet filter which would run before the Grails dispatcher got involved in the request. The Grails documentation says it can be done, but the instructions are ridiculously vague, and seem more centered on plugins rather than just a webapp.

I finally managed to work it out, and I wanted to show the source code involved for posterity, and anyone else who might stumble upon this solution.

First off, create your filter in /src/main/java/myPackage:

package myPackage;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author Nathan Crause <nathan@crause.name>
 */
@WebFilter(filterName = "testFilter", urlPatterns = {"/*"})
public class TestFilter implements Filter {

	private FilterConfig filterConfig;

	public TestFilter() {
	}

	/**
	 * @param request The servlet request we are processing
	 * @param response The servlet response we are creating
	 * @param chain The filter chain we are processing
	 *
	 * @exception IOException if an input/output error occurs
	 * @exception ServletException if a servlet error occurs
	 */
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;

		for (int i = 0; i < 100; ++i) {
			for (int c = 0; c < 80; ++c) {
				System.out.print("-");
			}
			System.out.println();
		}

		chain.doFilter(request, response);
	}

	/**
	 * Destroy method for this filter
	 */
	@Override
	public void destroy() {		
	}

	/**
	 * Init method for this filter
	 * @param filterConfig
	 */
	@Override
	public void init(FilterConfig filterConfig) {		
		this.filterConfig = filterConfig;
	}

}

Now the really poorly documented part – where the hell do you tell Grails to load the damned thing? Turns out you just use /grails-app/conf/spring/resources.groovy:

import org.springframework.core.Ordered
import org.springframework.boot.web.servlet.FilterRegistrationBean
import myPackage.TestFilter

beans = {
	testFilter(FilterRegistrationBean) {
		filter = bean(TestFilter)
		urlPatterns = ['/*']
		// we want all other Grails filters to have loaded first
		order = Ordered.LOWEST_PRECEDENCE
	}
}

Now, I originally opted for using pure Java for the speed, but it did expose the problem that none of my domain classes were accessible, which I did require. So, I re-implemented the filter as a Groovy class in the /grails-app/controllers/myPackage directory.

It’s also worth noting that you will be required to create a new hibernate session in order to perform queries.

package myPackage

import javax.servlet.Filter
import javax.servlet.FilterChain
import javax.servlet.FilterConfig
import javax.servlet.ServletException
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

/**
 * @author Nathan Crause <nathan@crause.name>
 */
class TestFilter implements Filter {
	
	FilterConfig filterConfig
	
	void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
		for (int i = 0; i < 10; ++i) {
			println("-" * 80)
		}

		Account.withNewSession {
			println(Account.last().inspect())
		}

		chain.doFilter(request, response)
	}
	
	void destroy() {		
	}
	
	void init(FilterConfig filterConfig) {		
		this.filterConfig = filterConfig
	}
	
}

Gradle deployment script for Grails webapp

Please note the “Update” section below.

I haven’t had much success with finding useful deployment strategies and/or scripts for Grails anywhere. The extent of the documentation I’ve been able locate for deployment simply tells you to create a WAR and upload it to the servlet container.

Not terribly helpful if you want to run a formal process.

So, for my Grails webapps, I came up with this. I create a file in the “gradle” directory named “deploy.gradle” containing the following:

buildscript {
	repositories {
		jcenter()
	}
	
	dependencies {
		classpath 'org.hidetake:gradle-ssh-plugin:2.9.0'
	}
}

apply plugin: 'org.hidetake.ssh'

// the below is in it's own ext block because it can potentially be
// overridden by .deployrc.gradle in the user's home directory, and the scmUser
// might be required in the following ext block (where the repository is
// set up)
ext {
	scmUser = project.hasProperty('user') ? project.getProperty('user') : System.properties['user.name']
}

// please note that the file must end in ".gradle" or Gradle won't know how
// to process it, and you'll get a "String index out of bounds: 0" error
def rcFile = new File("${System.properties['user.home']}/.deployrc.gradle")
if (rcFile.exists()) {
	apply from: rcFile.absolutePath
}

ext {
	timeNow = new Date().format('yyyyMMddHHmmss')
	// TODO: the below should use a repository directly associated with the individual user
	scmRepository = "${project.scmUser}@my.site:/var/repos/${project.name}.git"
	runEnvironment = project.hasProperty('env') ? project.getProperty('env') : 'production'
	scmBranch = project.hasProperty('branch') ? project.getProperty('branch') : 'master'
	tmpDir = System.getProperty('java.io.tmpdir') + "${project.name}/${timeNow}"
}

apply from: "${project.projectDir}/gradle/deploy/${project.runEnvironment}.gradle"

task deploy {
	group 'Remote Deployment'
	description 'Deploys a branch from your SCM to a target environment on a remote server'
	
	doLast {
		println "Deploying from branch ${project.scmBranch} to environment ${project.runEnvironment}"
		
		ssh.run {
			session(remotes.role('webserver')) {
				// upload the WAR to the target server(s)
				println 'Uploading WAR file'
				put from: "${project.buildDir}/libs/mercury.war", into: "mercury/mercury-${timeNow}.war"
				// stop Tomcat so we can get this party started
				println 'Stopping Tomcat'
				execute 'sudo service tomcat8 stop'
				// if a pre-existing link exists to "current", remove it
				println 'Symlinking'
				execute 'if [ -f mercury/mercury-current.war ]; then rm mercury/mercury-current.war; fi'
				// link our new WAR to the name "current"
				execute "cd mercury; ln -s mercury-${timeNow}.war mercury-current.war"
				// Tomcat doesn't clean up extracted WAR file directories, so we must flush out the /var/lib/tomcat8/webapps directory
				execute 'if [ -d /var/lib/tomcat8/mercury ]; then sudo rm -rf /var/lib/tomcat8/mercury; fi'
				// Fire tomcat back up
				println 'Starting Tomcat'
				execute 'sudo service tomcat8 start'
			}
		}
	}
}

task deployBuild << {
	println "Building WAR for ${project.runEnvironment} environment"
	
	exec {
		workingDir project.tmpDir
		environment 'TERM', 'dumb'	// this prevents grails+gradle for displaying a second progress bar during the following invocation
	
		commandLine 'grails', "-Dgrails.env=${project.runEnvironment}", 'war', 'mercury.war'
	}
}

task deployCheckout << {
	println "Cloning ${project.scmRepository}#${project.scmBranch} for user ${project.scmUser} into ${project.tmpDir}"
	
	exec {
		commandLine 'git', 'clone', '-b', project.scmBranch, project.scmRepository, project.tmpDir
	}
}

task deployCleanup << {
	println "Removing temporary directory ${project.tmpDir}"

	exec {
		commandLine 'rm', '-rf', project.tmpDir
	}
}

deployBuild.dependsOn deployCheckout
deploy.dependsOn deployBuild
deploy.finalizedBy deployCleanup

Also in the “gradle” directory is a subdirectory named “deploy” where I have the files specific to the environments to which I can deploy, such as “staging.gradle”:

remotes {
	web {
		role 'webserver'
		host = 'my.site'
		user = 'webapps'
		identity = file("${System.properties['user.home']}/.ssh/webapps")
	}
}

Now, you will need to make some modifications to your main “build.gradle” script. First, add in the SSH plugin dependency, immediately after the “buildscript” section:

plugins {
    id 'org.hidetake.ssh' version '2.10.1'
}

And then in the area where you have all the “apply” instructions:

apply from: "${project.projectDir}/gradle/deploy.gradle"

Using the script above, I can deploy a particular branch from within my git repository to a specific environment thus:

gradle -Penv=staging -Pbranch=hotfix/1.2.3.001 deploy

It’s probably not perfect, but since I’m new to Gradle and Grails, I think it’s a pretty good start!

Update!

Alas the “org.hidetake.ssh” plugin has not kept up to date with the latest updates to SSH, and as such the above scripts no longer work, and I have abandoned any attempts to use Gradle to effect deployments. Instead, I have switched over to using Ansible to deal with all of this.