Friday, June 28, 2013

Setting Gradle home directory and proxy in Jenkins

Real quick. Spent the past few hours working around some nasty issues with gradle and jenkins. It seems due to a bug, the jenkins gradle plugin puts the dependency/artifact cache under the jobs workspace. This really isn't a good idea as every job would then download all of the projects artifacts taking up large amounts of space. At the same time, I also needed to setup the proxy information for gradle, which sadly doesn't reuse the jenkins proxy information.

I was able to finally figure out a good place to define the gradle user home and proxy information in a single place to prevent each job from having to define it.

Go into Manage Jenkins > Configure System. Under Global properties check Environment variables and fill in the following for name and value:

name: GRADLE_OPTS
value: -Dgradle.user.home=/home/tomcat/.gradle -Dhttp.proxyHost=101.10.10.10 -Dhttp.proxyPort=3128

For the gradle.user.home property, I tried using ~/.gradle, but that didn't work which means most likely my $HOME environment variable was not set for whatever reason. My guess is it has something to do with all the troubles I've had lately using the bitnami jenkins amazon ami. I also tried setting the environment variable GRADLE_USER_HOME, but that didn't seem to work. Either way, hopefully this will help others.

Tuesday, June 25, 2013

Resource Filtering with Gradle

My team has recently started a new Java web application project and we picked gradle as our build tool. Most of us were extremely familiar with maven, but decided to give gradle a try.
Today I had to figure out how to do resource filtering in gradle. And to be honest it wasn't as easy as I thought it should be; as least coming from a maven background. I eventually figured it out, but wanted to post my solution to make it easier for others.

What is Resource Filtering?
First, for those that may not know, what is resource filtering? It's basically a way to avoid hard coding values in files and make them more dynamic. For example, I may want to display the version of my application in my application. The version is usually defined in your build file and this value can be injected or replaced in your configuration file during assembly. So I could have a file called config.properties under src/main/resources with the following content:
application.version=${application.version}. With resource filtering the ${application.version} value gets replaced with 1.0.0 during assembly, then my application can load config.properties and display the application version.

It's an extremely valuable and powerful feature in build tools like maven and one that I took advantage of often.

Resource Filtering in Gradle
With this being my first gradle project, I needed to find the recommended way to enable resource filtering in gradle. My first problem I had to figure out was where to define the property. In maven this would typically be defined in the project's pom.xml file as a maven property:

<properties>
    <application.version>1.0.0</application>
</properties>

For gradle the appropriate place seemed to be the project's gradle.properties file. So you would add the following to your project's gradle.properties file (Note, I'm not suggesting you would hardcode the modules version in the gradle.properties file. Obviously the value would be derived from the version property in your project. I'm just using this for a simple example):

application.version=1.0.0

The next, and most difficult, problem I had to track down was how to actually enable resource filtering. I was hoping to just set some enableFiltering option and define the includes/excludes list, but that doesn't seem to be the case (extra tip: don't do filtering on binary files like images). I did find some resources online, but this one seemed to be the best approach. So you will need to add the following to your build.gradle file:

import org.apache.tools.ant.filters.*

processResources {
    filter ReplaceTokens, tokens: [
        "application.version": project.property("application.version")
    ]
}

Next you need to update your resource file. So put a config.properties file under src/main/resources and add this:

application.version=@application.version@

Note, the use of @ instead of ${}. This is because gradle is based on ant, and ant by default uses the @ character as the token identifier whereas maven uses ${}.

Finally, if you build your project you can look under build/resources/main and you should see a config.properties file with a value of 1.0.0. You can also open up your artifact and see the same result.

Dot notation
One thing to note is I typically use a period or dot to separate words for properties: application.version instead of applicationVersion. So you will notice the surrounding quotes around "application.version" in the build.gradle file. This is required as failing to surround the key by quotes will fail the build. Probably because groovy's dynamic nature thinks you are traversing an object.

Overriding
I also investigated the best approach to overriding properties in gradle, as this appeared to be slightly different then how it's done in maven. In maven, properties can be overridden by properties defined in the user's setting.xml file or on the command line with the -D option. To override application.version in gradle on the command line I had to run the following:

gradle assemble -Papplication.version=2.0.0

If you want to override it for all projects you can add the property in your gradle.properties file under /user_home/.gradle.

Also, if you are overriding the value via the command line and your property value contains special characters like a single quote, you can wrap the value with double quotes like the following to get it to work:

gradle assemble -Papplication.version="2.0.0'6589"

Summary
Well I hope this helps and if anyone from the gradle community sees a better way to perform resource filtering I'd love to hear about it. I'd also like to see something as important as resource filtering becoming easier to perform in gradle. I think it's crazy having to add an import statement to perform something so simple.

Thursday, December 13, 2012

Configuring CAS Externally using Spring Import

Over the past few months, my team has been working on integrating the Jasig CAS (Central Authentication Service) framework with our application. CAS is a feature rich enterprise Single Sign On (SSO) service. Most importantly, it's very flexible. We need this flexibility because we have several customers, each with different authentication requirements:. Some with and without Active Directory, and others needing PKI (X.509) authentication. Beyond that, we also need to support light weight options for local development. So the problem we had to solve was how to configure CAS externally so we could dynamically change authentication mechanisms without having to rebuild CAS.

Let me first explain that the recommended way to extend CAS is using maven overlays. It's real simple to modify the default behavior in CAS by copying the files from the CAS WAR into your own WAR directory. For example, most of the authentication configuration exists in /WEB-INF/deployerConfigContext.xml. To change the default authentication behavior, you copy this file to your WARs /WEB-INF directory and modify it to fit your requirements. This is the file that defines all the authentication handlers for things like LDAP (BindLdapAuthenticationHandler), X.509 (X509CredentialsAuthenticationHandler), and my personal favorite the simple username equals password (SimpleTestUsernamePasswordAuthenticationHandler) authentication handler.

So the real problem became how can we enable the SimpleTestUsernamePassword authentication handler for local development and test servers, while disabling it for production systems? We wanted to avoid creating multiple CAS WARs containing configuration for their specific purpose: cas-ldap, cas-x509, cas-simple. Also, we wanted to avoid having a single CAS WAR that contained all methods of authentication. Ironically the solution was really simple and is actually used in other places in CAS.

Spring Import
The solution was spring imports (section 3.2.2.1). This was a feature I was not previously familiar with, but now think is one of the coolest features in spring because it lets you change the behavior of the system without having to rebuild. Spring lets you load in other bean xml files via the spring import tag. And since CAS uses spring, it was easy to modify CAS to be configured externally.

Configuring CAS Externally
First, I assume you have already setup your maven project that is performing the maven overlay. Once you have built your project do the following:

  • copy
    /cas/target/war/work/org.jasig.cas/cas-server-webapp/WEB-INF/deployerConfigContext.xml
    to
    /cas/src/main/webapp/WEB-INF
  • You'll also need to copy this same file to your application servers classpath for your application. For us this would be under the jboss conf directory: /jboss/server/default/conf. I also renamed this file to custom-deployerConfigContext.xml to avoid confusion.
  • Open the file /cas/src/main/webapp/WEB-INF/deployerConfigContext.xml
  • Remove all the bean tags in between the beans tag. This leaves you with nothing but the beans tag with nothing in between.
  • Paste in the following spring import tag:
    <import resource="classpath:custom-deployerConfigContext.xml"/>
  • Save your changes, rebuild and redeploy your CAS WAR
That's it. Now you can edit the custom-deployerConfigContext.xml and add/remove the SimpleTestUsernamePasswordAuthenticationHandler, or any other authentication handlers, without having to rebuild your CAS WAR.

Final Thoughts
Hopefully, you learned a little about how to configure CAS externally and a way to use springs import ability. For the most part this works great, but it isn't 100% fool proof. There are several CAS configuration files, so for the more complex authentication scenarios, you might have to modify the cas-servlet.xml or login-webflow.xml files. For example, if you want to do X.509 authentication, you have to include a bean in the cas-servlet.xml file and modify the login-webflow.xml file. But you could apply the same concepts to these files as well as cas-servlet.xml is just another spring file and login-webflow.xml is a spring webflow file that supports the bean-import tag.