Maven Notes

Also see old Maven notes.

See How to Publish Your Artifacts to Maven Central


Installation
  1. Download from maven.apache.org.
  2. Extract the download to where you would like to keep it. Maven is a more of a personal tool, so maybe local to your user rather than to a host-wide path.
  3. Set up the M2_HOME variable in your .profile to point to the root of the installation. Also, add variable M2 to point to the bin subdirectory, i.e.:
    $ export M2_HOME=~/dev/apache-maven/apache-maven-3.1.1
    $ export M2=${M2_HOME}/bin
    
  4. Add MAVEN_OPTS if you wish to override JVM properties. (See Maven download page.)
  5. Add what's in M2 to your path if you plan on using Maven from the command line a lot.
    $ PATH=${PATH}:${M2}
    
  6. Ensure that JAVA_HOME is set to the root of your Java installation and that $JAVA_HOME/bin is on your $PATH.
  7. Run mvn --version to verify correct installation.

Creating a project
  1. Create a subdirectory somewhere.
    ~/dev $ mkdir project
    
  2. Execute this Maven goal:
    $ mvn archetype:generate -DgroupId=com.etretatlogiciels.project \
                             -DartifactId=project \
                             -DarchetypeArtifactId=maven-archetype-quickstart \
                             -DinteractiveMode=false
    

What this means...

groupId the package path root (your domain)
artifactId the project (application) name
archetypeArtifactId the sort of Maven project you want

What this does...

Creates a project subdirectory and all supporting files as dictated by the default Maven archetype. Anything left blank or unspecified on the command line will be asked for interactively by Maven after you press Enter. There will not be any interaction because everything has been specified here as you will see.

What you see happen...

~/dev $ mvn archetype:generate -DgroupId=com.etretatlogiciels.project \
>                              -DartifactId=project \
>                              -DarchetypeArtifactId=maven-archetype-quickstart \
>                              -DinteractiveMode=false
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom <<<
[INFO]
[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Batch mode
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.0/maven-archetype-quickstart-1.0.jar
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.0/maven-archetype-quickstart-1.0.jar (5 KB at 18.7 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.0/maven-archetype-quickstart-1.0.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.0/maven-archetype-quickstart-1.0.pom (703 B at 4.8 KB/sec)
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.0
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.etretatlogiciels.project
[INFO] Parameter: packageName, Value: com.etretatlogiciels.project
[INFO] Parameter: package, Value: com.etretatlogiciels.project
[INFO] Parameter: artifactId, Value: project
[INFO] Parameter: basedir, Value: /home/russ/dev
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: /home/russ/dev/project
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.197s
[INFO] Finished at: Mon Dec 09 15:46:07 MST 2013
[INFO] Final Memory: 15M/148M
[INFO] ------------------------------------------------------------------------
~/dev $ ll
total 12
drwxr-xr-x  3 russ russ 4096 Dec  9 15:46 .
drwxr-xr-x 26 russ russ 4096 Dec  9 15:45 ..
drwxr-xr-x  3 russ russ 4096 Dec  9 15:46 project

What you see created...

~/dev $ tree project
project
+-- pom.xml
`-- src
    +-- main
    |   `-- java
    |       `-- com
    |           `-- etretatlogiciels
    |               `-- project
    |                   `-- App.java
    `-- test
        `-- java
            `-- com
                `-- etretatlogiciels
                    `-- project
                        `-- AppTest.java

11 directories, 3 files

Building the new project
$ mvn package

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building project 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ project ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/russ/dev/project/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:2.5.1:compile (default-compile) @ project ---
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /home/russ/dev/project/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ project ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/russ/dev/project/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:2.5.1:testCompile (default-testCompile) @ project ---
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /home/russ/dev/project/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ project ---
[INFO] Surefire report directory: /home/russ/dev/project/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.etretatlogiciels.project.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.004 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ project ---
[INFO] Building jar: /home/russ/dev/project/target/project-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.691s
[INFO] Finished at: Mon Dec 09 15:59:35 MST 2013
[INFO] Final Memory: 14M/209M
[INFO] ------------------------------------------------------------------------

A new subdirectory has been added as a result of the build:

~/dev $ tree project
.
.
.
`-- target
    +-- classes
    |   `-- com
    |       `-- etretatlogiciels
    |           `-- project
    |               `-- App.class
    +-- maven-archiver
    |   `-- pom.properties
    +-- project-1.0-SNAPSHOT.jar
    +-- surefire-reports
    |   +-- com.etretatlogiciels.project.AppTest.txt
    |   `-- TEST-com.etretatlogiciels.project.AppTest.xml
    `-- test-classes
        `-- com
            `-- etretatlogiciels
                `-- project
                    `-- AppTest.class

22 directories, 9 files

Running the newly built project
$ java -cp target/project-1.0-SNAPSHOT.jar com.etretatlogiciels.project.App
Hello World!

Using Maven behind a proxy

If you're trying to use Maven behind a proxy, it won't work because the biggest facet of Maven is to be able to download libraries from an on-line repository. The solution is done in the settings file, ~/.m2/settings.xml. Add the following to this file. Upon a new installation, you'll likely have to create it as it will not already exist.

    <settings>
      <proxies>
       <proxy>
          <active>true</active>
          <protocol>http</protocol>
          <host>web-proxy.austin.hp.com</host>
          <port>8080</port>
          <!--<username>proxyuser</username>
          <password>somepassword</password>
          <nonProxyHosts>www.google.com|*.somewhere.com</nonProxyHosts>-->
        </proxy>
      </proxies>
    </settings>

Re-Maven...

After years of eschewing Maven, I come back to it, did the download, exploded it, added its binary subdirectory to the end of my PATH, fixed working behind a proxy, then did:

~/dev/maven $ mvn archetype:generate

...whereupon, after creating all the stuff in ~/.m2/repository, it asked some things. I had forgot what all this was about. Finally, I took the default as shown, plus decided to write a Hello World project. Then I took the remainder of the defaults:

Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 328:
Choose org.apache.maven.archetypes:maven-archetype-quickstart version:
1: 1.0-alpha-1
2: 1.0-alpha-2
3: 1.0-alpha-3
4: 1.0-alpha-4
5: 1.0
6: 1.1
Choose a number: 6:
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.1/\
    maven-archetype-quickstart-1.1.jar
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.1/\
    maven-archetype-quickstart-1.1.jar (7 KB at 40.7 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.1/\
    maven-archetype-quickstart-1.1.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.1/\
    maven-archetype-quickstart-1.1.pom (2 KB at 14.9 KB/sec)
Define value for property 'groupId': : com.hp
Define value for property 'artifactId': : helloworld
Define value for property 'version':  1.0-SNAPSHOT: : 1.1
Define value for property 'package':  com.hp: :
Confirm properties configuration:
groupId: com.hp
artifactId: helloworld
version: 1.1
package: com.hp
 Y: :
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.1
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.hp
[INFO] Parameter: packageName, Value: com.hp
[INFO] Parameter: package, Value: com.hp
[INFO] Parameter: artifactId, Value: helloworld
[INFO] Parameter: basedir, Value: /home/russ/dev/maven
[INFO] Parameter: version, Value: 1.1
[INFO] project created from Old (1.x) Archetype in dir: /home/russ/dev/maven/helloworld
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1:17.788s
[INFO] Finished at: Mon Nov 18 10:57:06 MST 2013
[INFO] Final Memory: 14M/150M
[INFO] ------------------------------------------------------------------------

What I'm left with in the subdirectory in which I executed this is:

~/dev/maven $ tree
.
`-- helloworld
    +-- pom.xml
    `-- src
        +-- main
        |   `-- java
        |       `-- com
        |           `-- hp
        |               `-- App.java
        `-- test
            `-- java
                `-- com
                    `-- hp
                        `-- AppTest.java

10 directories, 3 files

Building

With the Hello World source code in place, let's just build it into a JAR. The semantics of the Maven command are surprising if you're new. install doesn't (build, then) install software in the sense you might think. What's installed is likely a JAR into your local repository.

~/dev/maven $ mvn install
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building helloworld 1.1
[INFO] ------------------------------------------------------------------------
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.6/\
    maven-resources-plugin-2.6.pom
.
.
.
(much churning here)
[INFO] Compiling 1 source file to /home/russ/dev/maven/helloworld/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ helloworld ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/russ/dev/maven/helloworld/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:2.5.1:testCompile (default-testCompile) @ helloworld ---
[INFO] Compiling 1 source file to /home/russ/dev/maven/helloworld/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ helloworld ---
.
.
.
(more churning here)
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.hp.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.007 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ helloworld ---
.
.
.
(still more churning)
[INFO] Installing /home/russ/dev/maven/helloworld/target/helloworld-1.1.jar to /home/russ/.m2/repository\
    /com/hp/helloworld/1.1/helloworld-1.1.jar
[INFO] Installing /home/russ/dev/maven/helloworld/pom.xml to /home/russ/.m2/repository/com/hp/helloworld\
    /1.1/helloworld-1.1.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 24.845s
[INFO] Finished at: Mon Nov 18 11:54:40 MST 2013
[INFO] Final Memory: 13M/111M
[INFO] ------------------------------------------------------------------------

Notice that Maven built the project, ran the JUnit test, then build the JAR file. Afterward, this is what we see:

~/dev/maven/helloworld $ ll
total 20
drwxr-xr-x 4 russ russ 4096 Nov 18 11:54 .
drwxr-xr-x 3 russ russ 4096 Nov 18 10:57 ..
-rw-r--r-- 1 russ russ  739 Nov 18 10:57 pom.xml
drwxr-xr-x 4 russ russ 4096 Nov 18 10:57 src
drwxr-xr-x 6 russ russ 4096 Nov 18 11:54 target
~/dev/maven/helloworld $ tree target
target
+-- classes
|   `-- com
|       `-- hp
|           `-- App.class
+-- helloworld-1.1.jar
+-- maven-archiver
|   `-- pom.properties
+-- surefire-reports
|   +-- com.hp.AppTest.txt
|   `-- TEST-com.hp.AppTest.xml
`-- test-classes
    `-- com
        `-- hp
            `-- AppTest.class

What's in the runnable JAR is:

~/dev/maven/helloworld $ $JAVA_HOME/bin/jar -tf ./target/helloworld-1.1.jar
META-INF/
META-INF/MANIFEST.MF
com/
com/hp/
com/hp/App.class
META-INF/maven/
META-INF/maven/com.hp/
META-INF/maven/com.hp/helloworld/
META-INF/maven/com.hp/helloworld/pom.xml
META-INF/maven/com.hp/helloworld/pom.properties

Running it

There's probably a better way, but this is the first way I found that worked and you have to pick the output out of the noise:

~/dev/maven/helloworld $ mvn exec:java -Dexec.mainClass="com.hp.App"
[INFO] Scanning for projects...
Downloading: http://repo.maven.apache.org/maven2/org/codehaus/mojo/exec-maven-plugin/maven-metadata.xml
~/dev/maven/helloworld $ mvn exec:java -Dexec.mainClass="com.hp.App"
.
.
.
(much churning here)
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building helloworld 1.1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> exec-maven-plugin:1.2.1:java (default-cli) @ helloworld >>>
[INFO]
[INFO] <<< exec-maven-plugin:1.2.1:java (default-cli) @ helloworld <<<
[INFO]
[INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ helloworld ---
.
.
.
(more churning here)
Hello World!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.927s
[INFO] Finished at: Mon Nov 18 12:16:57 MST 2013
[INFO] Final Memory: 9M/148M
[INFO] ------------------------------------------------------------------------

Saving the whales...

One thing that I loathed about Java back in the 90s when it started was the extra-deep subdirectory structure. Whoever came up with Maven was enamored with it. Whoever created Eclipse did the world a favor by tossing the unnecessary bits. Here's how to modify pom.xml so that the extra depth isn't there.

We start out with a sample project, default style:

~/dev/maven $ tree
.
`-- helloworld
    +-- pom.xml
    `-- src
        +-- main
        |   `-- java
        |       `-- com
        |           `-- hp
        |               `-- App.java
        `-- test
            `-- java
                `-- com
                    `-- hp
                        `-- AppTest.java

10 directories, 3 files

Then, we fix it to look like it was created by Eclipse instead:

~/dev/maven $ tree
.
`-- helloworld
    +-- pom.xml
    +-- src
    |   `-- com
    |       `-- hp
    |           `-- App.java
    `-- test
        `-- com
            `-- hp
                `-- AppTest.java

10 directories, 3 files

To accomplish this, we need only place the following into pom.xml:

  <build>
    <sourceDirectory>${project.basedir}/src</sourceDirectory>
    <testSourceDirectory>${project.basedir}/test</testSourceDirectory>
    <resources>
      <resource>
        <directory>${project.basedir}/src</directory>
      </resource>
    </resources>
  </build>

This works because Maven stuff is inerited and properties can be overridden. These settings are rewrites of the same ones, but only those we wish to overwrite, in the Super POM. This is from the Super POM according to POM Reference: The Basics: Inheritance: The Super POM:

  <build>
    <directory>${project.basedir}/target</directory>
    <outputDirectory>${project.build.directory}/classes</outputDirectory>
    <finalName>${project.artifactId}-${project.version}</finalName>
    <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
    <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
    <scriptSourceDirectory>src/main/scripts</scriptSourceDirectory>
    <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
    <resources>
      <resource>
        <directory>${project.basedir}/src/main/resources</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>${project.basedir}/src/test/resources</directory>
      </testResource>
    </testResources>
    .
    .
    .
  </build>

Creating my own archetype...

This is done by creating an actual, if uncoded, project. I'm following Guide to Creating Archetypes.

Step 1: Generate the project

~/dev/maven/archetype $ mvn archetype:generate \
                            -DgroupId=com.etretatlogiciels \
                            -DartifactId=java-project \
                            -DarchetypeArtifactId=maven-archetype-archetype
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom <<<
[INFO]
[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-archetype\
    /1.0/maven-archetype-archetype-1.0.jar
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-archetype\
    /1.0/maven-archetype-archetype-1.0.jar (7 KB at 25.0 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-archetype\
    /1.0/maven-archetype-archetype-1.0.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-archetype\
    /1.0/maven-archetype-archetype-1.0.pom (528 B at 3.1 KB/sec)
[INFO] Using property: groupId = com.etretatlogiciels
[INFO] Using property: artifactId = java-project
Define value for property 'version':  1.0-SNAPSHOT: : 1.0
[INFO] Using property: package = com.etretatlogiciels
Confirm properties configuration:
groupId: com.etretatlogiciels
artifactId: java-project
version: 1.0
package: com.etretatlogiciels
 Y: :
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-archetype:1.0
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.etretatlogiciels
[INFO] Parameter: packageName, Value: com.etretatlogiciels
[INFO] Parameter: package, Value: com.etretatlogiciels
[INFO] Parameter: artifactId, Value: java-project
[INFO] Parameter: basedir, Value: /home/russ/dev/maven/archetype
[INFO] Parameter: version, Value: 1.0
[INFO] project created from Old (1.x) Archetype in dir: /home/russ/dev/maven/archetype/java-project
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 13.900s
[INFO] Finished at: Mon Nov 18 15:09:24 MST 2013
[INFO] Final Memory: 15M/209M
[INFO] ------------------------------------------------------------------------

Step 2: Add the archetype.xml file

I found that java-project/src/main/resources/META-INF/maven/archetype.xml was almost what I wanted. I modified it like this:

<archetype>
  <id>java-project</id>
  <allowPartial>true</allowPartial>
  <sources>
    <source>src/App.java</source>
  </sources>
  <resources>
    <resource>src</resource>
  </resources>
  <testSources>
    <source>test/AppTest.java</source>
  </testSources>
  <testResources>
    <resource>test</resource>
  </testResources>
</archetype>

Step 3: Doctor the pom file

Actually, this is the "prototype" pom.xml spoken of in the article. They don't actually say that the pom.xml file generated is the one to modify, they only speak of "the prototype files and the prototype pom.xml".

The point is that you need to "soften" the generated pom.xml to account for how consumers of the archetype want to name their project, name their package, etc. So, my pom.xml started out this way:

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.etretatlogiciels</groupId>
  <artifactId>java-project</artifactId>
  <version>1.0</version>
  <name>Archetype - java-project</name>
  <url>http://maven.apache.org</url>
</project>

...and I modified (softened) it like this:

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>${groupId}</groupId>
  <artifactId>${artifactId}</artifactId>
  <version>${version}</version>
  <name>Archetype - java-project</name>
  <url>http://maven.apache.org</url>
</project>

Prototype files

Not part of this note: the ability to create actual Java source code with templates (soft stuff) inside that Maven will use to generate code. For example, App.java needs a soft package inside since it shouldn't be my own (com.etretatlogiciels).

Step 4: Build and install the new archetype

This puts it into the target subdirectory.

~/dev/maven/java-project $ mvn install

...with this appearance:

~/dev/maven/archetype/java-project $ tree
.
+-- pom.xml
+-- src
|   `-- main
|       `-- resources
|           +-- archetype-resources
|           |   +-- pom.xml
|           |   `-- src
|           |       +-- main
|           |       |   `-- java
|           |       |       `-- App.java
|           |       `-- test
|           |           `-- java
|           |               `-- AppTest.java
|           `-- META-INF
|               `-- maven
|                   `-- archetype.xml
`-- target
    +-- classes
    |   +-- archetype-resources
    |   |   +-- pom.xml
    |   |   `-- src
    |   |       +-- main
    |   |       |   `-- java
    |   |       |       `-- App.java
    |   |       `-- test
    |   |           `-- java
    |   |               `-- AppTest.java
    |   `-- META-INF
    |       `-- maven
    |           `-- archetype.xml
    +-- java-project-1.0.jar
    `-- maven-archiver
        `-- pom.properties

22 directories, 11 files

Please note that this "install" action causes the new archetype to be copied into Maven. That's how it's able to be used in the next step.

~/dev/maven/archetype/java-project $ locate java-project
/home/russ/.m2/repository/com/etretatlogiciels/java-project
/home/russ/.m2/repository/com/etretatlogiciels/java-project/1.0
/home/russ/.m2/repository/com/etretatlogiciels/java-project/maven-metadata-local.xml
/home/russ/.m2/repository/com/etretatlogiciels/java-project/1.0/_remote.repositories
/home/russ/.m2/repository/com/etretatlogiciels/java-project/1.0/java-project-1.0.jar
/home/russ/.m2/repository/com/etretatlogiciels/java-project/1.0/java-project-1.0.pom

Step 4: Use the new archetype

...to try it out. After a warning I got, I had to specify archetypeRepository so Maven could find it.

$ mvn archetype:generate                          \
  -DarchetypeRepository=/home/russ/.m2/repository \
  -DarchetypeGroupId=com.etretatlogiciels         \
  -DarchetypeArtifactId=java-project              \
  -DarchetypeVersion=1.0                          \
  -DgroupId=some new package path                 \
  -DartifactId=some new project name

If you got this far, you discovered that the developers of Maven were incapable of understanding that anyone would ever want to modify their precious subdirectory layout despite plent of protest to the contrary.

src
  +-- main
  |   `-- java
  `-- test
      `-- java

You simply cannot create an archetype that will modify this basic layout to match, for example, the simple layout of Eclipse's Java and WTP projects. This is why when you use Maven and Eclipse, the latter's simple layout goes out the window.

What you can do, however, especially in Eclipse and IntelliJ IDEA, is just present Maven with the structure you want to use, which is reflected slightly in pom.xml, and it will just use it without imposing the usual structure. The best advice can be found in Converting a newly set-up or existing project to Maven in Eclipse near the bottom of this page.


Maven and "scope"

One example, for now:

<scope>import</scope>

...indicates something in dependency management whose pom.xml is imported for use usually in setting the versions of many or all the JARs pulled into this project. It's instead of explicitly maintaining versions of JAR in the project's pom.xml when they must always be the same as in the JAR/project referenced by this scope.


"Shaded" JARs

Also known as "uber JARs," is when one JAR is used to subsume a bunch of other JARs, mitigating interdependence, for convenience. This is Maven terminology.


JAVA_HOME and Maven...

JAVA_HOME should be established in .profile and not use the tilde character in its definition:

export JAVA7_HOME=/home/russ/dev/jdk1.7.0_51
export JAVA6_HOME=/home/russ/dev/jdk1.6.0_45
export  JAVA_HOME=${JAVA7_HOME}
export   IDEA_JDK=${JAVA7_HOME}
export    M2_HOME=/home/russ/dev/apache-maven-3.1.1
export         M2=${M2_HOME}/bin

PATH=${JAVA_HOME}/bin:${PATH}
PATH=${PATH}:${M2}

Set Maven up as shown, too.

~ $ java -version
java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)
~ $ mvn --version
Apache Maven 3.1.1 (0728685237757ffbf44136acec0402957f723d9a; 2013-09-17 09:22:22-0600)
Maven home: /home/russ/dev/apache-maven-3.1.1
Java version: 1.7.0_51, vendor: Oracle Corporation
Java home: /home/russ/dev/jdk1.7.0_51/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.2.0-23-generic", arch: "amd64", family: "unix"

Useful Maven commands...

To equip the subdirectory with Eclipse project files (.project, .classpath and .settings):

$ mvn eclipse:eclipse

Note that, if you confirm that a dependency is a) listed in pom.xml and you can also b) see it resident in .m2/repository, but c) Eclipse still gives missing class errors it should find inside that JAR, you likely can also reissue the previous command line to help Eclipse get over it.

(See more on this command elsewhere on this page.)

Build project without running tests. It does compile the test code, it just doesn't run it:

$ mvn -DskipTests

Here's how to force Maven to update its repository for one project (or, as here, a group of projects subordinate to a project):

$ mvn dependency:resolve
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] Work-Streams-ACME
[INFO] Project configuration data
[INFO] acme-core
[INFO] acme-utils
[INFO] acme-service
[INFO] acme-web-service
[INFO] acme-api-client
[INFO] acme-person-client
[INFO] acme-client
[INFO] acme-test-fixture
[INFO] acme-service-tests
[INFO] acme-write
[INFO] acme-probe
[INFO]
...
[INFO] ------------------------------------------------------------------------
[INFO] Building Work-Streams-ACME 1.6.38-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.3:resolve (default-cli) @ acme-parent ---
[INFO]
[INFO] The following files have been resolved:
[INFO]    none
[INFO]
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Project configuration data 1.6.38-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.3:resolve (default-cli) @ acme-config ---
[INFO]
[INFO] The following files have been resolved:
[INFO]    com.acme.common:fs-initd:zip:3.0.b84:compile
[INFO]
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building cmisx-core 1.6.38-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.3:resolve (default-cli) @ acme-core ---
[INFO]
[INFO] The following files have been resolved:
[INFO]    com.acme.common:work-jvm-protector:jar:1.3.b5:compile
[INFO]    com.acme.common:work-async-service:jar:1.3.b5:compile
...
             [INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] Work-Streams-ACME ................................ SUCCESS [1.434s]
[INFO] Project configuration data ....................... SUCCESS [0.039s]
[INFO] acme-core ........................................ SUCCESS [0.183s]
[INFO] acme-utils ....................................... SUCCESS [0.094s]
[INFO] acme-service ..................................... SUCCESS [0.635s]
[INFO] acme-web-service ................................. SUCCESS [0.342s]
[INFO] acme-api-client .................................. SUCCESS [0.008s]
[INFO] acme-person-client ............................... SUCCESS [0.162s]
[INFO] acme-client ...................................... SUCCESS [0.126s]
[INFO] acme-test-fixture ................................ SUCCESS [0.137s]
[INFO] acme-service-tests ............................... SUCCESS [0.166s]
[INFO] acme-write ....................................... SUCCESS [0.175s]
[INFO] acme-probe ....................................... SUCCESS [0.179s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.885s
[INFO] Finished at: Thu Feb 13 14:08:13 MST 2014
[INFO] Final Memory: 20M/174M
[INFO] ------------------------------------------------------------------------

When experiencing inexplicable Maven errors...

When experiencing an inexplicable error running a Maven script, don't fail to think about pom.xml files in directories higher up from which the erroring one may be inheriting definitions (and problems). One of the them may have inadvertantly been mucked by a stray editor action.


Hierarchical pom.xml files

If you've got a superior (higher up) pom.xml, put the version into that and the dependency (into dependencyManagement with version, but the dependency into the subordinate pom.xml without the version. The parent pom.xml should govern the version; the child pom.xmls just eat whatever the parent says they must eat.


Interdependence and hierarchy

If module AB in project A needed to depend on dependency C for something that consumer D should be able to depend on by virtue of depending on project A for (that and many other) things, the dependency needs to be expressed in module AB's pom.xml, but dependency C need not be expressed in consumer D's pom.xml.


Maven dependencies

First, grab all dependencies:

$ mvn dependency:resolve

Then, list them with -o to keep noise low, remove extra information and duplicates:

$ mvn -o dependency:list \
        | grep ":.*:.*:.*" \
        | cut -d] -f2- \
        | sed 's/:[a-z]*$//g' \
        | sort -u

Dependency and dependency management

In pom.xml there are dependencies and mere dependency management. This...

<dependencyManagement>
  <dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk</artifactId>
    <version>1.7.11</version>
  </dependency>
</dependencyManagement>

...means, "If the Amazon Java SDK is noted as a dependency, then use this version." It does not mean, "Make the Amazon Java SDK 1.7.11 a dependency."

In IntelliJ, the module pom.xml must express the dependency if it is to exist:

<dependency>
  <groupId>com.amazonaws</groupId>
  <artifactId>aws-java-sdk</artifactId>
</dependency>

The version expressed in the root's dependencyManagement is the version that will be used. Typically, the version is the object of a property, consumed in the dependencyManagement section, but not referred to (nor even to the property) in the dependency section.


List of Maven build targets

There are also separate clean and site lifecycle targets that are different from these.

validate validate the project is correct and all necessary information is available.
compile compile the source code of the project.
test test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployed.
package take the compiled code and package it in its distributable format, such as a JAR.
integration-test process and deploy the package if necessary into an environment where integration tests can be run.
verify run any checks to verify the package is valid and meets quality criteria.
install install the package into the local repository, for use as a dependency in other projects locally.
deploy done in an integration or release environment, copies the final package to the remote repository for sharing with other developers and projects.
dependency:list mvn -o dependency:list will list out the (JAR) dependencies in your project, module-by-module if you have submodules in it.

Maven's eclipse:eclipse

To convert an existing Eclipse Java project over to use Maven, do this.

Start out with a pom.xml, you must write this yourself to prime the pump.

$ mvn eclipse:eclipse

This created .classpath and .project, but no subdirectory structure. I sort of expected something based on groupId.

After importing the project into Eclipse and creating new source folders src and test, I got:

[email protected]:~/dev/jtx$ ll
total 32
drwxr-xr-x.  5 russ russ 4096 Nov 26 14:15 .
drwxrwxr-x. 13 russ russ 4096 Nov 26 14:03 ..
-rw-r--r--.  1 russ russ 2332 Nov 26 14:15 .classpath
-rw-r--r--.  1 russ russ  821 Nov 26 13:53 pom.xml
-rw-r--r--.  1 russ russ  439 Nov 26 14:04 .project
drwxrwxr-x.  2 russ russ 4096 Nov 26 14:15 src
drwxrwxr-x.  3 russ russ 4096 Nov 26 14:15 target
drwxrwxr-x.  2 russ russ 4096 Nov 26 14:15 test

After creating packages in src and test:

[email protected]:~/dev/jtx$ tree
.
+-- pom.xml
+-- src
|   `-- com
|       `-- acme
|           `-- jtx
+-- target
|   `-- classes
|       `-- com
|           `-- acme
|               `-- jtx
+── test
    `-- com
        `-- acme
            `-- jtx

13 directories, 1 file

Doing mvn eclipse:eclipse, then importing a project into the Eclipse workspace isn't quite enough.

Right-click project and choose Configure → Convert to Maven Project. This option will not exist (contextually) if it's already a Maven project.

Once that's done, right-click the project again and choose Maven → Download JavaDoc (sic). Indeed, after doing that, comparing .m2/repository contents with what they used to be will reveal that the Javadoc did indeed come down.

However, waving the mouse over symbols, control-clicking a symbol, etc. still will not appear helpful, but ContentAssist will begin to work. To convince yourself, type a variable name, followed by a dot (.) and then press Ctrl + Spacebar.

(Thanks, Scott!)

Now, this is what Build Path looks like after mvn eclipse:eclipse. You must construct pom.xml right, which Configure → Convert to Maven Project will screw up, so keep a copy of what you wanted because you'll need to edit the result. I found, in particular, that the dependencies had been smoked.

On occasion (still haven't figured out what happens or how to fix it), right-clicking the project will not offer a Maven item (for use in choosing Download JavaDoc).

What I found is that I had to do Configure → Convert to Maven Project a second time. Then the Maven item appeared. Also, my dependencies did not disappear when I did it the second time.

Here are the differences with the project before I added Maven to it. Also, I went on to solve a number of other problems arising.

.classpath:
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-    <classpathentry kind="src" path="src"/>
-    <classpathentry kind="src" path="test"/>
-    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
-    <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
-    <classpathentry kind="lib" path="lib/log4j-api-2.1.jar" sourcepath="/home/russ/dev/json-to-xml/lib/log4j-api-2.1-sources.jar">
+    <classpathentry kind="var" path="M2_REPO/org/apache/logging/log4j/log4j-api/2.1/log4j-api-2.1.jar"/>
+    <classpathentry kind="var" path="M2_REPO/org/apache/logging/log4j/log4j-core/2.1/log4j-core-2.1.jar"/>
+    <classpathentry kind="var" path="M2_REPO/junit/junit/4.11/junit-4.11.jar">
         <attributes>
-            <attribute name="javadoc_location" value="jar:platform:/resource/json-to-xml/lib/log4j-api-2.1-javadoc.jar!/"/>
+            <attribute name="javadoc_location" value="jar:file:/home/russ/.m2/repository/junit/junit/4.11/junit-4.11-javadoc.jar!/"/>
         </attributes>
     </classpathentry>
-    <classpathentry kind="lib" path="lib/log4j-core-2.1.jar" sourcepath="lib/log4j-core-2.1-sources.jar">
+    <classpathentry kind="var" path="M2_REPO/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar">
         <attributes>
-            <attribute name="javadoc_location" value="jar:platform:/resource/json-to-xml/lib/log4j-core-2.1-javadoc.jar!/"/>
+            <attribute name="javadoc_location" value="jar:file:/home/russ/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-javadoc.jar!/"/>
         </attributes>
     </classpathentry>
-    <classpathentry kind="output" path="bin"/>
+    <classpathentry including="**/*.java" kind="src" output="target/classes" path="src">
+        <attributes>
+            <attribute name="optional" value="true"/>
+            <attribute name="maven.pomderived" value="true"/>
+        </attributes>
+    </classpathentry>
+    <classpathentry kind="src" output="target/test-classes" path="test">
+        <attributes>
+            <attribute name="optional" value="true"/>
+            <attribute name="maven.pomderived" value="true"/>
+        </attributes>
+    </classpathentry>
+    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
+        <attributes>
+            <attribute name="maven.pomderived" value="true"/>
+        </attributes>
+    </classpathentry>
+    <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+        <attributes>
+            <attribute name="maven.pomderived" value="true"/>
+        </attributes>
+    </classpathentry>
+    <classpathentry kind="output" path="target/classes"/>
 </classpath>
.project:
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
        <name>json-to-xml</name>
-       <comment></comment>
+       <comment>NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are
not supported in M2Eclipse.</comment>
        <projects>
        </projects>
        <buildSpec>
@@ -10,8 +10,14 @@
                        <arguments>
                        </arguments>
                </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.m2e.core.maven2Builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
        </buildSpec>
        <natures>
+               <nature>org.eclipse.m2e.core.maven2Nature</nature>
                <nature>org.eclipse.jdt.core.javanature</nature>
        </natures>
 </projectDescription>
.settings/org.eclipse.jdt.core.prefs:
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
.settings/org.eclipse.m2e.core.prefs:

(new file)

activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1
pom.xml:

(This is my new one.)

<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>com.acme</groupId>
  <artifactId>json-to-xml</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <name>json-to-xml</name>
  <description>JSON-to-XML filter not requiring a POJO description of the JSON.</description>

  <properties>
    <log4j.version>2.1</log4j.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <build>
    <sourceDirectory>src</sourceDirectory>
    <testSourceDirectory>test</testSourceDirectory>
    <resources>
      <resource>
        <directory>src</directory>
        <excludes>
          <exclude>**/*.java</exclude>
        </excludes>
      </resource>
    </resources>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.1</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>
target:

(new subdirectory because Maven doesn't like bin)

.gitignore:

(ibid)

-bin/
+target/
 *.swp

Other stuff:

[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!

Add:

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

If you see:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
(default-compile) on project json-to-xml: Fatal error compiling: invalid target release: 1.8 → [Help 1]
Do this:
~ $ cat .mavenrc
JAVA_HOME=/home/russ/dev/jdk1.8.0_25

Using or not Maven: Apache HTTP as a practical example

(This is advice I once gave to someone in the Eclipse Newcomers' forum.)

A wee bit new to Eclipse? You need to get the JARs from Apache ( https://hc.apache.org/downloads.cgi. How this is done depends on how you build.

Method 1, the hard way

You're not using Maven or Ivy.
You download either the binary or the source.

If new to Eclipse, you should at least do this once so you sort of understand how JARs work directly in the project.

Disadvantages

Advantages

Unless you want to look at the source code or think Apache's got a bug you'll have to trace through, download the binary.

  1. Create a subdirectory, lib, in your project.
  2. Use Build Path → Libraries → Add JARs to add:
    • httpclient-4.3.6.jar
    • httpclient-cache-4.3.6.jar
    • httpcore-4.3.3.jar
    • httpmime-4.3.6.jar
  3. You'll also need:
    • commons-codec-1.6.jar
    • commons-logging-1.1.3.jar
    • fluent-hc-4.3.6.jar

As soon as you do the Build Path step, this will make Apache HTTP interface available to Eclipse Content Assist and you can use the errors flagged by Eclipse in your source code to get the imports added (Eclipse will do this for you).

Method 2, the easy, modern way

Use Maven, Ivy, Gradle, etc. (Maven shown here.) You must add Apache HTTP Components to the <dependencies> section in your pom.xml file:

  1. Go to http://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient/4.3.6 and look at the XML to copy and paste under the Maven tab:
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.3.6</version>
    </dependency>
    

Disadvantages

Advantages

If you're not using Maven or Ivy, you should make a mental note to learn. The curve's a little steep and confusing at first, but Maven, as much as I may not like it personally, is the lingua franca in our industry today.


A brief exposé on Maven dependency

(Yeah, I know, this is pretty random.)

In the <dependencyManagement> section it's possible to specific a "super" project whose pom.xml will be used to specific dependencies. This will suffice also for subdirectory pom.xmlfiles. In our exercise here, we're replacing just such a super project, fs-dependency-management with another, tem-temple.

Any project that's got an integration.properties file is a "top-level" project and is a candidate, like tem-temple, for becoming a "super" project. Whether chosen so or not, it falls outside most of this discussion in that such a project must not make use of dependency management to name another project, like fs-dependency-management, tem-temple or other, from which to take its dependencies and versions.

For projects that aren't top-level...

Anything that's not in the "super" project's pom.xml will need to be listed in the project pom.xml with its version because not obtainable from the "super" project's pom.xml. Versions are ideally specified in the project's "parent" pom.xml' <properties> section; child pom.xml files will get them from there, they do not need repeating in sibling pom.xml files, etc.

In subordinate pom.xml files, use the <dependencies> section, and not the <dependencyManagement> section. The group- and artifactId need listing; use the macro definition for the version:

<dependency>
  <groupId>com.acme.fireworks</groupId>
  <artifactId>jato-assist-rocket</artifactId>
  <version>${jato-assist-rocket.version}</version>
</dependency>

However, any dependency already specified in the parent (wait for it) or super project need not have its version specified.

Dependency specification in super- and parent projects

Specifying dependencies this way will ensure that all adopting projects, whether by dependency management or by hierarchical inheritance, are enforced in the child pom.xml.

In the super project, the dependency is specified using the standard <dependencies> section including version, of course.

In the simple parent pom.xml file, this can be done two ways.

  1. A dependency can be set up in <dependencyManagement> with version. This means that, "if any child pom.xml specifies a need for a that dependency, it will get the same one (i.e.: same version) specified in the parent.
  2. A dependency can be defined outside the <dependencyManagement> section. If a parent does this, it is available via inheritance to the child pom.xml (though the child may override, but this is not recommended).


AntBuildException
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-antrun-plugin:1.7:run (blah-xml) \
Jon project blah: An Ant BuildException has occured: Execute failed: java.io.IOException: Cannot run \
program "xmllint" (in directory "/home/russ/acme-projects/blah/target/sources"): error=2, No such file or directory
[ERROR] around Ant part ...... @ 18:120 in /home/russ/acme-projects/blah/target/antrun/build-main.xml

xmllint is missing. Install it:

$ sudo bash
# apt-get update
# apt-get install libxml2-utils

Converting a newly set up or existing project to Maven in Eclipse

I don't use Maven archetypes as I've long been used to doing things Eclipse's way and I like the organization and the decrease in subdirectory depth anyway. Here's how I convert a project to using Maven.

I had to install Maven (~/dev/apache-maven-3.3.3) and set up environment variables M2_HOME, M2 and JAVA_HOME as I believe is recounted at the top of this page.

What did I do to promote the project to a Maven build?

  1. I copied over the pom.xml of another, unrelated project as a starting point. I changed all its features to reflect the new project and set up the dependencies that you can find noted whenever you try to grab a library, read a tutorial, etc. Google around for the JAR you're after and you'll probably find a suitable dependency statement to put into your pom.xml.

  2. I right-clicked the project and chose Configure → Convert to Maven Project....

  3. I opened a shell and went to the project root, because I'm more of a command-line guy, and did mvn test compile. I could have right-clicked on the project, chosen Maven → Update Project... (I have tried this; it does work).

  4. I kept running the command-line compile and also Eclipse Project → Clean, and fixing pom.xml (mostly adding the structures JARs) until I got a clean build.

Decidedly, I've been shamed into becoming a Maven guy.


How Maven inheritance works (and a note on aggregation)

The parent project pom.xml file can express that its project has a specific packaging, namely, pom (see below). Property definitions, dependencies, plug-ins, repositories and other resources, except those the subordinate wishes to make different from the parent, are inherited.

Basically, this is how to proceed. The parent pom.xml is sketched out with all the properties, repositories (if explicit specifications to make), dependencies, plug-ins, etc.

<xml version="1.0">
<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.4000
                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.etretatlogiciels</groupId>
  <artifactId>some-code</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <organization>
    <name>Etretat Logiciels, DBA</name>
    <url>http://www.etretatlogiciels.com</url>
  </organization>

  ...

  <properties>
    <junit-version>4.11<junit-version>
    <surefire-version>2.15<surefire-version>
  <properties>

  <dependencies>
    <dependency>
      <groupId>junit<groupId>
      <artifactId>junit<artifactId>
      <version>${junit-version}<version>
      <scope>test<scope>
    <dependency>
  <dependencies>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins<groupId>
          <artifactId>maven-surefire-plugin<artifactId>
        <version>${surefire-version}<version>
      <plugin>
    <plugins>
    <pluginManagement>
  <build>
</project>

...and its child pom.xml is templated thus:

<xml version="1.0">
<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.4000
                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.etretatlogiciels</groupId>
    <artifactId>some-code</artifactId>
    <version>1.0.0</version>
    <relativePath>../pom.xml</relativePath>
  <parent>

  <artifactId>api</artifactId>
  <packaging>jar</packaging>
  <name>module</name>
</project>

Aggregation...

...is when you use <modules><module>...</module></modules> to list the subordinate components of a project. It's not well explained how this differs from hierarchy and inheritance still applies, and there's also a way to share resources between subordinate pom.xml, but you didn't hear it from me.


How Maven repositories work

Downloading during Maven build...

  1. By default, you download JARs from the central repository.
  2. To override this, you can specify a mirror either by a statement in the individual pom.xml or a setting in ~/.m2/settings.xml.
    <settings>
    ...
      <mirrors>
        <mirror>
          <id>       UK                          </id>
          <name>     UK Central                  </name>
          <url>      http://uk.maven.org/maven2  </url>
          <mirroOf>  central                     </mirroOf>
      </mirror>
    </mirrors>
    ...
    </settings>
    
  3. You can download from an internal repository doing this (in pom.xml):
    <project>
      ...
      <repositories>
        <repository>
          <id>   local-maven-repository  </id>
          <url>  http://maven.local      </url>
        </repository>
      </repositories>
      ...
    </project>
    
  4. You can force Mave to use a single, in-house repository mirroring all requests and containing all of the desired artifacts.
    <settings>
    ...
      <mirrors>
        <mirror>
          <id>       internal-repository                       </id>
          <name>     Maven repository manager running locally  </name>
          <url>      http://maven.local                        </url>
          <mirroOf>  *                                         </mirroOf>
      </mirror>
    </mirrors>
    ...
    </settings>
    

Uploading...

  1. You must have privileges to upload.
  2. You need access via SCP, SFTP, FTP, WebDAV or the filesystem using a wagon, or plug-in, in Maven.
  3. In setting up an internal repository, you need only follow the layout already in use at other, remote sites. For example, ...
    maven2
    +-- repository1
    +-- repository2
    `-- repositoryN
        `-- software-name
            +-- name1
            +-- name2
            `-- nameN
                +-- version1
                +-- version2
                `-- versionN
                    +-- name-versionN.jar
                    +-- name-versionN.jar.md5
                    +-- name-versionN.jar.sha1
                    +-- name-versionN.javadoc.jar
                    +-- name-versionN.javadoc.jar.md5
                    +-- name-versionN.javadoc.jar.sha1
                    +-- name-versionN.pom
                    +-- name-versionN.pom.md5
                    +-- name-versionN.pom.sha1
                    +-- name-versionN.sources.jar
                    +-- name-versionN.sources.jar
                    +-- name-versionN.sources.jar
                    +-- maven-metadata.xml
                    +-- maven-metadata.xml.md5
                    `-- maven-metadata.xml.sha1
    

    The best way to do this is to build a project, then copy the downloaded JARs to the site demonstrated just above.


Local-to-project Maven respository

Let's say I'm working on a project named project. (I have to lay this out yet again because I can't find that I doc'd it anywhere.)

Please note the following, local place to put a JAR, cda2fhir-0.2.jar, that is not in any Maven central repository, so that it gets built in my project:

~/dev/project $ tree lib
lib
└── tr
    └── com
        └── srcdc
            └── cda2fhir
                ├── 0.2
                │   ├── cda2fhir-0.2.jar
                │   ├── cda2fhir-0.2.jar.md5
                │   ├── cda2fhir-0.2.jar.sha1
                │   ├── cda2fhir-0.2.pom
                │   ├── cda2fhir-0.2.pom.md5
                │   └── cda2fhir-0.2.pom.sha1
                ├── maven-metadata.xml
                ├── maven-metadata.xml.md5
                └── maven-metadata.xml.sha1
5 directories, 9 files

...and here are the entries in 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">
  .
  .
  .
  <properties>
    <cda2fhir.version>0.2</cda2fhir.version>
  </properties>
  .
  .
  .
  <repositories>
    <repository>
      <id>data-local</id>
      <name>data</name>
      <-- Hey, this next line is the same as:
            "file:///home/russ/dev/project/lib"
          -->
      <url>file://${project.basedir}/lib</url>
    </repository>
    .
    .
    .
  </repositories>
  .
  .
  .
  <dependencies>
    <dependency>
      <groupId>tr.com.srcdc</groupId>
      <artifactId>cda2fhir</artifactId>
      <version>${cda2fhir.version}</version>
    </dependency>
    .
    .
    .
  <dependencies>

hostname in certificate didn't match

You see this in Maven:

[ERROR]     Unresolveable build extension: Plugin org.apache.nifi:nifi-nar-maven-plugin:1.0.1-incubating or one of its
dependencies could not be resolved: Failed to read artifact descriptor for
org.apache.nifi:nifi-nar-maven-plugin:jar:1.0.1-incubating: Could not transfer artifact
org.apache.nifi:nifi-nar-maven-plugin:pom:1.0.1-incubating from/to central (https://repo.maven.apache.org/maven2):
hostname in certificate didn't match: &tl;repo.maven.apache.org> != &tl;repo1.maven.org> OR &tl;repo1.maven.org> -> [Help 2]

It's likely something very temporary broken at the repository server that will be fixed soon, but in the meantime, add this to your Maven command line:

-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true

Symbol TestName causes "org.junit.rules does not exist"

You see this in Maven (or IntelliJ IDEA):

package org.junit does not exist

because you're using TestName. Why?

It's because TestName is fairly recent to JUnit (don't know the exact version it was introduced) and Maven is getting an older version.

Even if you specify the latest version of JUnit:

<properties>
  <junit.version>4.12</junit.version>
.
.
.
<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>[${junit.version}]</version>
  </dependency>
</dependencies>

...you may not be getting (v4.12) since specifying

  <version>4.12</version>

...only means "allow anything, but prefer 4.12." When a conflict is detected, Maven is allowed to "choose the best version." That won't be v4.12 unless it happens to be present in ~/.m2/repository/org/junit.

To force v4.12 (and Maven downloading it), use brackets:

<properties>
  <junit.version>4.12</junit.version>
.
.
.
<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>[${junit.version}]</version>
  </dependency>
</dependencies>

Maven "lastUpdated" problem...

You invoke the build and get this message at some point:

[ERROR] Failed to execute goal on project <project name>: Could not resolve dependencies for project
<some project- or module name and version> Failure to find org.apache.commons:commons-io:jar:2.5 in
file:///home/russ/sandboxes/<some Maven repository path> was cached in the local repository,
resolution will not be reattempted until the update interval of psjbase has elapsed
or updates are forced

You look at ~/.m2/repository/org/apache/commons/commons-io and see:

~/.m2/repository/org/apache/commons/commons-io $ tree
.
└─ 2.5
    ├─ commons-io-2.5.jar.lastUpdated
    └─ commons-io-2.5.pom.lastUpdated

You use mvn -U clean compile, etc. to solve the problem because that's what the folks on stackoverflow.com are saying, but to no avail. What to do?

In my case, I remembered a development host I had that probably had this artifact on it and, sure enough, I found it under the version 1.3.2. I copied it and surgically implanted it in my local repository:

~/.m2/repository/org/apache/commons/commons-io $ tree
.
├─ 1.3.2
│   ├─ commons-io-1.3.2.pom
│   ├─ commons-io-1.3.2.pom.sha1
│   └─ _maven.repositories
└─ 2.5
    ├─ commons-io-2.5.jar.lastUpdated
    └─ commons-io-2.5.pom.lastUpdated

commons-io.jar: Apache Commons I/O not in Maven?

My code requires commons-io-2.5.jar where I get some classes and/or methods that I cannot get from commons-io-1.3.2.jar. Well, this is a long and nasty journey. You can skip to the end to see how simple a problem it was, but the journey itself is instructive to see how I tried to debug it.

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.5</version>
</dependency>

When I attempt a build of the project requiring the later JAR, I get this error from Maven. (I'm doing a little wrapping and highlighting to make this clearer):

.
.
.
Downloading: https://repo.maven.apache.org/maven2/org/apache/commons/commons-io/2.5/commons-io-2.5.jar
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] IMAT Solutions NiFi Pipeline ....................... SUCCESS [  0.777 s]
[INFO] nifi-shared ........................................ SUCCESS [  0.078 s]
[INFO] cda-filter ......................................... SUCCESS [  0.027 s]
[INFO] fhir-processors .................................... FAILURE [  1.148 s]
[INFO] legacy ............................................. SKIPPED
[INFO] medical-filter ..................................... SKIPPED
[INFO] jdbc ............................................... SKIPPED
[INFO] standard-processors ................................ SKIPPED
[INFO] nifi-imat-nar ...................................... SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.305 s
[INFO] Finished at: 2017-03-29T15:03:09-06:00
[INFO] Final Memory: 28M/603M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project fhir-processors:
    Could not resolve dependencies for project com.imatsolutions.nifi.pipeline:fhir-processors:jar:1.0.0:
    Could not find artifact org.apache.commons:commons-io:jar:2.5
       in data-local (file:///home/russ/sandboxes/nifi-pipeline.v73_release.dev/code/nifi-pipeline/fhir-processors/lib)
.
.
.

It's what's happening a few lines above that tells the story (in green): Maven has gone to Apache's Maven repository to fetch the requested JAR (but, it wasn't there). Let's demonstrate why using wget on that path:

$ wget https://repo.maven.apache.org/maven2/org/apache/commons/commons-io/2.5/commons-io-2.5.jar
--2017-03-29 15:05:41--  https://repo.maven.apache.org/maven2/org/apache/commons/commons-io/2.5/commons-io-2.5.jar
Resolving repo.maven.apache.org (repo.maven.apache.org)... 151.101.48.215
Connecting to repo.maven.apache.org (repo.maven.apache.org)|151.101.48.215|:443... connected.
HTTP request sent, awaiting response... 404 Not Found
2017-03-29 15:05:42 ERROR 404: Not Found.

In a web browser, https://repo.maven.apache.org/maven2/org/apache/commons/commons-io/ displays that only version 1.3.2 is even there:

Index of /maven2/org/apache/commons/commons-io/

../
1.3.2/                                   2012-12-20 06:21                    -
maven-metadata.xml                       2007-07-02 14:32                  304
maven-metadata.xml.md5                   2007-07-02 14:32                   32
maven-metadata.xml.sha1                  2007-07-02 14:32                   40

...and, when you look inside at what is really there:

Index of /maven2/org/apache/commons/commons-io/1.3.2

../
commons-io-1.3.2.jar                     2007-07-02 14:32                87776
commons-io-1.3.2.jar.asc                 2007-06-26 20:59                  194
commons-io-1.3.2.jar.asc.md5             2010-11-11 22:50                   32
commons-io-1.3.2.jar.asc.sha1            2010-11-11 22:50                   40
commons-io-1.3.2.jar.md5                 2007-07-02 14:32                   32
commons-io-1.3.2.jar.sha1                2007-07-02 14:32                   40
commons-io-1.3.2-javadoc.jar             2007-07-02 14:31               383040
commons-io-1.3.2-javadoc.jar.asc         2007-06-26 20:58                  194
commons-io-1.3.2-javadoc.jar.asc.md5     2010-11-11 22:50                   32
commons-io-1.3.2-javadoc.jar.asc.sha1    2010-11-11 22:50                   40
commons-io-1.3.2-javadoc.jar.md5         2007-07-02 14:31                   32
commons-io-1.3.2-javadoc.jar.sha1        2007-07-02 14:31                   40
commons-io-1.3.2.pom                    2012-10-09 14:28                 640
commons-io-1.3.2.pom.asc                 2012-10-09 14:34                  189
commons-io-1.3.2.pom.asc.md5             2012-10-09 14:34                   32
commons-io-1.3.2.pom.asc.sha1            2012-10-09 14:34                   40
commons-io-1.3.2.pom.md5                 2012-10-09 14:29                   32
commons-io-1.3.2.pom.sha1                2012-10-09 14:29                   40
commons-io-1.3.2-sources.jar             2007-07-02 14:31               135544
commons-io-1.3.2-sources.jar.asc         2007-06-26 20:58                  194
commons-io-1.3.2-sources.jar.asc.md5     2010-11-11 22:50                   32
commons-io-1.3.2-sources.jar.asc.sha1    2010-11-11 22:50                   40
commons-io-1.3.2-sources.jar.md5         2007-07-02 14:31                   32
commons-io-1.3.2-sources.jar.sha1        2007-07-02 14:31                   40

...by looking at the contents of commons-io-1.3.2.pom, you see that:

<project 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.apache.commons</groupId>
    <artifactId>commons-io</artifactId>
    <version>1.3.2</version>
    <distributionManagement>
    <relocation>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <message>https://issues.sonatype.org/browse/MVNCENTRAL-244</message>
    </relocation>
  </distributionManagement>
</project>

Apache tells Maven, even though the JARs are sitting right there, to go get them from Maven Central!

The work-around...

So, taking a hint from this, I created v2.5 in the existing file system structure of my local Maven repository:

~/.m2/repository/org/apache/commons/commons-io $ tree
.
├─ 1.3.2
│   ├─ commons-io-1.3.2.pom
│   ├─ commons-io-1.3.2.pom.sha1
│   └─ _maven.repositories
└─ 2.5
    └─ commons-io-2.5.pom

...where commons-io-2.5.pom is nothing more than a copy of what's in commons-io-1.3.2.pom with its version changed. That way, Maven goes to Maven Central to get this JAR.

Magically (not!), my build begins to work.

The real answer is nothing more than a mistaken groupId, as I learned after a visit to the mailing list, [email protected]. The correct dependency specification is:

<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.5</version>
</dependency>

In my defence, please note that there are other Apache Commons products that contain "apache" as an element in the groupId. I actually, brainlessly thought they were all this way.

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-csv</artifactId>
  <version>1.4</version>
</dependency>