DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Languages
  4. Maven Polyglot: replacing pom.xml with Clojure, Scala, or Groovy Script

Maven Polyglot: replacing pom.xml with Clojure, Scala, or Groovy Script

Maven is a great build tool, but your pom.xml file can get aggravating. Using Maven Polyglot, you can create build scripts using a variety of JVM-friendly languages.

Peter Szanto user avatar by
Peter Szanto
·
Feb. 10, 17 · Tutorial
Like (18)
Save
Tweet
Share
16.89K Views

Join the DZone community and get the full member experience.

Join For Free

Maven's best-kept secret is that it supports pom.groovy,  pom.scala, and many other dialects, on top of pom.xml. This feature was introduced in 2015, but somehow, it was left out from the documentation (or no one read it). Maven Polyglot, an official Maven extension makes this possible.

What's wrong with pom.xml?

XML was the next big thing 15 years ago. Every cool technology used XML. The list includes J2EE, Spring, Hibernate, Ant, EJB, SOAP, and, of course, Maven, too. As time passed, all those projects moved away from XML, mostly in favor of Java annotations. Maven’s case is a bit different, not just because annotations wouldn’t work, but also because XML was used not only for configuration and metadata, but also for some sort of build script, something that it was never meant to be used for.

Getting started with maven polyglot in three easy steps

1. Generate the build script from an existing pom.xml

The quickest way to get started is to convert an existing Maven project by executing the below command in the project directory:

mvn io.takari.polyglot:polyglot-translate-plugin:translate -Dinput=pom.xml -Doutput=pom.groovy 

It will recursively process all submodules if they exist. The result will be a pom.groovy file being generated next to the pom.xml. The generated build script's structure is quite self-explanatory, as it is very similar to the pom.xml, just not an XML anymore. For example, this  pom.xml...

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.exampledriven</groupId>
  <artifactId>polyglot-example</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>polyglot-example</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

</project>


...will be translated to this pom.groovy .

project {
  modelVersion '4.0.0'
  groupId 'org.exampledriven'
  artifactId 'polyglot-example'
  version '1.0-SNAPSHOT'
  name 'polyglot-example'
  properties {
    'project.build.sourceEncoding' 'UTF-8'
  }
  dependencies {
    dependency {
      groupId 'junit'
      artifactId 'junit'
      version '3.8.1'
      scope 'test'
    }
  }
}


Or into this pom.scala.

import org.sonatype.maven.polyglot.scala.model._
import scala.collection.immutable.Seq

Model(
  "org.exampledriven" % "polyglot-example" % "1.0-SNAPSHOT",
  name = "polyglot-example",
  dependencies = Seq(
    "junit" % "junit" % "3.8.1" % "test"
  ),
  properties = Map(
    "project.build.sourceEncoding" -> "UTF-8"
  ),
  modelVersion = "4.0.0"
) 


2. Create an extensions file

The next step is to create a .mvn/extensions.xml file with the following content:

<?xml version="1.0" encoding="UTF-8"?> 
<extensions>
  <extension>
    <groupId>io.takari.polyglot</groupId>
    <artifactId>polyglot-groovy</artifactId>
    <version>0.1.19</version>
  </extension>
</extensions> 


Yes, old habits die hard. In order to get rid of XML, we have to create one more XML, but this will be the last one, I promise.

3. Clean up

To avoid confusion, delete all the pom.xml files. Execute mvn clean install or any other command, the project will behave exactly like a regular Maven project.

Main features

Build scripts can be written in the following languages

  • Ruby
  • Groovy
  • Scala
  • Clojure

Or in the following markup languages

  • Atom
  • YAML

These dialects can be generated by replacing the word groovy with the desired name in the translate command and in the extensions.xml file. The size of the build script can be significantly reduced using Atom or YAML, but other than that there is no real benefit of those. The real power comes when a programming language is used.

Inlined plugins

The single biggest selling point is the use of inlined plugins. This means that ordinary code can be executed inside the POM file. Here is a Groovy example that creates a file during the compile phase if a system property named file-test is set to true:

build {

    $execute(id: 'hello', phase: 'compile') {

         if ("true".equals(System.getProperty("file-test"))) {
             println "File generation is enabled"

             println properties.size()
             println this.getProperties().containsKey('greet')
             println properties
             println properties.get('greet')

             if (getProperties().getOrDefault('greet', false)) {
                 System.out.println 'Hello from groovy'
             }

             def directory = "target/classes/new"
             def dirCreated = new File(directory).mkdir();

             println "Directory was created? " + dirCreated
             def file = new File(directory + "/hello.txt")

             if (!file.exists()) {
                 println "Creating hello.txt"

                 file.createNewFile();
                 file.write("hello from groovy")

             } else {
                 println "hello.txt is already created"

             }
         } else {
             println "File generation is disabled"
         }

     }

 }

The same could be achieved by configuring a maven plugin, but that often requires cumbersome configuration, that is very hard to understand. Inlined plugins allow bigger level of control over the build script and they are closer to traditional build scripts.

Break points

As of IntelliJ IDEA 2016.3, break points can be set inside build scripts. Here is a proof:

intellij-breakpoint

At the time of writing this article, there were some limitations:

  • When running into break points in multi-module projects, IntelliJ opens the wrong build script
  • It works only with the Groovy dialect.

Debug messages

Anywhere in the build script, language-specific debug statements can be added. This obviously includes the inlined plugins, but in other places, such as in profile-specific parts, a debug statement can be useful

Accessing the execution context

Inlined plugins can access the Maven execution context, which will allow programmatic access of the metadata of the Maven project.

$execute(id: 'hello', phase: 'compile', ) {ec ->
  println 'Version : ' + ec.getProject().getModel().getVersion()
  println 'Group ID : ' + ec.getProject().getModel().getGroupId()
  println 'Artifact ID : ' + ec.getProject().getModel().getArtifactId()
  println 'Basedir : ' + ec.basedir()

This works only with  polyglot-groovy 0.1.20 or newer. At the time of writing this article 0.1.19, was the latest, but  polyglot-scala 0.1.19 already supports it, like this :

build = Build(
  tasks = Seq(Task("someTaskId", "verify") { ec =>
    println(s"\nbaseDir: \n${ec.basedir}")
  })
),


What about the Java DSL?

Wouldn’t it be nice to build Java with a build script written in Java? Something like this:

project(
  modelVersion -> "4.0.0",
  parent(
    groupId -> "io.takari",
    artifactId -> "takari",
    version -> "14"
  ),
  groupId -> "io.takari.polyglot",
  artifactId -> "polyglot",
  version -> "0.1.16-SNAPSHOT",
  packaging -> "pom",
  name -> "Polyglot :: Aggregator",
  licenses(
    license(
      name -> "The Eclipse Public License, Version 1.0",
      url -> "http://www.eclipse.org/legal/epl-v10.html",
      distribution -> "repo"
    )    
  )
  ...
  plugin("maven-release-plugin", new ImmutableMap.Builder<String, String>()
           .put("preparationGoals", "clean install")
           .put("mavenExecutorId", "forked-path")
           .build()
)
);


Everything is either a lambda or a method call. If you like it, vote up this GitHub issue, and maybe it will help prioritizing it.

Tooling support and requirements

Maven Polyglot has the following requirements:

  • Maven 3.3.1 +
  • Java 7 +

The following tools support Maven Polyglot:

  • IntelliJ 2016.3 +
  • Eclipse through experimental (at the time of writing) plugins
  • All CI tools that supports Maven

Summary

The big benefit of Maven Polyglot is certainly the backward compatibility with traditional Maven. A polyglot project can be converted back to a regular Maven project any time (of course, the inline plugins will be lost). The learning curve is very small — those who know Maven know Maven Polyglot, too. The project is quite stable and safe to use on production projects.

How does it compare to gradle?

Gradle, by default, supports Groovy (starting with Gradle 3.0 it supports Kotlin too), but it does not support the other languages that Maven Polyglot does. Gradle is newer than Maven, and many of its mistakes are avoided. On the other hand, switching from Maven to Gradle takes more effort than switching to Maven Polyglot, both in terms of learning curve and updating build infrastructure.

Further reading

  • Intellij polyglot support announcement

  • Maven Polyglot offical page

Examples and source code

Examples used for this project:

  • https://github.com/ExampleDriven/maven-polyglot-groovy-example

  • https://github.com/ExampleDriven/maven-polyglot-groovy-simple-example

  • https://github.com/ExampleDriven/maven-polyglot-scala-example

The official example:

  • https://github.com/takari/polyglot-maven-examples

Apache Maven Polyglot (computing) Groovy (programming language) Continuous Integration/Deployment intellij Scala (programming language) Clojure

Published at DZone with permission of Peter Szanto. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • What Is API-First?
  • Create a CLI Chatbot With the ChatGPT API and Node.js
  • Choosing the Right Framework for Your Project
  • Container Security: Don't Let Your Guard Down

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: