26. Bundling Clojure as a Maven Plug-in

This chapter will take you through writing a dialog input box in Clojure that pops up during the Maven build and asks for a value. This can be useful for version control labels or configuration parameters that would otherwise have to be passed in on the command line.

Assumptions

In this chapter we assume the following:

Image You work in an environment with Java projects that use Maven to build and install projects.

Image You are familiar with Maven tasks and how they look in XML.

Benefits

The benefit of this chapter is that you will learn how to add Maven build steps to add checks, which can add value to your build process without adding risk to the production application. This can be a great way to get started with Clojure in your day job.

The Recipe—Code

Follow these steps for the recipe:

1. Create a new Leiningen project maven-plugin-clj in your projects directory, and change to that directory:

lein new maven-plugin-clj
cd maven-plugin-clj

2. Modify your project.clj so it looks like the following:

(defproject org.clojurerecipes/maven-plugin-clj "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.apache.maven.plugins/maven-plugin-plugin "3.2"]
                 [clj-time "0.5.1"]]
  :plugins [[lein-localrepo "0.5.2"]]
  :main maven-plugin-clj.core
  :pom-addition [:build
                 [:pluginManagement
                  [:plugins
                   [:plugin
                    [:groupId "org.apache.maven.plugins"]
                    [:artifactId "maven-plugin-plugin"]
                    [:version "3.2"]
                    [:configuration
                     [:skipErrorNoDescriptorsFound "true"]]
                    [:executions
                     [:execution
                      [:id "mojo-descriptor"]
                      [:phase "compile"]
                      [:goals
                       [:goal "getinput"]]]]]]]])

3. Modify the file src/maven_plugin_clj/core.clj to have the following contents:

(ns maven-plugin-clj.core
  (:gen-class
   :name ^{org.apache.maven.plugins.annotations.Mojo
           {:name "getinput"}} org.clojurerecipes.InputBox
   :state state
   :init init
   :prefix "-"
   :extends org.apache.maven.plugin.AbstractMojo
   :methods [[setPrompt [String] void]
             [setTitle [String] void]
             [setFieldDefault [String] void]])
  (:import (javax.swing JFrame JOptionPane))
  (:require [clj-time.format :as time-format]
            [clj-time.core :as time-core]))

(defn -init []
  [[] (atom {:prompt "What label would you like to apply?"
             :title "Maven Label Dialog (in Clojure)"
             :fieldDefault (str "PROJECT_LABEL_"
                                (time-format/unparse (time-format/formatter
                                                                 "yyyyMMdd")
                                                     (time-core/now)))})])

(defn setfield
  [this key value]
  (swap! (.state this) into {key value}))

(defn getfield
  [this key]
  (@(.state this) key))

(defn -setPrompt [this loc]
  (setfield this :prompt loc))

(defn  -getPrompt
  [this]
  (getfield this :prompt))

(defn -setTitle [this loc]
  (setfield this :title loc))

(defn  -getTitle
  [this]
  (getfield this :title))

(defn -setFieldDefault [this loc]
  (setfield this :fieldDefault loc))

(defn  -getFieldDefault
  [this]
  (getfield this :fieldDefault))

(defn get-JFrame []
  (let [jFrame (proxy [JFrame] [])]
    (.setVisible jFrame true)
    (.setDefaultCloseOperation jFrame JFrame/EXIT_ON_CLOSE)
    jFrame))

(defn run-input-box [this jFrame]
  (let [prompt (-getPrompt this)
        optionPaneTitle  (-getTitle this)
        textBoxDefault (-getFieldDefault this)
        result (JOptionPane/showInputDialog jFrame prompt optionPaneTitle
JOptionPane/PLAIN_MESSAGE nil nil textBoxDefault)] (.dispose jFrame)
    (java.lang.System/setProperty "MAVEN_LABEL" result)))

(defn -execute [this & args]
  (run-input-box this (get-JFrame)))

(defn -main [& args])

4. Create the directory resources/META-INF/maven.

5. Create the file resources/META-INF/maven/plugin.xml with the following contents:

<?xml version="1.0" encoding="UTF-8"?>
<plugin>
  <name>maven-plugin-clj</name>
  <description></description>
  <groupId>org.clojurerecipes</groupId>
  <artifactId>maven-plugin-clj</artifactId>
  <version>0.1.0-SNAPSHOT</version>
  <goalPrefix>maven-plugin-clj</goalPrefix>
  <isolatedRealm>false</isolatedRealm>
  <inheritedByDefault>true</inheritedByDefault>
  <mojos>
    <mojo>
      <goal>getinput</goal>
      <requiresDirectInvocation>false</requiresDirectInvocation>
      <requiresProject>true</requiresProject>
      <requiresReports>false</requiresReports>
      <aggregator>false</aggregator>
      <requiresOnline>false</requiresOnline>
      <inheritedByDefault>true</inheritedByDefault>
      <implementation>org.clojurerecipes.InputBox</implementation>
      <language>java</language>
      <instantiationStrategy>per-lookup</instantiationStrategy>
      <executionStrategy>once-per-session</executionStrategy>
      <threadSafe>false</threadSafe>
      <parameters/>
    </mojo>
  </mojos>
  <dependencies>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-plugin-api</artifactId>
      <type>jar</type>
      <version>3.1.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-model</artifactId>
      <type>jar</type>
      <version>3.1.0</version>
    </dependency>
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-utils</artifactId>
      <type>jar</type>
      <version>3.0.10</version>
    </dependency>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-artifact</artifactId>
      <type>jar</type>
      <version>3.1.0</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.sisu</groupId>
      <artifactId>org.eclipse.sisu.plexus</artifactId>
      <type>jar</type>
      <version>0.0.0.M2a</version>
    </dependency>
    <dependency>
      <groupId>javax.enterprise</groupId>
      <artifactId>cdi-api</artifactId>
      <type>jar</type>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>javax.annotation</groupId>
      <artifactId>jsr250-api</artifactId>
      <type>jar</type>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <type>jar</type>
      <version>1</version>
    </dependency>
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <type>jar</type>
      <version>10.0.1</version>
    </dependency>
    <dependency>
      <groupId>com.google.code.findbugs</groupId>
      <artifactId>jsr305</artifactId>
      <type>jar</type>
      <version>1.3.9</version>
    </dependency>
    <dependency>
      <groupId>org.sonatype.sisu</groupId>
      <artifactId>sisu-guice</artifactId>
      <type>jar</type>
      <version>3.1.0</version>
    </dependency>
    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <type>jar</type>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.sisu</groupId>
      <artifactId>org.eclipse.sisu.inject</artifactId>
      <type>jar</type>
      <version>0.0.0.M2a</version>
    </dependency>
    <dependency>
      <groupId>asm</groupId>
      <artifactId>asm</artifactId>
      <type>jar</type>
      <version>3.3.1</version>
    </dependency>
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-component-annotations</artifactId>
      <type>jar</type>
      <version>1.5.5</version>
    </dependency>
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-classworlds</artifactId>
      <type>jar</type>
      <version>2.4</version>
    </dependency>
    <dependency>
      <groupId>org.apache.maven.plugin-tools</groupId>
      <artifactId>maven-plugin-annotations</artifactId>
      <type>jar</type>
      <version>3.2</version>
    </dependency>
    <dependency>
      <groupId>org.clojure</groupId>
      <artifactId>clojure</artifactId>
      <version>1.5.1</version>
    </dependency>
  </dependencies>
</plugin>

6. Next we’ll create the pom.xml file. Run the following commands on the command line:

lein clean
lein pom

7. At the time of writing, there was a bug1 in lein pom that created duplicate <build> sections in the generated pom.xml. We’ll remove that now. Modify the generated pom.xml to comment out the first <build> section so it looks like this:

1. https://github.com/technomancy/leiningen/pull/454

<!--<build>
    <sourceDirectory>src</sourceDirectory>
    <testSourceDirectory>test</testSourceDirectory>
    <resources>
      <resource>
        <directory>resources</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>dev-resources</directory>
      </testResource>
      <testResource>
        <directory>resources</directory>
      </testResource>
    </testResources>
    <directory>target</directory>
    <outputDirectory>target/classes</outputDirectory>
    <plugins/>
  </build> -->

If you can’t see the duplicate, then you may be working on a version that has the bug fixed—skip this step.

8. Next we’ll publish to the local Maven repository. Run the following on the command line:

lein uberjar
lein localrepo install -p pom.xml  target/maven-plugin-clj-0.1.0-SNAPSHOT-
standalone.jar org.clojurerecipes/maven-plugin-clj 0.1.0-SNAPSHOT

9. Next we’ll test this out in a Maven project. On a new command prompt (keep the old one in case you need to change something) in your parent projects directory, run the following command to create a new Maven project:

mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes
-DgroupId=org.clojurerecipes -DartifactId=maven-plugin-test
-DinteractiveMode=false

cd maven-plugin-test/

10. Next we’ll modify this to bring in our Clojure Maven plug-in. Modify the pom.xml to look like the following:

<?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.clojurerecipes</groupId>
  <artifactId>maven-plugin-clj-test</artifactId>
  <packaging>jar</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>maven-plugin-clj</name>
  <build>
    <sourceDirectory>src</sourceDirectory>
    <testSourceDirectory>test</testSourceDirectory>
    <plugins>
      <plugin>
  <groupId>org.clojurerecipes</groupId>
        <artifactId>maven-plugin-clj</artifactId>
        <version>0.1.0-SNAPSHOT</version>
<executions>
          <execution>
            <id>mojo-descriptor</id>
            <phase>compile</phase>
            <goals>
              <goal>getinput</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-antrun-plugin</artifactId>
  <version>1.1</version>
  <executions>
    <execution>
      <phase>compile</phase>
      <goals>
        <goal>run</goal>
      </goals>
      <configuration>
        <tasks>
          <echo>******** Displaying value of property ********</echo>
          <echo>${MAVEN_LABEL}</echo>
        </tasks>
      </configuration>
    </execution>
  </executions>
      </plugin>
    </plugins>
  </build>
</project>

Note the plug-in section where we bring in our Maven plug-in. Obviously, the versions must match. Note the echo section where the result from the input box is displayed to stdout.

11. Delete the file src/test/java/org/clojurerecipes/AppTest.java.

Testing the Recipe

The test goes as follows:

1. Test it by running the following on the command line:

mvn compile

You should get the result shown in Figure 26.1.

Image

Figure 26.1 A dialog box from a Maven plug-in implemented in Clojure

2. Then you should see this in the command prompt:

[echo] ******** Displaying value of property ********
[echo] PROJECT_LABEL_20150612

Notes on the Recipe

In the project.clj, in the dependencies section we load up the Maven plug-in because we’ll rely on this library later. In the plug-ins section we bring in the localrepo library because we’ll create a library with Leiningen that we’ll publish to our local Maven repository. The pom-addition section is used by the pom.xml generator feature in Leiningen when we use it to generate a pom.xml for our Maven library later.

Looking at the file core.clj, in the namespace section at the top, we set this Clojure namespace to generate a Java class. Included in this are the annotations for Maven to recognize the plug-in, the prefix for the methods we’ll export, a state variable, and the list of methods that we’ll export. We also import a Java Swing library and a Clojure library for time.

Next, in the init method, we define some default values for state.

Then we provide a facility for mutable state by having a generic getters and setters given a field name, getField and setField. Now we have getters and setters for Title, Prompt, and Field Defaults.

After this, the get-JFrame function instantiates our dialog box. Then the run-input-box function populates our dialog box with the required detail.

Finally, the execute method is called by Maven to run this class.

Looking at the plugin.xml file, we see that it is read by the parent Maven process when this library is brought in as a plug-in. Note that the version here, <version>0.1.0-SNAPSHOT</version>, has to match the version in your project.clj (defproject org.clojurerecipes/maven-plugin-clj "0.1.0-SNAPSHOT".

Conclusion

In this recipe we built a Clojure project that extends a Maven class and prompted the user for input using a Swing screen. Then we packaged our project as a Jar file, and included it in a Maven build project, applying the appropriate XML files. Then we ran our Maven build and had it obtain input from the user via our Clojure project and use that input as part of the build process.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.83.199