Quokka vs Maven

Key Advantages of Quokka:

Usability

  • Humane, validated XML: Quokka's project files make extensive use of attributes and provide shorthand notations for many cases. Also, unlike Maven, quokka project files do not contain arbitrary configuration elements and can be validated against a DTD. See here for a comparison of a simple web application project.

  • Detailed Project Help: Quokka's help plugin provides a very comprehensive HTML report for a project showing all dependencies, paths, targets and properties. See the following example for the project that generates this site.

  • Plugins bind themselves automatically by default: Quokka's plugin architecture allows a plugin to implement an abstract target defined in another plugin. For example, the lifecycle plugin defines an abstract target compile, which is implemented by the javac plugin. When using the javac plugin, it automatically binds itself compile. Maven on the other hand requires the user to specify what phase a goal is bound to manually.

  • Coherent site & documentation: While still a work in progress, quokka provides a coherent site and set of documentation. Furthermore, plugin documentation is available for all versions of plugins, not just the latest version (historical versions of the core documentation is also available). In fact, plugin documentation is bundled with the plugin itself.

  • Dependency handling: Quokka allows transitive dependencies to be marked as mandatory or optional. By default, only mandatory transitive dependencies are included with a convenient shorthand notation for specifiying options. This fine control is via Path Specifications that allow a graph of dependencies to be specified. e.g. A fictious example of "testng(qdox(log4j), bsh)" might represent that you require the testng dependency along with the qdox dependency of it, it's log4j dependency, etc. This fine level of control also applies to plugin dependencies. In contrast, Maven cannot include optional dependencies and it appears to be impossible to exclude dependencies of plugins.

  • Global JDK configuration: JDK source and target levels can be specified globally and all plugins that are JDK aware, such as javac and javadoc will pick up the configuration automatically. The default heap size for forked JVMs can also be set globally.

  • Aggregated reports: All reports currently available for quokka support aggregation into a single report when doing multi-project builds. TODO ... check current status of Maven aggregated reports.

Extensibility/Flexibility

  • No hard-wired notion of a lifecycle in the core: Nothing regarding the lifecycle plugin, including the targets and paths it uses it hard-wired into the core. It is just a normal plugin, contributing abstract targets and default path definitions. The plugin architecture is powerful enough to support this functionality. It also means it is possbile for other plugins to introduce additional paths and abstract targets. For example, the JEE plugin introduces a war path that specifies the libraries bundled into the web application's lib directory.

  • Multiple artifacts per project: The quokka core supports multiple artifacts per project. Support for multiple artifacts is dependent on particular plugin implementations, however it is normal for plugins that perform packaging to support multiple artifacts. e.g. the Jar plugin allows any number of .jar artifacts to be built from a single project, however the Javac plugin only supports compiling all source files as a single unit. In constrast, Maven only supports a single artifact explicitly and then relies on specific plugins to handle special cases such as remote interfaces for EJBs.

  • Arbitrary paths from the repository: Projects can define their own arbitrary paths and add any artifacts from the repository to it. Such paths can them be passed as arguments to plugins or used for any other purpose such as a stand-alone Ant target. In contrast, Maven appears to only support a fixed set of paths and leaves plugins to implement their own specialised mechanisms.

  • Multiple paths exported to the repository: When an artifact is exported to the repository, it can define multiple paths of dependencies with it. e.g. "runtime" is the default path that usually refers to any dependencies the artifact has at runtime. However, there could be another path such as "runtime-jdk14" that includes additional libraries required when running with a jdk of 1.4 or below. In contrast, Maven only exports it's fixed set of paths, usually the runtime path only.

  • Transitive dependencies can be disabled: At times, transitive dependencies can be more trouble than they are worth. In such cases, they can be easily disabled in Quokka by appending a + after the path name. e.g. The following definition will include transitive dependencies:
    <dependency group="apache.ant" version="1.7.1" paths="runtime"/>
    And the following will not include transitive dependencies (in this case ant's launcher.jar):
    <dependency group="apache.ant" version="1.7.1" paths="runtime+"/>
    In constrast, Maven cannot disable transitive dependencies - you have to manually exclude each one.

  • Extensibility via Ant targets & tasks: As quokka is essentially a plugin for Ant, it is possible to combine quokka targets with standard Ant targets. Ant targets can be stand-alone, or depend on or be a dependency of other targets. All standard Ant tasks are available, including optional tasks when the optional jars are referenced from the repository. Also, additional builtin Ant tasks have been provided including the for, if and switch tasks from Ant-contrib. You can even bundle standard Ant tasks in dependency sets and use them like plugins. In contrast, Maven has no native scripting language for extensions. An Ant-run plugin does exist that allows some Ant functionality to be run before or after goals, but is limited in comparison and means your project ends up depending on both Ant and Maven.

  • Extensibility via scripting: The scripting plugin supports writing ad hoc targets in any scripting language compatible with Apache BSF, or JSR 223. Here's an example from the scripting plugin help:
    <dependency group="apache.ant" name="commons-net" version="1.7.1" paths="ant-types"/>
    <plugin group="quokka.plugin.fop" version="?" templates="transform"/>
    
    <plugin group="quokka.plugin.script" version="?">
        <target name="myscript" template="javascript">
            <property name="script"><![CDATA[
              // Example of built-in tasks (echo)
              for (i=1; i<=10; i++) {
                echo = project.createTask("echo");
                echo.setMessage(i*i);
                echo.perform();
              }
    
              // Example of optional Ant task (ftp) - dependencies are added to ant-types
              ftp = project.createTask("ftp")
              ftp.setUserid("ftp.crap.com")
    
              // Example of using a a quokka plugin (fop) - the plugin and template must be declared
              props = new java.util.Properties()
              props.put("in","src/fop/fo.xml")
              props.put("out","${q.project.targetDir}/fop/fo.pdf")
              quokka.execute("quokka.plugin.fop", "transform", props)
            ]]></property>
        </target>
    </plugin>
    

  • IDE/Tools integration: Quokka is implemented as a transparent plugin to an unmodified version of Ant. It hooks into already existing extension points in Ant. This means for IDEs and other tools that use Ant, quokka appears to be Ant (assuming you have control over the classpath used to launch Ant). The upshot is that for IDEs quokka integration comes essentially for free. Note: IDE integration is currently only tested for IntelliJ IDEA and works without modification. Eclipse and Netbeans have not been tested. In constrast, Maven requires specific plugins to be written for all tools and ides.

  • Dependency Sets versus inheritence: Quokka allows build configurations to be composed of reusable configurations called dependency sets. Dependency sets may include dependencies, plugins, properties, imported Ant targets and build resources. For example, there is a standard Jar Dependency Set supplied with quokka that bundles together the plugins required to compile java sources, copy and filter resources and build a .jar file. Each project has a root dependency set, that may nest other dependency sets such as the Jar. Nested sets may nest other sets to any level, providing a powerful composition model for reusing build configurations. In constrast, Maven provides a single inheritance model that cannot handle build resources, or additional ad hoc targets.

  • Build Resources: A build resource is essentially a reference to a file or directory that can bundled up with a plugin and then be overridden in either dependency sets or by placing the resources in the build/resources directory of the project. An example is the jalopy source code formatting plugin that requires an XML file of formatting rules. By specifying it as a build resource with a key of "jalopy-style.xml", it can be overridden in dependency sets. This allows, for example, a corporate-wide dependency set to be defined that includes the corporate code formatting rules. Maven has no such facility.

  • Multi-project flexibility: Quokka's multi-project support is achieved using the subAnt task, together with a buildpath task that can take a path of build files and determine the interdependencies between them. This allows a great deal of flexibility such as building different subsets of children from the parent, auto-discovery of modules, or optionally building all the children before the parent. Maven supports mult-module builds, however it appears that all modules must be explictly defined and then executing a target always executes the same target on all modules.

Reproducibility

  • Versioned Global Repository: Artifacts in the global repository are immutable. If the metadata needs updating, then a different revision of the same artifact is created. This ensures that if an artifact is referenced from the global repository, exactly the same artifact will always be returned over time. I believe Maven now has a policy of making artifacts immutable too. However, if the metadata is wrong with Maven, you have to wait for the next release of the artifact to fix it. With Quokka, you can increment the repository version while leaving the artifact's own version intact.

  • Plugin versions are fixed: Plugins never magically upgrade themselves to new versions based on what is available in the repository at runtime. Plugin versions must be specified explicitly and do not change. Maven's default behaviour is to upgrade automatically which means plugins may upgrade without warning, breaking builds.

  • Bootstrapping: Quokka supports multiple versions of itself to be installed simultaneously. Projects can then be configured to request a specific version of quokka and the bootstrapping process will automatically select the correct version. Bootstrapping can also select a particular JVM and JVM arguments.

  • Snapshots do not expire: Snapshot dependencies in quokka do not expire and get upgraded automatically. The user controls explicitly when snapshots are updated. In contrast, Maven snapshots expire and update automatically, potentially breaking the build in the process.

Robustness & Agility

Note: The items below refer to design aspects that will impact robustness and agility over time. Maven as a product will be more robust than the initial quokka release.

  • Small code base: Quokka plugins are generally a thin wrapper around existing Ant tasks and libraries, and therefore inherit the robustness of the Ant tasks. In contrast, Maven tends to rewrite plugins from scratch.
    TODO: Contrast compiler plugin implementations as an example

  • Plugin targets run in completely isolated classloaders: There is no possibility of a conflict between different versions of 3rd party libraries with regard to different targets. e.g. Jalopy and Cobertura plugins can use different versions of the ORO library and not conflict.

  • Development reports are not rewritten: Quokka uses the native reports generated by tools such as jUnit and Checkstyle. It does not rewrite them and attempt to massage them into a common look and feel. As most of the key reports such as unit tests and code coverage require frames in any case, rewriting them adds little value. Using native reports means you have all the features of native reports immediately when a new version is released.

  • Development reports and site generation are separate: Development reports and web sites usually have very different audiences. In fact, development reports would usually never be deployed to a web site with the exception of open source projects. As such, development reports are completely seperate in quokka. This means you don't waste time transforming a web site to get a new version of a development report.

  • No standard definition of things such as mailing lists are built into the core: Quokka project definitions do not include web site information in them. This allows the project core to be more stable than Maven. Such information can be standardised in a site plugin for quokka if there is a need.

Fewer Dependencies

  • Core has no external dependencies: The quokka core does not depend on any 3rd party libraries excluding Ant. As such there is a miminal chance of conflicts with project libraries.

  • Plugins do not introduce additional dependencies: Quokka plugins do not introduce additional dependencies on 3rd party libraries (excluding libraries used in actually peforming the purpose of the plugin). For example, the Maven javadoc plugin introduces a dependency on Apache's commons-lang library. Quokka plugins do not introduce such dependencies, attempting to keep the overall project dependencies to a minimum.

  • Dependencies are per target, not per plugin: Quokka defines dependencies for each target within a plugin and allows the selective inclusion of individual targets. This means that if you are not using a subset of functionality from a plugin you don't need the dependencies for that subset. For example, a plugin might produce HTML and PDF reports. If you're not interested in PDF reports you could exclude the PDF target and automatically exclude the PDF dependencies as a result. In constrast, Maven plugins define dependencies at a plugin level.

  • Overrides: Quokka supports overriding of versions. It is used to resolve conflicts and normalise dependencies to a single version. These are applied as a path is resolved, so the overridden version of a dependency is never touched resulting in fewer dependencies. In contrast, Maven resolves all possible dependencies in a path and then applies a conflict resolution and duplicate removal process. This means that dependencies you never use are still required to be in the repository.

  • Dependency comparison: The Dependency Analaysis section of the Side by Side guide shows how all of these design choices add up to a massive difference in dependencies.

Repository

  • Repository definition is seperate to the project definition: Maven leaks it's pom.xml into the repository, including information about test libraries, parent projects, etc. A particularly bad example is the JSP 2.0 module from Jetty here. Determing the dependencies for this module includes traversing parent poms and adding dependencies dynamically created from plugins. In contrast, Quokka exports artifacts to the repository which has its own definition and lifecycle independent of the project defintion. Here's the quokka definition of the same artifact.

  • Remote repository metadata is available locally: Quokka supports indexing of repositories. This allows full metadata about artifacts contained in remote repository to be available locally. This makes things like searching remote repositories possible. In constrast, Maven doesn't support querying of remote repositories. This has lead to various stop gap solutions like http://www.mvnrepository.com to provide an index. Unfortunately, such information is not available Maven plugins, making it's utility limited.

  • QA process for the global repository: While Quokka's current repository is small, the artifacts that are in there have gone through a review process. Part of that process is running a verify target which is a bit like lint for repositories. It currently includes the following checks:
    • Checks the artifact has a license
    • Warns if the artifact has multiple licenses
    • Checks the version is in a standard format
    • Warns if the name and group do not match conventions
    • Checks there is a description
    • Checks there are no duplicates by hash or maven id
    • Checks there are no mandatory dependencies on certain libraries such as XML apis
    • Checks that the description, licenses, dependencies and paths match the previous version of the same artifact
    • Warns if the aritfact does not exist in the maven repository
    • If the artifact exists in maven, checks that the paths and checksums match
    • Checks that any referenced artifacts including dependencies, conflicts and overrides exist in the repository

    Hopefully, this process will make the Quokka repository much better than the Maven repository. The checks above would have caught a lot of the current problems in the Maven repository.

  • Repository management: Quokka supports various configurations of repositories out of the box, including the ability to store project dependencies along side the source code.
  • Consistent naming: Quokka has strict naming standards that apply to all artifacts in the global repository. In constrast, maven has two flavours of naming mixed in the same repository, e.g. ant:ant and org.apache.ant:ant.

Key Advantages of Maven:

  • Maturity: Maven is a mature product with an active community. Quokka is newcomer.

  • Plugins: The availability of plugins for Maven is far broader.

  • Global repository: Maven has a fairly comprehensive global repository. However, it suffers badly from incorrect and/or incomplete metadata, or even missing artifacts. Due to this and other reasons, quokka cannot use Maven repositories directly, but it does provide facilities via the maven plugin to import from Maven.