Monday, September 19, 2011

Gradle Overview Presentation

Here is a copy of the Gradle presentation that will be given on Wednesday's Charleston Java User's Group (CJUG).  This is a 1 hour presentation, so the topics in no way get into the details of what is available in Gradle.  I highly encourage anyone interested in Gradle to visit www.gradle.org.  The documentation on gradle.org is well written.  In addition, there are plenty of examples in the absolute flexibility that Gradle provides as not only a Java build tool, but a wide variety of plugins and other incredible features.

https://docs.google.com/present/edit?id=0AbPfjtUUq51FZGhmcmo3cXdfMzdmaHJyNjJjMw&hl=en_US

Evidence of the Danger of Too Many PowerPoint Presentations

Source : Random Sampling of 100 PowerPoint presentations.

Sunday, September 11, 2011

Introduction to Gradle Part 3

In part 1 and 2, we built a simple Gradle build file for a Java project, war project, Groovy project , and finally a Gradle multi-build project.  We saw how these Gradle plugins worked and how to setup complex multi-project dependencies.  There is so much that groovy Gradle can do including OSGi, Scala, reports, and much more.  The documentation for Gradle builds can be found here.

Gradle customizations

Last, let's design some custom tasks.  Since, we are writing build scripts in Groovy, Gradle build custom tasks are simple and straight-forward.



(Project D/build.gradle)



import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction


class LumberghTask extends DefaultTask {
    def person = "Peter"
    def request = "Um... yah, I'm going to have to ask you to work this weekend."
   
    @TaskAction
    def greetEmployee() {
        println "Hello ${person} ... How's it going?"
        println request
    }
}

task hello(type: LumberghTask)

task requestWork(type: LumberghTask) {
    person = 'Samir'
    request = "Yah, if you could get those TPS reports to me, yah that would be great"
}


The script above is a custom task called the LumberghTask.  This task asks a variety of employees pointless and useless queries, but demonstrates how custom tasks work in Gradle.

The first step to run this task will be to call the hello target.  Execute the following task first:
gradle -q hello

Output:


Hello Peter ... How's it going?
Um... yah, I'm going to have to ask you to work this weekend.

Next, execute the following:


gradle -q requestWork


Output:

Hello Samir ... How's it going?
Yah, if you could get those TPS reports to me, yah that would be great

The requestWork overrides the person and request variables based on the task being called.  The taskHello is calling the LumberghTask directly. There are so many combinations available in Gradle, that reading the documentation provided here is highly recommended.  Gradle and Groovy provides maximum felxibility for any build environment.

Introduction to Gradle Part 2

In part 1, we built project A, a simple Gradle build file that compiled Java class files and built a jar file.  Next, we will build a war project dependent on the jar file generated from project A.  This uses Maven dependency managment.  It is possible to setup your project completely using Maven or Ivy.  Gradle also has a framework to manage it's own dependencies and will be described shortly using subprojects. One interesting extension of the war plugin is the Jetty plugin.  Gradle provides a library that will wrap a war in a self-contained web container.  This is nice if you have a small web app and want to get it up and running quickly.

Build Project B (simple Java war build)


(Project B/build.gradle)


// We can add comments to the build script using typical Java/Groovy notation
// We need to specify the java plugin which by default looks for src/main/java
usePlugin 'java'
usePlugin 'war' // Use the war plugin to build a war file
usePlugin 'maven'

version = 1.0
artifactId='projectB'

repositories { // reference all of the required Maven repositories.
      mavenRepo(urls : "file://localhost/tmp/repos/")
      mavenCentral() // Setup the Maven repository

}


dependencies { // Add external dependencies here
      compile group: 'commons-cli', name: 'commons-cli', version: '1.0'
      compile group: 'projectA', name: 'projectA', version:'1.0'

 }

uploadArchives { // Again upload our final product into the local Maven repository
    repositories.mavenDeployer {
        repository(url: "file://localhost/tmp/repos/")
        pom.groupId = 'projectB'
        pom.version = '1.0'
        pom.artifactId = 'projectB'
    }
}

assemble.dependsOn ':uploadArchives'

sourceSets { // Defines which source files to include in the build
      main {
            java {
            }
            resources {
            }
      }
}


ProjectB/.classpath
ProjectB/.settings/org.eclipse.jdt.core.prefs
ProjectB/bin/com/pac/domain/Manager.class
ProjectB/.project
ProjectB/build.gradle
ProjectB/build/libs/ProjectB-3.1.war
ProjectB/build/classes/main/com/pac/domain/Manager.class
ProjectB/build/classes/main/Manager.class
ProjectB/build/ivy.xml
ProjectB/build/poms/pom-default.xml
ProjectB/src/main/java/com/pac/domain/Manager.java


Build Project C (Groovy Class build)

In project C, we will build a Groovy based project that uses Java libraries and is also dependent on our project A.  This Gradle maven project will again use the Maven repository to dowload dependencies and upload the final artifiact after the build.



(Project C/build.gradle)


// We can add comments to the build script using typical Java/Groovy notation
// We need to specify the java plugin which by default looks for src/main/java

usePlugin 'groovy'
usePlugin 'maven'
usePlugin 'java'

version = 1.0

repositories {
      mavenRepo(urls : "file://localhost/tmp/repos/")
      mavenCentral() // Setup the Maven repository
}


dependencies { // Add external dependencies here
      groovy module('org.codehaus.groovy:groovy-all:1.7.0') {
          dependency('commons-cli:commons-cli:1.0')
          dependency('projectA:projectA:1.0')
       }
 }


uploadArchives {
    repositories.mavenDeployer {
        repository(url: "file://localhost/tmp/repos/")
        pom.groupId = 'projectC'
        pom.version = '1.0'
        pom.artifactId = 'projectC'
    }
}

assemble.dependsOn ':uploadArchives'

sourceSets { // Defines which source files to include in the build
      main {
            groovy {
            }
            resources {
            }
      }
}

Files in project C...
ProjectC/.classpath
ProjectC/.settings/org.eclipse.jdt.core.prefs
ProjectC/bin/com/pac/domain/Company.class
ProjectC/.project
ProjectC/build.gradle
ProjectC/build/libs/ProjectC-1.0.jar
ProjectC/build/classes/main/com/pac/domain/Company.class
ProjectC/build/ivy.xml
ProjectC/build/poms/pom-default.xml
ProjectC/src/main/groovy/com/pac/domain/Company.groovy




Configuring Gradle Multi-Project Builds


Next, let's setup a Gradle multi-project build called UberProject which will contain projectA, project B, and project C.  This mult-project build will contain the following structure:

UberProject/

        settings.gradle
        /ProjectA
        /ProjectA/build.gradle
        /ProjectB
        /ProjectB/build.gradle
        /ProjectC
        /ProjectC/build.gradle

settings.gradle will contain the following line:
include 'ProjectA', 'ProjectB', 'ProjectC'

What is nice about the Gradle multi-project build is that the main settings.gradle and build.gradle (if existing) will be inherited and can be re-used in all of the subprojects.  This is similar in concept to the Maven pom, but much easier to read and appears on the surface more straight-forward.

Next, in each project we need to use the 'dependsOn' call to manage inter-project dependencies.  Remove any reference to projectA, projectB, or projectC in the dependencies section.  Leave all of the third party libraries, so that we can still download these dependencies as needed.

Add the following line to project C:
dependsOn(':ProjectA')

Add the following line to project B:
dependsOn(':ProjectA')

Run gradle assemble on project C.  You should see the following output.

:ProjectA:compileJava
:ProjectA:processResources
:ProjectA:classes
:ProjectA:jar
:ProjectA:assemble
:ProjectC:compileJava
:ProjectC:compileGroovy
:ProjectC:processResources
:ProjectC:classes
:ProjectC:jar
:ProjectC:assemble

BUILD SUCCESSFUL

Total time: 16.298 secs

Notice how building project C now, automatically builds the dependency of project A.  Nice right?

Now, let's have some fun with our projects and flex the depedency management features in Gradle by adding a circular dependency to project A.  One of the best ways to understand how Gradle works is to break the build.

Add the following line to project A:
dependsOn(':ProjectC')

You should see the following output:

FAILURE: Build failed with an exception.

* Where:
Build file '/home/robert/workspace/UberProject/ProjectC/build.gradle' line: 12

* What went wrong:
A problem occurred evaluating project ':ProjectC'.
Cause: Circular referencing during evaluation for project ':ProjectA'.

* Try:
Run with -s or -d option to get more details. Run with -S option to get the full (very verbose) stacktrace.

BUILD FAILED

Total time: 8.698 secs

By adding a reference to projectC to projectA, we have added a circular dependency.  Gradle finds circular dependencies in multi-project builds and does not allow a build to continue once the circular dependency is found.

Introduction to Gradle Part 1

Many build tools have been created for building and packaging software. Make, Ant, Maven, etc., all seem to have their own frameworks of completing tasks that are driven by the structure of each build tool.   The industry while successfully using existing tools constantly requires more features in a build tool to adapt to newer technologies that are emerging (OSGi, Grails, Scala, Jetty, numerous advances in IDEs, etc.).

This article focuses on using Gradle, because of the wide usage of Java and J2EE as an enterprise platform along with Groovy as a Java meta-language.   Gradle is also poised to become the next widely adopted build tool in the world of software build tools and configuration management because Gradle bridges the feature gap between Ant and Maven.

Gradle is a build tool based on the popular language Groovy.  Groovy is an extension of Java and has many built-in features and frameworks for completing complex tasks.  Gradle has many features which will be described briefly.

Gradle fills in the build tool feature gaps that the two most commonly used build tools, Maven and Ant.  Either tool may have one or less of the following features, but neither tool has all of the following features:
  • Both Ant and Maven build scripts are based in xml.  A build tool that allows the flexibility of a true language allows the developer to include libraries into the build file.  The semantics of a language based build tool also allow flexible development and lower learning curves if the Domain Specific Language (DSL) used in the build tool are already known.
  • Software configuration managers are often the keepers of all build knowledge.  A build tool that does not require special knowledge either due to complexity of a project or intimate knowledge about a build tool configuration keeps both developers and software configuration managers involved in the process at all times.
  • The choice of being able to create builds by convention ala Maven or in a more free manner such as Ant is an either/or choice of build tools. 
  • Good dependency management.  While Ant has dependency management in Ivy and Maven has its own dependency management.  A tool that is simple and excels in dependency management is needed.
  • Partial builds.  In Maven when building a multi-project module it is difficult to build only specific projects without building all projects.
  • A potpourri of plugins and other 3rd party modules that are easy to integrate into a build.  Also, a build tool should allow users to easily build their own customizations with minimum hassle.

The history of software build tools has always had challenges.  Builds in Make for instance used tabs in the semantics of a build file, Ant without ant-contrib or other 3rd party libraries was never meant to run a for loop, Maven pom files are difficult to manage for the average developer trying to include a custom task not in the standard lifecycle.  Each build tool had its own idiosyncrasies.

Gradle Features

Gradle provides the following features:
  • Robust dependency management.  Gradle uses Ivy to resolve dependencies on third party libraries or even inter-project dependencies.  Circular dependencies will also cause build failures.  Gradle also builds on Ivy with its own distinct features not found in the standard Ivy distribution.
  • Gradle allows you to build by convention, IF you choose to build by convention.  The alternative of building by non-convention or a unique non-standard convention is just as easy to select.
  • Ant tasks can be called directly from Gradle.  If a project already is written in ANT, it is possible to convert all, some, or none of the build scripts to Gradle.
  • Gradle being a meta-language of Java allows many of the same object types to be introduced into the build scripts.  The richness of Java/Groovy  objects introduce a higher level of flexibility when writing build scripts.
  • Gradle executes builds by phases.  Maven has a similar architecture, but Gradle makes configuring special hooks by execution phase simple.
  • Support for existing Maven and Ivy repositories exists through client modules.
  • Multiproject builds are made simple in Gradle with features such as configuration injection across projects, partial builds, and robust transitive dependency management.
  • Since Gradle is based on Groovy, it is possible to include third party libraries and customize build scripts based on the Groovy and Java languages.
  • Numerous plugins for Jetty, wars, Scala, Maven, code quality (metrics), OSGi, Eclipse, and Reporting.
  • A Gradle wrapper, that will setup Gradle-free machines with a fresh new copy of Gradle from your repository.

A Simple Build Script

Let us start by creating a simple Gradle build script for Java code.  The installation and setup of Gradle can be found here. Project A is a simple java project that follows Java naming conventions and uses a Maven central repository and a local maven repository located in the local file system at /tmp/repos/.

Build Project A (simple Java jar build)


(Project A/build.gradle)


// We can add comments to the build script using typical Java/Groovy notation
//We need to specify the java plugin which by default looks for src/main/java


usePlugin 'java'
usePlugin 'maven'

version = 1.0

configure(install.repositories.mavenInstaller) {
    pom.version = '1.0'
    pom.artifactId = 'project-A'
}


repositories {
      mavenCentral() // Setup the Maven repository
      mavenRepo(urls : "file://localhost/tmp/repos/")
}

// call uploadArchives to deploy to repository
uploadArchives {
    repositories.mavenDeployer {
        repository(url: "file://localhost/tmp/repos/")
        pom.groupId = 'projectA'
        pom.version = '1.0'
        pom.artifactId = 'projectA'
    }
}

install.dependsOn ':uploadArchives'

dependencies { // Add external dependencies here

      compile group: 'commons-cli', name: 'commons-cli', version: '1.0'
      compile group: 'commons-logging', name: 'commons-logging', version: '1.0.4'
      compile group: 'commons-net', name: 'commons-net', version: '1.4.1'
      compile group: 'junit', name: 'junit', version: '4.7'
      compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.6.2'
      compile group: 'quartz', name: 'quartz', version: '1.5.1'
      compile group: 'xstream', name: 'xstream', version: '1.2.1'
      compile group: 'commons-net', name: 'commons-net', version: '2.0'
}


sourceSets { // Defines which source files to include in the build

      main {
            java {
            }
            resources {
            }
      }
}




Run 'gradle assemble'
Output:


:compileJava
downloading (2 KB) http://repo1.maven.org/maven2/commons-cli/commons-cli/1.0/commons-cli-1.0.pom
..
downloading (2 KB) http://repo1.maven.org/maven2/commons-lang/commons-lang/1.0/commons-lang-1.0.pom
...
downloading (5 KB) http://repo1.maven.org/maven2/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.pom
.....
downloading (1 KB) http://repo1.maven.org/maven2/junit/junit/4.7/junit-4.7.pom
..
downloading (24 KB) http://repo1.maven.org/maven2/org/codehaus/groovy/groovy-all/1.6.2/groovy-all-1.6.2.pom
...................
downloading (9 KB) http://repo1.maven.org/maven2/org/apache/ant/ant/1.7.1/ant-1.7.1.pom
........
downloading (4 KB) http://repo1.maven.org/maven2/org/apache/ant/ant-parent/1.7.1/ant-parent-1.7.1.pom
.....
downloading (2 KB) http://repo1.maven.org/maven2/org/apache/ant/ant-launcher/1.7.1/ant-launcher-1.7.1.pom
...
downloading (6 KB) http://repo1.maven.org/maven2/jline/jline/0.9.94/jline-0.9.94.pom
.....
downloading (146 B) http://repo1.maven.org/maven2/quartz/quartz/1.5.1/quartz-1.5.1.pom
..
downloading (371 B) http://repo1.maven.org/maven2/xstream/xstream/1.2.1/xstream-1.2.1.pom
..
downloading (6 KB) http://repo1.maven.org/maven2/com/thoughtworks/xstream/xstream/1.2.1/xstream-1.2.1.pom
......
downloading (7 KB) http://repo1.maven.org/maven2/com/thoughtworks/xstream/xstream-parent/1.2.1/xstream-parent-1.2.1.pom
.......
downloading (638 B) http://repo1.maven.org/maven2/xpp3/xpp3_min/1.1.3.4.O/xpp3_min-1.1.3.4.O.pom
..
downloading (7 KB) http://repo1.maven.org/maven2/commons-net/commons-net/2.0/commons-net-2.0.pom
.......
downloading (24 KB) http://repo1.maven.org/maven2/org/apache/commons/commons-parent/11/commons-parent-11.pom
...................
downloading (4 KB) http://repo1.maven.org/maven2/org/apache/apache/4/apache-4.pom
.....
downloading (30 KB) http://repo1.maven.org/maven2/commons-cli/commons-cli/1.0/commons-cli-1.0.jar
.......................
downloading (38 KB) http://repo1.maven.org/maven2/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar
............................
downloading (232 KB) http://repo1.maven.org/maven2/junit/junit/4.7/junit-4.7.jar
..................................................................................
downloading (4.45 MB) http://repo1.maven.org/maven2/org/codehaus/groovy/groovy-all/1.6.2/groovy-all-1.6.2.jar
.........................................................................
downloading (353 KB) http://repo1.maven.org/maven2/quartz/quartz/1.5.1/quartz-1.5.1.jar
........................................................................
downloading (63 KB) http://repo1.maven.org/maven2/commons-lang/commons-lang/1.0/commons-lang-1.0.jar
..............................................
downloading (1.32 MB) http://repo1.maven.org/maven2/org/apache/ant/ant/1.7.1/ant-1.7.1.jar
...............................................................................
downloading (87 KB) http://repo1.maven.org/maven2/jline/jline/0.9.94/jline-0.9.94.jar
..............................................................
downloading (12 KB) http://repo1.maven.org/maven2/org/apache/ant/ant-launcher/1.7.1/ant-launcher-1.7.1.jar
..........
downloading (349 KB) http://repo1.maven.org/maven2/com/thoughtworks/xstream/xstream/1.2.1/xstream-1.2.1.jar
..................................................................................
downloading (24 KB) http://repo1.maven.org/maven2/xpp3/xpp3_min/1.1.3.4.O/xpp3_min-1.1.3.4.O.jar
...................
:processResources
:classes
:jar
:assemble

BUILD SUCCESSFUL


Total time: 1 mins 13.323 secs



The build will generate classes and a final jar file in the following directories:

ProjectA/.classpath
ProjectA/.classpath.old
ProjectA/.settings/org.eclipse.jdt.core.prefs
ProjectA/bin/com/pac/domain/Employee.class
ProjectA/.project
ProjectA/build.gradle
ProjectA/build/libs/ProjectA-1.0.jar
ProjectA/build/classes/main/com/pac/domain/Employee.class
ProjectA/build/ivy.xml
ProjectA/build/poms/pom-default.xml
ProjectA/src/main/java/com/pac/domain/Employee.java

One thing to note.  If all of the dependencies have not been added to the gradle an error will occur.  The list of dependencies includes transitive dependencies or libraries that are required by libraries listed in your dependency list.  One way to automate the compilation of transitive dependencies is to add the following line to the build script...


configurations.compile.transitive = true


Any library that is required, but is not explicitly defined in the build script will be managed automatically with this entry.  Also, it is possible to manage transitive dependencies at a more granular level (disabling for specific libraries).  One example of this is as follows:

            dependency("commons-cli:commons-cli:1.0") {
                transitive = false
            }


Useful Firefox Plugins For Web Development

I found this article useful in my AJAX development.  Thought I would share.  Enjoy!

Here's another personal favorite plugin that reads/modifies HTTP headers.