Wednesday, August 26, 2015

A Maven Plugin Developer's Thoughts on Gradle

I use both Gradle and Maven, and I maintain a plugin for working with Groovy (GMavenPlus). I thought this gave me a somewhat unique perspective. I know this comparison is far from exhaustive, but I wanted to share some thoughts I'd jotted down.

When I first heard about Gradle, I was fairly dubious. Maven had been around for about 6 years at that point and they were making some rather large promises. Although I was intrigued, my reaction was "come back to me when you have a non-RC 1.0 release". Since then, they've had several stable releases and about 6 years of their own maturing.

What I like about Gradle
  • They kept the best parts of Maven
    • The lifecycle concept
    • The standardized project layout
    • The dependency management concept
  • Very concise syntax.  It's an amazing feeling to be able to do a standard Java build with a single line of code.
  • Keeps the simple things simple, while allowing you to use the power of Groovy and Ant if you need something non-standard.
  • Gradle Wrapper (gradlew) is a nice way to help keep builds reproducible
  • Application Plugin (Maven has ways to do this, but having it in a plugin is convenient)
  • Much more flexible than Maven (in nearly every way -- dependency management, lifecycle customization, custom tasks, etc)
  • Can put environment config and dev helper scripts right into the build as tasks instead of separate script files
  • HTML test report is nicer than Surefire's, and I now prefer it over console text
  • Has a test runtime scope (unlike Maven)
What I don't like about Gradle
  • Allowing people to do custom things so easily might lead to the tangled mess that was Ant. There's already some pretty crazy builds out there (though arguably this isn't much riskier than what you could do with AntRun or GMavenPlus).
  • Because it's Groovy instead of XML, you don't answers get as helpful hints from your IDE.
  • Can't include multiple source directories each with their own includes/excludes patterns (they have a TODO for this).
  • Groovy building Groovy can sometimes be problematic
  • No equivalent of mvn install, which makes end-to-end testing of plugins clunkier (though they now offer TestKit that should help with this)
  • Some areas might be perceived as being less mature
  • No isolation of plugin dependencies -- they all run on the same classpath (unlike Maven), unless the author took the time to do custom classloader work, which most don't.
Notable differences from Maven
  • In Gradle, default target bytecode version is version of JDK, whereas in Maven default is currently 1.5
  • Gradle's commands are case-insensitive (something I actually really like)
  • Gradle doesn't compile tests unless specifically asked for (gradle classes != mvn compile, gradle testClasses == mvn compile) (not really a positive or negative, just something to be aware of)
  • Gradle will stop testing after the first module to have a test failure. You have to use the --continue argument to continue on to the other modules the way Maven does. I kinda wish this was the default.
What I like About Maven
  • Maven's inflexibility has largely proven very successful in preventing pointless deviation from the established pattern.
  • The lifecycle concept
  • The standardized project layout
  • The dependency management concept
What I don't like About Maven
  • XML is pretty cumbersome (though Polyglot for Maven isn't completely dead). This leads to out of control copy-and-pasting of POMs, with dependencies that have no business being there.
  • Convention over configuration doesn't go far enough (ideally, a basic Java project should be just a few lines, like it is in Gradle)
  • No ability to specify ranges of versions that are acceptable for transitive resolution (I haven't used it much -- but it's a really cool feature)
  • No TestRuntime scope
  • No Maven Wrapper equivalent of gradlew (though there is an unofficial project)
How Gradle has improved Maven
It took a while, but you can finally opt out of transitive dependencies. I think Gradle (and Ivy) earned a good deal of credit for the pressure to offer this feature.

I honestly think despite there being some areas Gradle can be improved, it's generally the better choice for most projects.  There are circumstances where this isn't the case (like if you're building a Maven plugin). But in most cases, I think Gradle's pros outweigh the cons. But if you prefer Maven (or your CTO/CIO/Architect is making the choice for you), rest assured that despite my recommendation, I'm committed to maintaining GMavenPlus for the indefinite future so that the choice is always yours to make.

More Resources

Monday, August 10, 2015

Forcing Windows 10 Upgrade

Want to get your free upgrade to Windows 10, but don't want to wait your turn? Run this reg file
Windows Registry Editor Version 5.00
Then open the command prompt as an administrator and run
wuauclt.exe /updatenow

Wednesday, July 15, 2015

Custom configuration script ASTs

Nikolay Totomanov asked on the Groovy mailing list how one could add a default constructor (if it doesn't already exist) to all classes. I realized there were no examples on the internet (that I could find anyway) of
  1. How to pass parameters into an AST in a configuration script
  2. How to use a custom AST in a configuration script
So I decided to try to remedy that with this post.

1. AST parameters in a configuration script
Let's say you wanted to do
in a configuration script. How do you pass the includes? Use a map. The equivalent configuration script would be
withConfig(configuration) {
    ast(groovy.transform.TupleConstructor, includes:['foo'])

2. Custom AST in a configuration script
If you wanted to create your own AST and use it in a configuration script, I suggest looking at Groovy as a starting point. Here, I'll use groovy.transform.TupleConstructor and org.codehaus.groovy.transform.TupleConstructorASTTransformation as an example to solve Nikolay's problem. Here is the result
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.AnnotatedNode
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.ConstructorNode
import org.codehaus.groovy.ast.Parameter
import org.codehaus.groovy.ast.stmt.BlockStatement
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.transform.AbstractASTTransformation
import org.codehaus.groovy.transform.GroovyASTTransformation
import org.codehaus.groovy.transform.GroovyASTTransformationClass

@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
public class DefaultConstructorASTTransformation extends AbstractASTTransformation {
    public void visit(ASTNode[] nodes, SourceUnit source) {
        init(nodes, source)
        AnnotatedNode parent = (AnnotatedNode) nodes[1]
        if (parent instanceof ClassNode) {
            ClassNode cNode = (ClassNode) parent
            if (!cNode.getDeclaredConstructor(new Parameter[0])) {  // doesn't already have a default constructor
                cNode.addConstructor(new ConstructorNode(ACC_PUBLIC, new Parameter[0], cNode.EMPTY_ARRAY, new BlockStatement()))
public @interface DefaultConstructor {}

withConfig(configuration) {

Tuesday, May 26, 2015

Guava Hadoop Classpath Issue

Blogging this because it was slightly too large for a tweet.  If you've got a stacktrace like

java.lang.NoClassDefFoundError: com/google/common/io/LimitInputStream
at org.apache.hadoop.mapreduce.JobSubmitter.submitJobInternal( at org.apache.hadoop.mapreduce.Job$ at org.apache.hadoop.mapreduce.Job$ at Method) at at at org.apache.hadoop.mapreduce.Job.submit( at org.apache.hadoop.mapreduce.Job.waitForCompletion(

You may find this problematic dependency tree

It seems Google has once again broken compatibility in Guava by removing LimitInputStream in Guava 15.  And while much of Hadoop (except the new versions which have upgraded their Guava version) are on an older version of Guva, the hadoop-auth module contains a newer version of Guava that most dependency management tools (aka Maven and Gradle) will choose over the older version.  Adding an exclusion for this transitive dependency should resolve this issue.

Friday, May 15, 2015

IntelliJ and junit-hierarchicalcontextrunner

For those using junit-hierarchicalcontextrunner getting an exception like
java.lang.Exception: The inner class com.mycompany.SomeClassTest$InnerClass is not static.
 at org.junit.runners.BlockJUnit4ClassRunner.validateNoNonStaticInnerClass(
You might be able to get rid of that error (especially if it happens when running in IntelliJ, but not the commandline) by upgrading to JUnit 4.12 and junit-hierarchicalcontextrunner 4.12.0. However, if you're an IntelliJ user, you'll find that running an individual test still runs all the tests. This will be fixed in an upcoming release.

Friday, May 1, 2015

Codehaus Migration

Since Codehaus is shutting down, you may be wondering where a project you use has moved. Here's where some of the more popular projects have moved.

Project Old Homepage New Homepage
JavaNCSS NONE YET (though Codehaus has a mirror on Github)
Mojo (still transitioning)
Pico Container
Plexus Classworlds
Plexus Containers

I'll keep this page updated as more information becomes available (let me know if you spot something incorrect or out of date). Also if there's a project not on this list that you think should be, let me know and I'll add it. I know some of these moves are old news, but I listed them anyway since once Codehaus shuts down, any redirects they may have had will stop working.

Monday, March 23, 2015

Skype formatting

This seems to be nowhere documented, but here's some markup Skype recognizes.  An example of what you'd type is in gray, and what Skype would render is in blue.




@@ *not bold* (note the space after the two at signs, and that this must be the first thing in a message)
*not bold*

!! preformatted (note the space after the two exclamation points, and that this must be the first thing in a message)

Preformatting also overrides other markup, for example
!! *not bold*
*not bold*

Preformatting also respects whitespaces, for example
!! above

Of course Skype also supports /me, like most chat clients do these days
/me yawns
[user name] yawns
[user name], will be whatever your name is in Skype (not your username)

And since, you can also enable/disable the markup feature with
/wikimarkup on/off