Wednesday, 15 April 2015

Enforcement Rules in Gradle

I previously noted that Gradle has no built in enforcement rules, there is a blog here detailing how you can add your own, but that's not satisfactory for a number of reasons which I won't go in to. What is one to do? Well this one took that as a challenge so the gauntlet has been thrown down and accepted!

Sweeney

Well the first rule of any development is to come up with a suitably obscure and memorable name. So my thinking went: enforces rules -> like the police -> what do we call the police? Well a number of things but in the UK 'The Sweeney' was a memorable Police drama (at least for me) hence sweeney!

Sweeney Gradle Plugin

The sweeney gradle plugin supports a few basic rules out of the box with a couple of specifics for the Java and Gradle versions, it also supports enforcing rules and emitting warnings from rules. 

Built in Rules

Out of the box it has an equality, pattern and version range rules that can be configured, using syntax very similar to how dependencies are defined (indeed I used that as inspiration). In addition (at least at time of writing) it has support for validating the Java version number and Gradle version number.

Extensible

Since I know that I won't be able to cover every rule out there and that people/companies will have their own requirements to enforce build practices then the plugin is designed for users to add in there own rules (and definition parsers) so that it has flexibility which hopefully will ease adoption.

How does this help?

This plugin provides the community with a foundation to build extensible validation rules for Gradle build processes, comes with some useful built in rules and gives a consistent mechanism to define them that is easy to understand.

Wednesday, 11 March 2015

Pushing changes from Shippable to Github

If you use Shippable as your Continuous Integration server on an open source project hosted on Github you will find that by default you cannot push any changes back to Github such as when you increment a version number and wish to push the changes back. This is because Shippable uses HTTPS for public Github repositories so trying to push changes back to it will require a username and password.

Solution

The solution is actually simple and requires two steps, firstly installing the the deployment key in your user or repository and then switching git over to ssh rather than https.

Deployment key

You can find your shippable deployment key on the right hand side of your account, this is a standard SSH public key, copy this key.

You can either add this key to your repository or to your user (or another user if you so choose) that has access to your repositories. Be aware though that Github restricts the re-use of keys so if you add it to your repository then you can only have it on a single repository and shippable only provides a single deployment key. I personally would recommend creating another Github account that shippable can use and controlling access to the repository through that account with the deployment key.

Adding the key is easy, go to your settings on your Github account and choose SSH Keys on the left and click 'Add SSH Key' give it a title and paste the key into the box.

Shippable configuration

Shippable uses a yml file (shippable.yml) located in the root of your project, in the before_script section you just need to add the following line:
 - git remote set-url origin git@github.com:$REPO_NAME.git

To confirm that the URL has changed you can add the following line to confirm the new origin url.
 - git config --get remote.origin.url

$REPO_NAME is an environment variable provided by Shippable that resolves to the plain repository path, e.g. for the Twitter commons repository using the URL https://github.com/twitter/commons.git $REPO_NAME resolves to twitter/commons

Tuesday, 10 February 2015

Gradle and the parent pom

We've all got used to various maven features that we have come to rely on, and in my effort to migrate to Gradle I have endeavoured to find alternative replacments, one of which is the parent POM functionality. Gradle does not natively have this feature that as Maven users we rely on.

Workarounds

There are a number of workarounds documented, firstly the most common is to use the apply from to load a file from a URI (you can use a URL) but this is not ideal for many reasons. The alternative is to write your own plugin that alters your build.gradle file. Anorakgirl has a great write-up on how to do this here Gradle and the parent POM and is the inspiration for my plugin.

Gradle pater-build plugin

I love Anorakgirls write up but I was thinking that it really is a workaround and devs would need to start to understand the internal Gradle API, so if the apply from syntax has its short comings as Anorakgirl pointed out then how about subverting or working around them. I created the gradle-pater-build-plugin to do just that, the plugin allows you to define your Gradle build scripts inside a jar file and load them in (along with their dependencies) have them automatically discovered and applied to your build script.

A Simple Example

Lets take a slightly contrived example, you're a Java house and you want to enforce the use of JUnit (and a specific version) through all of your projects, Using the pater-build plugin you would do the following:

Step 1: Create your project

Create your Java gradle project, the simplest way is to create a directory (for the purpose of this example we will call it junit-dependency) and run:
gradle init wrapper
This will create an empty project.

Step 2: Create your build file

Edit your build.gradle file to add the java and maven plugins as well as setting up your repositories. It should end up looking like:

apply plugin: 'java'
apply plugin: 'maven'

repositories {
    jcenter()
    mavenLocal()
}

group = 'com.example'
version = '1.0'

dependencies {

}

Step 3: Create your parent gradle file

Create the build script that will be packaged up inside the jar file. From the root of your project you need to create the following folders
src/main/resources/META-INF/pater-build
and in it put the gradle build file, let's call this
java-junit.gradle
In this file we need to add in the JUnit dependency so it will end up looking like:

apply plugin: 'java'

dependencies {
    testCompile 'junit:junit:4.12'
}

In addition the build script needs to include the java plugin in order to get the testCompile scope.

Step 4: Create your jar containing your parent gradle file

Compile and install your new library using the following command:
gradle clean install
This will create a jar artifact in your local maven repository under the group 'com.example', the artifact name 'junit-dependency' and the version '1.0'

Step 5: Include your parent jar as a build script dependency

To use your newly created library as a template you need to include it in your build script dependencies along with the pater-build plugin. A build script to use it (in this case we will call it usage.gradle) would look like:

buildscript {
 repositories {
  mavenLocal()
  jcenter()
 }
 dependencies {
  //apply the pater plugin in order to discover gradle build scripts
  classpath 'com.fizzpod:gradle-pater-build-plugin:1.0.0'
  //apply your plugin in the build script for scanning by the pater plugin
  classpath 'com.example:junit-dependency:1.0'
 }
}

repositories {
    jcenter()
    mavenLocal()
}
//apply the pater-build plugin to discover the other gradle build scripts
apply plugin: 'com.fizzpod.pater-build'

This includes the pater-build plugin which will scan for gradle build scripts inside other jars included on the buildscript classpath and the jar file you created in Step 4 which holds your custom build script.

If you run the usage.gradle file with a -i flag:
gradle -b usage.gradle -i

You will see the logging from the pater-build plugin saying that it has found your build script.

Using build file resolvers:
Discovered build files: [META-INF/pater-build/java-junit.gradle]

If you then run the dependencies task you will see a report that includes JUnit as a dependency:
gradle -b usage.gradle dependencies

------------------------------------------------------------
Root project
------------------------------------------------------------

archives - Configuration for archive artifacts.
No dependencies

compile - Compile classpath for source set 'main'.
No dependencies

default - Configuration for default artifacts.
No dependencies

runtime - Runtime classpath for source set 'main'.
No dependencies

testCompile - Compile classpath for source set 'test'.
\--- junit:junit:4.12
     \--- org.hamcrest:hamcrest-core:1.3

testRuntime - Runtime classpath for source set 'test'.
\--- junit:junit:4.12
     \--- org.hamcrest:hamcrest-core:1.3

BUILD SUCCESSFUL

Summary

So what just happened? Well the pater-build plugin scanned the other libraries included on the buildscript classpath and discovered the java-junit.gradle file in the com.example:junit-dependency:1.0 library. It then proceeded to load it into the gradle build file to automatically include JUnit into the testCompile setting in the root build script. 

This is a very simple example of the power that the pater-build plugin gives you to create a suite of libraries that define the dependencies and default settings for all sorts of projects in much the same way as the Maven parent pom can.

Thursday, 29 January 2015

Gradle finding out of date dependencies

Ever wondered how to find dependencies that are no longer up to date in Gradle? Well Ben Manes has the plugin for you in the Gradle Versions Plugin. I'm not going to detail how to use it, it is plainly documented on his site, needless to say ensuring that your dependencies are as up to date as possible is crucial to managing your software, if you don't know that the libraries are out of date then you can't make sensible decisions on your software versioning.

Useful Gradle Build Process Plugins

Gradle is a great tool for building Java projects, in my opinion it is much better than its only real competitor Maven. A lot has been written about Gradle especially with comparison to Maven and building with it, I'm not really going to cover that. What I am going to do is highlight a few plugins which I find invaluable when putting a Gradle script together for a Java project.

Release Management

Maven has the well known and well publicised 'Maven Release Plugin', but what is there for Gradle? Well there are a few options out there (search for gradle release plugin) but for me the best one is the Axion Release Plugin.

This nicely separates the release management from the building of the software, in addition the versioning is controlled by the source control system tags rather than in Maven where it goes and edits the XML files and has to push these back to the repository.

Distribution

Ever struggled with the question 'How should I distribute my Java application?' there has never really been a nice consistent way to distribute it. As Java developers that has usually been left to other people to figure out. However if you look around the web at the tools that we use as developers you are seeing more and more applications packaged up in an easily distributable manner. Enter the Gradle OS Package plugin developed by the clever people at NetFlix. It allows you to create a distribution of your application as an RPM or DEB package for easy installation on to Linux. 

Enforcing version

How many times have you tried to run a build, found that things on your machine are not quite the same as on someone else's or on the build server and the software doesn't quite build in the same way? Maven has the Enforcer Plugin, Gradle doesn't have a plugin with quite the same power or flexibility as the enforcer, but it is easy to role your own as this blog details. One of the downsides is that it has no consistency of DSL support. There is an open source gradle enforcer plugin however this hasn't been released so not available in any public repository as far as I am aware and is actually quite old now.

Thursday, 11 November 2010

Loading a List from a file in Spring Application Context

Have you ever wanted to load a List in your application context from a file? Perhaps you want to define a variable number of settings to be injected in, or in my case I wanted to pull in the Hibernate mapping files into an AnnotationSessionFactoryBean. Spring doesn't appear to have a way to do this out of the box, so here is how I have done it.

So you want to load a list of strings defined in a file called names.txt such as:
James
Adam
Peter
Paul
Your application context would look something like:

<bean id="resourceList" class="com.fizzpod.spring.ResourceListFactoryBean">
<property name="targetListClass">
<value>java.util.ArrayList</value>
</property>
<property name="locations">
<list>
    <value>classpath:names.txt</value>
</list>
</property>
</bean>
The ResourceListFactoryBean is (obviously) a factory bean for creating a list from a Spring resource. Now this uses some of Springs magic to turn the classpath:names.txt into a spring Resource object. To take advantage of the normal ListFactoryBean by extending it so that you can define a list using the normal ListFactoryBean (why you would want to do that rather than using the util:list tag). Anyway the class looks like this:


/**
* Creates a List of values from the specified resources.
*/
public class ResourceListFactoryBean extends ListFactoryBean {

  /**
   * The List implementation class to create
   */
 private Class targetListClass = null;
 
  /**
   * The resources to create the list from
   */
 private Resource[] locations = null;

  /**
   * Flag for whether the source list is set
   */
 private boolean sourceListSet = false;
 
 @Override
 /**
  * Set the source List, just used to detect whether
   * a sourceList is set
   * @param sourceList the source list to create the list from.
  */
 public void setSourceList(List sourceList) {
  super.setSourceList(sourceList);
  sourceListSet = true;
 }
 
 @Override
  /**
   * To allow the creation of the list class
   * @param targetListClass the class of the list to instantiate
   */
 public void setTargetListClass(Class targetListClass) {
  super.setTargetListClass(targetListClass);
  this.targetListClass = targetListClass;
 }

 /**
  * Set a location of the list file to load.
   * @param location the resource to load the settings f
  */
 public void setLocation(Resource location) {
  this.locations  = new Resource[] {location};
 }

 /**
  * Set locations of list files to be loaded.
   * @param locations the locations of the resources
  */
 public void setLocations(Resource[] locations) {
  this.locations = locations;
 }
 
 @Override
 @SuppressWarnings("unchecked")
  /**
   * Create a list from the resources, delegating the
   * super class if any source list has been set.
   * @return the list instantiated from the context definition
   */
 protected List createInstance() {
  List result = null;
  if(this.sourceListSet) {
   result = super.createInstance();
  }
  if(this.locations != null) {
   List resourceList = null;
   if(result == null) {
    result = instantiateListClass();
   }
   
   Class valueType = null;
   
   for(Resource resource: locations) {
    List contents = this.resolveList(resource);
   
    if (this.targetListClass != null) {
     valueType = GenericCollectionTypeResolver.getCollectionType(this.targetListClass);
    }
    if (valueType != null) {
     TypeConverter converter = getBeanTypeConverter();
     for (Object elem : contents) {
      result.add(converter.convertIfNecessary(elem, valueType));
     }
    } else {
     result.addAll(contents);
    }
   }
  }
  if(result == null) {
   throw new IllegalArgumentException("You must set either the sourceList, locations or location parameters");
  }  return result;
 }

  /**
   * Instantiates the list class
   * @return the list class as defined by the context
   */
 private List instantiateListClass() {
  List result;
  if (this.targetListClass != null) {
   result = (List) BeanUtils.instantiateClass(this.targetListClass);
  } else {
   result = new ArrayList();
  }
  return result;
 }

 @SuppressWarnings("unchecked")
  /**
   * Loads the resource into a list of strings
   * @return the list of strings loaded from the resource
   */
 private List resolveList(Resource resource) {
  InputStream inStream = null;
  List lines = null;
  try {
   inStream = resource.getInputStream();
   lines = IOUtils.readLines(inStream);
  } catch (IOException e) {
   throw new IllegalArgumentException("Could not read resource " + resource.getDescription(), e);
  } finally {
   IOUtils.closeQuietly(inStream);
  }
  return lines;
 }

}


Note that the above code relies on the apache commons io to parse and load the resource into a list. Also note that the some of the ListFactoryBean methods are overriden so that we can detect if the user is just defining a normal list. Why, well like I said above if you just want a list use the util:list tag, but by doing this in the above code it allows for a mixed list of items from a resource and sources defined in the xml to be merged together. The following example shows that and as a bonus how to define a single resource for example:

<bean id="resourceList" class="com.fizzpod.ResourceListFactoryBean">
   <property name="targetListClass">
       <value>java.util.ArrayList</value>
   </property>
   <property name="sourceList">
           <list>
               <value>Sarah</value>
               <value>Helen</value>
               <value>Debbie</value>
           </list>
       </property>
   <property name="locations" value="classpath:names.txt"></property>
</bean>

And that's about all. Hope this is of help to someone.

Monday, 11 October 2010

Simple Notepad Version 1.4


Version 1.4 is out and as normal my methodology of trying to do frequent releases means the list of new features is short so short it is a single feature. What is it? Well now you can copy selected text from any page into a note. How? it's as simple as selecting the text and right clicking to get a context menu. It's not just for selecting text though, you can also just right click on a link and copy that to a note as well as just right clicking on a page and you get the title of a page and the url. Simple.
This time there is a catch... The context menus are only available in version 6 of chrome. So this means that Simple Notepad no longer works in chrome 5, sorry for all the chrome 5 users but progress marches on.