Russell Bateman 2009 last update:
Also see old Maven notes.
There is probably no better—easier to read, clearer to understand—exposé than Baeldung's short Apache Maven Tutorial.
See How to Publish Your Artifacts to Maven Central
Maven comes from Yiddish meyvn, "one who understands." Extrapolated due to usage, a maven is someone who is an expert.
Apache Maven is an Apache Software Foundation project that is used primarily to build and manage Java-based projects, but it can be used for C#, Ruby, Scala and many other languages.
Maven is basically a plug-in execution framework. Everthing that happens in the build process happens because some plug-in accomplishes it. There is a core if limited set of plug-ins provided when Maven is installed, but there are also important third-party plug-ins.
Any time a developer nees to perform custom tasks just a bit out of the ordinary, such as create WAR or NAR files, often it's a third-party developer that wrote it.
(It's probably best to defer to the installation instructions which were recorded long after these.)
$ export M2_HOME=~/dev/apache-maven/apache-maven-3.1.1 $ export M2=${M2_HOME}/bin
$ PATH=${PATH}:${M2}
This assumes you've set up your user to consume a valid JDK with JAVA_HOME set up because you're using (Eclipse, IntelliJ IDEA, etc.).
$ sudo bash # cd /opt
/opt # tar -zxf /home/russ/Downloads/apache-maven-x.y.z-bin.tar.gz
M2_HOME=/opt/apache-maven-3.8.1
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/opt/apache-maven-3.8.1/bin"
# update-alternatives --install "/usr/bin/mvn" "mvn" "/opt/apache-maven-x.y.z/bin/mvn" # update-alternatives --set mvn /opt/apache-maven-x.y.z/bin/mvn
# wget https://raw.github.com/dimaj/maven-bash-completion/master/bash_completion.bash --output-document /etc/bash_completion.d/mvn
$ mvn --version Apache Maven 3.6.3 Maven home: /usr/share/maven Java version: 11.0.10, vendor: AdoptOpenJDK, runtime: /home/russ/dev/jdk-11.0.10+9 Default locale: en_US, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-74-generic", arch: "amd64", family: "unix"
~/dev $ mkdir project
$ mvn archetype:generate -DgroupId=com.etretatlogiciels.project \ -DartifactId=project \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false
What this means...
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
$ 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
$ java -cp target/project-1.0-SNAPSHOT.jar com.etretatlogiciels.project.App Hello World!
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>
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
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
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] ------------------------------------------------------------------------
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:
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>
This is done by creating an actual, if uncoded, project. I'm following Guide to Creating Archetypes.
~/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] ------------------------------------------------------------------------
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>
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>
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).
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
...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.
In Maven 2, if you didn't specify the version for plug-ins and dependencies used in pom.xml, it would pick the latest plugin version automatically. With Maven 3, you get errors like:
[WARNING] Some problems were encountered while building the effective model [ERROR] Some problems were encountered while processing the POMs 'dependencies.dependency.version' for \ package-path:jar is missing @ line number, column column number
To fix this, you have to specify the plug-in or JAR version.
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.
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 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"
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 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.
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.
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.
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
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.
There are also separate clean and site lifecycle targets that are different from these.
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.
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:
russ@nargothrond:~/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:
russ@nargothrond:~/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.
<?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>
<?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>
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
(new file)
activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1
(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>
(new subdirectory because Maven doesn't like bin)
(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]
~ $ cat .mavenrc JAVA_HOME=/home/russ/dev/jdk1.8.0_25
(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.
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.
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.
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).
Use Maven, Ivy, Gradle, etc. (Maven shown here.) You must add Apache HTTP Components to the <dependencies> section in your pom.xml file:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.3.6</version> </dependency>
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.
(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.
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.
See these notes.
[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
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?
Decidedly, I've been shamed into becoming a Maven guy.
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>
The purpose of <pluginManagement /> is to configure project builds that inherit from the pom.xml that defines <pluginManagement />. What's inside <pluginManagement /> doesn't affect the the project build (i.e.: the root pom.xml), but the behavior of the subordinate pom.xml files.
...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.
Downloading during Maven build...
<settings> ... <mirrors> <mirror> <id> UK </id> <name> UK Central </name> <url> http://uk.maven.org/maven2 </url> <mirroOf> central </mirroOf> </mirror> </mirrors> ... </settings>
<project> ... <repositories> <repository> <id> local-maven-repository </id> <url> http://maven.local </url> </repository> </repositories> ... </project>
<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...
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.
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>
It looks like I'm going to have to create a private library subdirectory à la Maven because the latest of the MDHT (and Eclipse) JARs aren't available via Maven repositories, not even the http://devsoap.sitenv.org:8081/artifactory. I attempted to e-mail [email protected] for help or advice on this artifactory, but that e-mail address no longer exists.
So, I have little choice but to attempt to proceed. The biggest challenge will be to find pom.xml for each of these JARs. See Local-to-project Maven repository for how to do this. It's going to be very tedious. The JARs in question are listed here. I'm checking the dates of the JARs to determine which can still be got via Maven. The JARs listed here purport to be the latest and, for the most part, they are later than what I've been getting via Maven. The ones I mark with ✓ will be got via existing Maven means; the rest will have to be set up. Those marked with ✗ do not appear to be used so far despite their presence in this list.
russ@nargothrond ~/Downloads/mdht/mdhtruntime $ tree . ├── mdht │ ├── org.eclipse.mdht.emf.runtime-3.0.0.201802220601.jar │ ├── org.eclipse.mdht.uml.cda-3.0.0.201802220601.jar │ ├── org.eclipse.mdht.uml.hl7.datatypes-3.0.0.201802220601.jar │ ├── org.eclipse.mdht.uml.hl7.rim-3.0.0.201802220601.jar │ ├── org.eclipse.mdht.uml.hl7.vocab-3.0.0.201802220601.jar │ ├── ✗ org.hl7.cbcc.privacy.consentdirective_1.0.0.20170920.jar │ ├── ✗ org.hl7.security.ds4p.contentprofile_3.0.0.20170920.jar │ ├── org.openhealthtools.mdht.uml.cda.consol2-3.0.3.20180222.jar │ └── org.openhealthtools.mdht.uml.cda.mu2consol-3.0.3.20180222.jar └── non-mdht ├── ✓ lpg.runtime.java-2.0.17.v201004271640.jar ├── org.eclipse.emf.common-2.12.0.v20160420-0247.jar ├── org.eclipse.emf.ecore-2.12.0.v20160420-0247.jar ├── org.eclipse.emf.ecore.xmi-2.12.0.v20160420-0247.jar ├── org.eclipse.ocl-3.6.0.v20160523-1914.jar ├── org.eclipse.ocl.common-1.4.0.v20160521-2033.jar ├── org.eclipse.ocl.ecore-3.6.0.v20160523-1914.jar ├── org.eclipse.uml2.common-2.1.0.v20170227-0935.jar └── org.eclipse.uml2.types-2.0.0.v20170227-0935.jar
Here's what the pom.xml file looks like. Unless I can find another way, I'll have to generate these by hand for all of the above.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <groupId>org.eclipse.emf.common</groupId> <artifactId>org.eclipse.emf.common</artifactId> <version>2.11.1.v20160208-0816</version> <description>Artifactory auto generated POM</description> </project>
I have seen scripts to do this, however. Here's one to try:
#!/bin/sh if [ -n "$*" ]; then mvn -e install:install-file \ -DlocalRepositoryPath=$1 \ -DcreateChecksum=true \ -Dpackaging=jar \ -Dfile=$2 \ -DgroupId=$3 \ -DartifactId=$4 \ -Dversion=$5 \ -Dpackaging=jar \ -DgeneratePom=true else echo "Create local artifactory repository, arguments:" echo " 1: path to repository subdirectory" echo " 2: path to JAR" echo " 3: groupId" echo " 4: artifactId" echo " 5: version" fi
...where (most of this comes from pom.xml dependencies):
(The suggestion comes from https://stackoverflow.com/questions/2018965/maven-installing-artifacts-to-a-local-repository-in-workspace.) Let's run this and see:
russ@nargothrond ~/Downloads/mdht/mdhtruntime/non-mdht $ ../make-repo.sh \ ../lib \ ./org.eclipse.uml2.types-2.0.0.v20170227-0935.jar \ org.eclipse.uml2.types \ org.eclipse.uml2.types \ 2.0.0.v20170227-0935 [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Maven Stub Project (No POM) 1 [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- maven-install-plugin:2.5.2:install-file (default-cli) @ standalone-pom --- [INFO] pom.xml not found in org.eclipse.uml2.types-2.0.0.v20170227-0935.jar [INFO] Installing /home/russ/Downloads/mdht/mdhtruntime/non-mdht/org.eclipse.uml2.types-2.0.0.v20170227-0935.jar \ to /home/russ/Dow... [INFO] Installing /tmp/mvninstall614828569340065552.pom to /home/russ/Downloads/mdht/mdhtruntime/lib/org/ecl... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 0.289 s [INFO] Finished at: 2018-06-01T11:26:41-06:00 [INFO] Final Memory: 8M/481M [INFO] ------------------------------------------------------------------------
...and I see this:
russ@nargothrond ~/Downloads/mdht/mdhtruntime $ tree lib lib └── org └── eclipse └── uml2 └── types └──org.eclipse.uml2.types ├── 2.0.0.v20170227-0935 │ ├── org.eclipse.uml2.types-2.0.0.v20170227-0935.jar │ ├── org.eclipse.uml2.types-2.0.0.v20170227-0935.jar.md5 │ ├── org.eclipse.uml2.types-2.0.0.v20170227-0935.jar.sha1 │ ├── org.eclipse.uml2.types-2.0.0.v20170227-0935.pom │ ├── org.eclipse.uml2.types-2.0.0.v20170227-0935.pom.md5 │ └── org.eclipse.uml2.types-2.0.0.v20170227-0935.pom.sha1 ├── maven-metadata-local.xml ├── maven-metadata-local.xml.md5 └── maven-metadata-local.xml.sha1 7 directories, 9 files
...and pom.xml (generated) contains:
<?xml version="1.0" encoding="UTF-8"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <groupId>org.eclipse.uml2.types</groupId> <artifactId>org.eclipse.uml2.types</artifactId> <version>2.0.0.v20170227-0935</version> <description>POM was created from install:install-file</description> </project>
So, this is still tedious, but a lot less work than what I was imagining. Here's a script for the first set:
#!/bin/sh ../make-repo.sh ../lib \ org.eclipse.mdht.emf.runtime-3.0.0.201802220601.jar \ org.eclipse.mdht.emf.runtime \ org.eclipse.mdht.emf.runtime \ 3.0.0.201802220601 ../make-repo.sh ../lib \ org.eclipse.mdht.uml.cda-3.0.0.201802220601.jar \ org.eclipse.mdht.uml.cda \ org.eclipse.mdht.uml.cda \ 3.0.0.201802220601 ../make-repo.sh ../lib \ org.eclipse.mdht.uml.hl7.datatypes-3.0.0.201802220601.jar \ org.eclipse.mdht.uml.hl7.datatypes \ org.eclipse.mdht.uml.hl7.datatypes \ 3.0.0.201802220601 ../make-repo.sh ../lib \ org.eclipse.mdht.uml.hl7.rim-3.0.0.201802220601.jar \ org.eclipse.mdht.uml.hl7.rim \ org.eclipse.mdht.uml.hl7.rim \ 3.0.0.201802220601 ../make-repo.sh ../lib \ org.eclipse.mdht.uml.hl7.vocab-3.0.0.201802220601.jar \ org.eclipse.mdht.uml.hl7.vocab \ org.eclipse.mdht.uml.hl7.vocab \ 3.0.0.201802220601 ../make-repo.sh ../lib \ org.openhealthtools.mdht.uml.cda.consol2-3.0.3.20180222.jar \ org.openhealthtools.mdht.uml.cda.consol2 \ org.openhealthtools.mdht.uml.cda.consol2 \ 3.0.3.20180222 ../make-repo.sh ../lib \ org.openhealthtools.mdht.uml.cda.mu2consol-3.0.3.20180222.jar \ org.openhealthtools.mdht.uml.cda.mu2consol \ org.openhealthtools.mdht.uml.cda.mu2consol \ 3.0.3.20180222
...and the second set:
#!/bin/sh ../make-repo.sh ../lib \ ./org.eclipse.emf.common-2.12.0.v20160420-0247.jar \ org.eclipse.emf.common \ org.eclipse.emf.common \ 2.12.0.v20160420-0247 ../make-repo.sh ../lib \ ./org.eclipse.emf.ecore-2.12.0.v20160420-0247.jar \ org.eclipse.emf.ecore \ org.eclipse.emf.ecore \ 2.12.0.v20160420-0247 ../make-repo.sh ../lib \ ./org.eclipse.emf.ecore.xmi-2.12.0.v20160420-0247.jar \ org.eclipse.emf.ecore.xmi \ org.eclipse.emf.ecore.xmi \ 2.12.0.v20160420-0247 ../make-repo.sh ../lib \ ./org.eclipse.ocl-3.6.0.v20160523-1914.jar \ org.eclipse.ocl \ org.eclipse.ocl \ 3.6.0.v20160523-1914 ../make-repo.sh ../lib \ ./org.eclipse.ocl.common-1.4.0.v20160521-2033.jar \ org.eclipse.ocl.common \ org.eclipse.ocl.common \ 1.4.0.v20160521-2033 ../make-repo.sh ../lib \ ./org.eclipse.ocl.ecore-3.6.0.v20160523-1914.jar \ org.eclipse.ocl.ecore \ org.eclipse.ocl.ecore \ 3.6.0.v20160523-1914 ../make-repo.sh ../lib \ ./org.eclipse.uml2.common-2.1.0.v20170227-0935.jar \ org.eclipse.uml2.common \ org.eclipse.uml2.common \ 2.1.0.v20170227-0935
The following artifacts could not be resolved: org.eclipse.ocl:org.eclipse.ocl:jar:3.6.200.v20170522-1736 org.eclipse.ocl.common:org.eclipse.ocl.common:jar:1.4.200.v20160613-1518 org.eclipse.ocl.ecore:org.eclipse.ocl.ecore:jar:3.6.200.v20170522-1736 org.eclipse.mdht.uml.hl7.vocab:org.eclipse.mdht.uml.hl7.vocab:jar:3.0.0.202201250600: Could not find artifact org.eclipse.ocl:org.eclipse.ocl:jar:3.6.200.v20170522-1736 in MDHT libraries (static) (file:///home/russ/sandboxes/mdht-restlet/lib)
...you likely need to verify that you populated the lib subdirectory with the missing JAR(s). Once that's done, and as Maven remembers past failures, you must "clear Maven's throat" as it were:
$ mvn -U clean package
Then you might find that the list of missing artifacts has diminished some. No one said this wasn't going to be tedious. Tell the supplier of the JARs you're using to get with the program and commit them to Maven Central or some repository somewhere.
I'm unsure of why someone would not use this way instead of the more complex way described above and below. At one point, I was convinced by something I read on stackoverflow.com that there were reasons not to do this, but once I got over it, I've used it quite a bit without consequence.
Simply add your dependency to a subdirectory off the project- (or module-) path like this:
project/ └── libs/ └── cda2fhir-0.2.jar
...then reference it in pom.xml something like this:
<properties> <cda2fhir.version>0.2</cda2fhir.version> </properties> <dependencies> <dependency> <groupId>tr.com.srcdc</groupId> <artifactId>cda2fhir</artifactId> <version>${cda2fhir.version}</version> <scope>system</scope> <systemPath>${project.basedir}/libs/cda2fhir-${cda2fhir.version}.jar</systemPath> </dependency> ...
Here's an article on local, in-project Maven repository as a last resort in a great and enlightening discussion: Adding external/custom JARs into Maven project
This is a simpler exposé than the one just above. It might be less muddy and better explained.
Here's how I went about making of an ad hoc and ancient JAR a participant in a Maven build. I didn't know much at all about this JAR except that it represents Acme's SimpleAPI and I have sample programs using it.
What do I have? File acmesimpleapi.jar which I know to be version 5.2. It contains the package, com.acme.simpleapi. I decided—and it's completely arbitrary—on com.acme as the groupId and acme-simpleapi as artifactId. These are needed in order to reference the JAR inside dependendencies in the pom.xml file as we'll see later on.
In my scratch directory, where I'm going to make this JAR into a proper Maven repository, I have:
russ@nargothrond ~/dev/acmesimpleapi $ ll total 16 drwxrwxr-x 3 russ russ 4096 Aug 21 13:09 . drwxrwxr-x 4 russ russ 4096 Aug 21 12:56 .. drwxrwxr-x 3 russ russ 4096 Aug 21 13:03 lib # empty lib directory -rwxrwxr-x 1 russ russ 291 Aug 21 13:01 make-repo.sh # my working script -rwxrwx--- 1 russ russ 52246 Mar 15 12:03 acmesimpleapi.jar # original JAR
My working script just invokes Maven to do the work of tucking the JAR away into proper Maven-repository garb. I'm annotating it for clarity (so, the green notes are not part of the script):
#!/bin/sh mvn -e install:install-file \ -DlocalRepositoryPath=./lib \ # where to put the results -DcreateChecksum=true \ -Dpackaging=jar \ -Dfile=./acmesimpleapi.jar \ # what the original JAR in hand is called -DgroupId=com.acme \ # what I decided the groupId is going to be -DartifactId=acme-simpleapi \ # what I decided the JAR is going to be called -Dversion=5.2 \ -Dpackaging=jar \ -DgeneratePom=true
I invoke my script. (The first time, lots of stuff gets brought down by Maven into ~/.m2/repository and shows up when you run this script, but I'm not showing that.
russ@nargothrond ~/dev/acmesimpleapi $ ./make-repo.sh Warning: JAVA_HOME environment variable is not set. [INFO] Error stacktraces are turned on. [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Maven Stub Project (No POM) 1 [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- maven-install-plugin:2.5.2:install-file (default-cli) @ standalone-pom --- [INFO] pom.xml not found in acmesimpleapi.jar [INFO] Installing /home/russ/dev/acmesimpleapi/acmesimpleapi.jar to \ /home/russ/dev/acmesimpleapi/lib/com/acme/acme-simpleapi/5.2/acme-simpleapi-5.2.jar [INFO] Installing /tmp/mvninstall3630215891605911268.pom to \ /home/russ/dev/acmesimpleapi/lib/com/acme/acme-simpleapi/5.2/acme-simpleapi-5.2.pom [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.613 s [INFO] Finished at: 2018-08-21T13:19:39-06:00 [INFO] Final Memory: 9M/303M [INFO] ------------------------------------------------------------------------
The result is something I could never have concocted purely by hand and it is Maven-correct:
russ@nargothrond ~/dev/acmesimpleapi $ tree lib lib └── com └── acme └── acme-simpleapi ├── 5.2 │ ├── acme-simpleapi-5.2.jar │ ├── acme-simpleapi-5.2.jar.md5 │ ├── acme-simpleapi-5.2.jar.sha1 │ ├── acme-simpleapi-5.2.pom │ ├── acme-simpleapi-5.2.pom.md5 │ └── acme-simpleapi-5.2.pom.sha1 ├── maven-metadata-local.xml ├── maven-metadata-local.xml.md5 └── maven-metadata-local.xml.sha1 4 directories, 9 files
I move that over into my IntelliJ IDEA project, under a lib subdirectory and it appears (identically to) just as above.
Now I get into the business of telling Maven about it via pom.xml. There are three sections in this file where I need to intervene:
This is the pom.xml file insofar as the SimpleAPI JAR is concerned. I'm using dots where there is surely more content in a real 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>com.acme.fireworks</groupId> <artifactId>untitled</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>jar</packaging> <properties> <!-- #1 above: macro for version, just something I obsess over --> <acme-simpleapi-version>5.2</acme-simpleapi-version> . . . </properties> <repositories> <!-- #2 above: how you tell Maven where the local repository is--> <repository> <id>acme-simpleapi</id> <name>Acme SimpleAPI JAR</name> <url>file://${project.basedir}/lib</url> </repository> . . . </repositories> <dependencies> <!-- #3 above: how you define the dependency so it gets built into project --> <dependency> <groupId>com.acme</groupId> <artifactId>acme-simpleapi</artifactId> <version>${acme-simpleapi-version}</version> </dependency> . . . </dependencies> . . . </project>
This isn't merely relevant to what we've done here, but also to browsing external code of any sort. IntelliJ IDEA keeps all the external contributing components to a project, including JDK- and third-party JARs and also a JAR like the one we've rigged into its own, local repository here, under External Libraries in the Project pane.
The hierarchy and even class files can be browsed. The class files open with code recreated from the .class file. Also, an option to navigate to the real source code, if you have it, is available in the upper right-hand corner of the editor, on the same yellow line as "Decompile .class file, bytecode version:". This option appears as Download Sources and Choose Sources....
If trying to create a static local repository for Maven, you may be doing this as a last resort after trying to get the JAR in from Maven Central or another repository. This left artifacts on the path ~/.m2/repository/... and you now get an error like:
Could not resolve dependencies for project <project-name>: \ <JAR-or-so-name> was not found in file:///home/<user>/<path>/<project>/lib \ during a previous attempt. This failure was cached in the local repository and resolution \ is not reattempted until the update interval of <JAR> library (static) has elapsed or updates are forced
The solution is to go to ~/.m2/repository/... and remove the failed import from Maven Central (or other), then try your build again.
Maven is patiently awaiting the JAR to get filled into your local Maven repository (on path ~/.m2/repository) and won't switch to your static local one (in the project probably in subdirectory lib until you stop Maven from trying to get it from the normal place.
...including scripts. The first one will put the JAR into Nexus. The second will put it to anywhere on the local filesystem.
#!/bin/bash # ------------------------------------------------------------------------ # Deploy JAR artifacts to the Sonatype Nexus artifactory. It's not # practical or recommended to try to do this with curl. # # Notes: # "repositoryId" corresponds to the list of servers in ~/.m2/settings.xml. # There are only two, legitimate repository targets; both are "hosted" # repositories: # # a. maven-snapshots, an internal place for unversioned (SNAPSHOTs) # artifacts. Only artifacts with a version ending in -SNAPSHOT # may go here. # b. maven-releases, an internal place for versioned artifacts. (This # is the default. # # Russell Bateman # May 2019 # ------------------------------------------------------------------------ set -u # (catch uninitialized variables when encountered) BOLD=$(tput bold) PLAIN=$(tput sgr0) TRUE=1 FALSE=0 NEXUS_URL=http://maven.acme.com:8081/repository/maven-releases/ REPOSITORYID=maven-releases POM_FILE= GROUPID= ARTIFACTID= VERSION= JAR_FILE=unknown.jar POM_GENERATED=$FALSE verbose=$TRUE pretend=$FALSE DoHelp() { echo "\ +----------------------------------------------------------+ | Deploy JAR packaging to Sonatype Nexus Maven artifactory | +----------------------------------------------------------+ $0 plus these required and optional arguments: Options: -h display this help blurb -n execute nothing, but show what will be done (dry run) -q suppress all output -u (default: $NEXUS_URL) -r (default: maven-releases) -p (optional, then no -gav) -g (required) -a (required) -v (required) Arguments: path-to-JAR (required) " } GeneratePomXml() { # gotta have, especially, the distributionManagement paragraph cat > pom.xml << 'end' <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>nothing</groupId> <artifactId>nothing</artifactId> <version>nothing</version> <description>Temporary pom.xml to make Maven work.</description> <distributionManagement> <repository> <id>maven-releases</id> <name>maven-releases</name> <url>http://maven.acme.com:8081/repository/maven-releases</url> <layout>default</layout> </repository> <snapshotRepository> <id>maven-snapshots</id> <name>maven-snapshots</name> <url>http://maven.acme.com:8081/repository/maven-snapshots/</url> <layout>default</layout> </snapshotRepository> </distributionManagement> </project> end POM_GENERATED=$TRUE } ErasePomXml() { rm pom.xml } if [ -z "$*" ]; then DoHelp fi set -- `getopt hnqu:p:g:a:v:j: $*` while [ $1 != -- ]; do case $1 in -h) DoHelp $* ; exit 0 ;; -n) pretend=$TRUE ;; -q) verbose=$FALSE ;; -u) NEXUS_URL=$2 ; shift ;; -r) REPOSITORYID=$2 ; shift ;; -p) POM_FILE=$2 ; shift ;; -g) GROUPID=$2 ; shift ;; -a) ARTIFACTID=$2 ; shift ;; -v) VERSION=$2 ; shift ;; *) echo "WARNING: ignoring invalid options ($1)..." ;; esac shift done shift MAVEN=`which mvn` if [ -z "$MAVEN" ]; then echo "Missing Maven--must install" exit 0 fi if [ -z "$*" ]; then echo "Missing JAR filepath" exit 0 fi if [ -z "$NEXUS_URL" ]; then echo "Missing artifactory URL" exit 0 fi if [ -z "$REPOSITORYID" ]; then echo "Missing repositoryId" exit 0 fi if [ -z "$GROUPID" ]; then if [ -n "$POM_FILE" ]; then echo "Using specified custom pom.xml to get groupId" else echo "Missing group id" exit 0 fi fi if [ -z "$ARTIFACTID" ]; then if [ -n "$POM_FILE" ]; then echo "Using specified custom pom.xml to get artifactId" else echo "Missing artifact id" exit 0 fi fi if [ -z "$VERSION" ]; then if [ -n "$POM_FILE" ]; then echo "Using specified custom pom.xml to get version" else echo "Missing artifact version" exit 0 fi fi if [ -z "$JAR_FILE" ]; then echo "Missing artifact (JAR) name" exit 0 fi JAR_FILE="${1:-}" if [ $verbose -eq $TRUE ]; then echo "Deploying $JAR_FILE to Sonatype Nexus repository." fi if [ -n "$POM_FILE" ]; then printf "\ ${BOLD}mvn deploy:deploy-file${PLAIN}\n\ ${BOLD}-Dpackaging=${PLAIN}jar \n\ ${BOLD}-DgeneratePom=${PLAIN}false \n\ ${BOLD}-DpomFile=${PLAIN}$POM_FILE \n\ ${BOLD}-Dfile=${PLAIN}$JAR_FILE \n\ ${BOLD}-DrepositoryId=${PLAIN}$REPOSITORYID \n\ ${BOLD}-Durl=${PLAIN}$NEXUS_URL\n" else printf "\ ${BOLD}mvn deploy:deploy-file${PLAIN}\n\ ${BOLD}-Dpackaging=${PLAIN}jar \n\ ${BOLD}-DgeneratePom=${PLAIN}true \n\ ${BOLD}-DgroupId=${PLAIN}$GROUPID \n\ ${BOLD}-DartifactId=${PLAIN}$ARTIFACTID \n\ ${BOLD}-Dversion=${PLAIN}$VERSION \n\ ${BOLD}-Dfile=${PLAIN}$JAR_FILE \n\ ${BOLD}-DrepositoryId=${PLAIN}$REPOSITORYID \n\ ${BOLD}-Durl=${PLAIN}$NEXUS_URL\n" fi if [ $pretend -ne $TRUE ]; then GeneratePomXml if [ -n "$POM_FILE" ]; then mvn deploy:deploy-file \ -Dpackaging=jar \ -DgeneratePom=false \ -DpomFile=${POM_FILE} \ -Dfile=$JAR_FILE \ -DrepositoryId=$REPOSITORYID \ -Durl=$NEXUS_URL else mvn deploy:deploy-file \ -Dpackaging=jar \ -DgeneratePom=true \ -DgroupId=$GROUPID \ -DartifactId=$ARTIFACTID \ -Dversion=$VERSION \ -Dfile=$JAR_FILE \ -DrepositoryId=$REPOSITORYID \ -Durl=$NEXUS_URL fi if [ $POM_GENERATED -eq $TRUE ]; then ErasePomXml fi fi # vim: set tabstop=2 shiftwidth=2 noexpandtab:
#!/bin/bash # ------------------------------------------------------------------------ # Deploy JAR artifacts to the local filesystem on a specified path. # # Russell Bateman # May 2019 # ------------------------------------------------------------------------ set -u # (catch uninitialized variables when encountered) BOLD=$(tput bold) PLAIN=$(tput sgr0) TRUE=1 FALSE=0 TARGET_PATH= POM_FILE= GROUPID= ARTIFACTID= VERSION= JAR_FILE=unknown.jar verbose=$TRUE pretend=$FALSE DoHelp() { echo "\ +------------------------------------------+ | Deploy JAR packaging to local filesystem | +------------------------------------------+ $0 plus these required and optional arguments: Options: -h display this help blurb -n execute nothing, but show what will be done (dry run) -q suppress all output -u (required) -p (optional, then no -gav) -g (required) -a (required) -v (required) Arguments: path-to-JAR (required) " } if [ -z "$*" ]; then DoHelp fi set -- `getopt hnqu:p:g:a:v:j: $*` while [ $1 != -- ]; do case $1 in -h) DoHelp $* ; exit 0 ;; -n) pretend=$TRUE ;; -q) verbose=$FALSE ;; -u) TARGET_PATH=$2 ; shift ;; -p) POM_FILE=$2 ; shift ;; -g) GROUPID=$2 ; shift ;; -a) ARTIFACTID=$2 ; shift ;; -v) VERSION=$2 ; shift ;; *) echo "WARNING: ignoring invalid options ($1)..." ;; esac shift done shift MAVEN=`which mvn` if [ -z "$MAVEN" ]; then echo "Missing Maven--must install" exit 0 fi if [ -z "$*" ]; then echo "Missing JAR filepath" exit 0 fi if [ -z "$TARGET_PATH" ]; then echo "Missing local-repository path" exit 0 fi if [ -z "$GROUPID" ]; then if [ -n "$POM_FILE" ]; then echo "Using specified custom pom.xml to get groupId" else echo "Missing group id" exit 0 fi fi if [ -z "$ARTIFACTID" ]; then if [ -n "$POM_FILE" ]; then echo "Using specified custom pom.xml to get artifactId" else echo "Missing artifact id" exit 0 fi fi if [ -z "$VERSION" ]; then if [ -n "$POM_FILE" ]; then echo "Using specified custom pom.xml to get version" else echo "Missing artifact version" exit 0 fi fi if [ -z "$JAR_FILE" ]; then echo "Missing artifact (JAR) name" exit 0 fi JAR_FILE="${1:-}" if [ $verbose -eq $TRUE ]; then echo "Deploying $JAR_FILE to Sonatype Nexus repository." fi if [ -n "$POM_FILE" ]; then printf "\ ${BOLD}mvn install:install-file${PLAIN}\n\ ${BOLD}-Dpackaging=${PLAIN}jar \n\ ${BOLD}-DgeneratePom=${PLAIN}false \n\ ${BOLD}-DcreateChecksum=${PLAIN}true \n\ ${BOLD}-Dfile=${PLAIN}$JAR_FILE \n\ ${BOLD}-DlocalRepositoryPath=${PLAIN}$TARGET_PATH\n" else printf "\ ${BOLD}mvn install:install-file${PLAIN}\n\ ${BOLD}-Dpackaging=${PLAIN}jar \n\ ${BOLD}-DgeneratePom=${PLAIN}true \n\ ${BOLD}-DcreateChecksum=${PLAIN}true \n\ ${BOLD}-DgroupId=${PLAIN}$GROUPID \n\ ${BOLD}-DartifactId=${PLAIN}$ARTIFACTID \n\ ${BOLD}-Dversion=${PLAIN}$VERSION \n\ ${BOLD}-Dfile=${PLAIN}$JAR_FILE \n\ ${BOLD}-DlocalRepositoryPath=${PLAIN}$TARGET_PATH\n" fi if [ $pretend -ne $TRUE ]; then if [ -n "$POM_FILE" ]; then mvn install:install-file \ -Dpackaging=jar \ -DgeneratePom=false \ -DcreateChecksum=true \ -Dfile=$JAR_FILE \ -DlocalRepositoryPath=$TARGET_PATH else mvn install:install-file \ -Dpackaging=jar \ -DgeneratePom=true \ -DcreateChecksum=true \ -DgroupId=$GROUPID \ -DartifactId=$ARTIFACTID \ -Dversion=$VERSION \ -Dfile=$JAR_FILE \ -DlocalRepositoryPath=$TARGET_PATH fi fi # vim: set tabstop=2 shiftwidth=2 noexpandtab:
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
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:
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/dev/<some Maven repository path> was cached in the local repository, resolution will not be reattempted until the update interval of base-library 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
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] Etretat Logiciels 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] el-nifi-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.etretatlogiciels.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/dev/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!
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>
Who can say why Maven implementors steadfastly refuse to make it easy to list out the value of properties?
Put this as its own target in the <build> paragraph of your pom.xml. Do not put this under <pluginManagement ../>, but merely under <plugins .../> or it will not work:
<build> <plugins> . . . <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.8</version> <executions> <execution> <phase>compile</phase> <configuration> <target> <echoproperties /> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> . . . </plugins> </build>
Next, when you run Maven, do this:
$ mvn clean compile | grep echoproperties | awk '{print $2}'
This will list out the properties, something really huge and unmanageable like this (I'm showing just a few here) because many lines are duplicates (depending on the complexity, number of submodules, etc. of your project):
ant.java.version=1.8 ant.project.default-target=main ant.project.name=maven-antrun- ant.version=Apache awt.toolkit=sun.awt.X11.XToolkit basedir=/home/russ/dev/nifi-pipeline.v74_release.dev/code/nifi-pipeline cassandra-unit.version=3.1.3.2 cassandra.version=3.9 java.vendor=Oracle java.vendor.url=http\://java.oracle.com/ java.version=1.8.0_112 line.separator=\n maven.compiler.plugin.version=3.5.1 maven.compiler.source=1.8 maven.compiler.target=1.8 os.arch=amd64 os.name=Linux os.version=4.8.0-53-generic path.separator=\: junit.version=4.12 user.country=US user.home=/home/russ user.language=en user.name=russ user.timezone=America/Denver
You can eliminate duplication and also sort what comes out by adding sort -u:
$ mvn clean compile | grep echoproperties | awk '{print $2}' | sort -u
To look for just one property, do this:
$ mvn clean compile | grep echoproperties | awk '{print $2}' | sort -u | grep property-name
Building using mvn clean -U package, I have been getting:
~/dev/mdht-restlet $ mvn clean -U package [INFO] Scanning for projects... [ERROR] [ERROR] Some problems were encountered while processing the POMs: [ERROR] 'dependencies.dependency.version' for lpg.runtime.java:lpg.runtime.java:jar is missing. @ line 49, column 17 [ERROR] 'dependencies.dependency.version' for org.eclipse.emf.ecore.xmi:org.eclipse.emf.ecore.xmi:jar is missing. @ line 53, column 17 [ERROR] 'dependencies.dependency.version' for org.eclipse.emf.ecore:org.eclipse.emf.ecore:jar is missing. @ line 57, column 17 [ERROR] 'dependencies.dependency.version' for org.eclipse.emf.common:org.eclipse.emf.common:jar is missing. @ line 61, column 17 [ERROR] 'dependencies.dependency.version' for org.eclipse.ocl:org.eclipse.ocl:jar is missing. @ line 65, column 17 [ERROR] 'dependencies.dependency.version' for org.eclipse.ocl.common:org.eclipse.ocl.common:jar is missing. @ line 69, column 17 [ERROR] 'dependencies.dependency.version' for org.eclipse.ocl.ecore:org.eclipse.ocl.ecore:jar is missing. @ line 73, column 17 [ERROR] 'dependencies.dependency.version' for org.eclipse.uml2.types:org.eclipse.uml2.types:jar is missing. @ line 77, column 17 [ERROR] 'dependencies.dependency.version' for org.eclipse.mdht:org.eclipse.mdht.emf.runtime:jar is missing. @ line 82, column 17 [WARNING] 'dependencies.dependency.scope' for org.eclipse.mdht:org.eclipse.mdht.cda-runtime:pom must be one of [provided, compile, runtime, test, system] but is 'import'. @ line 92, column 14 [ERROR] 'dependencies.dependency.version' for org.eclipse.mdht:org.eclipse.mdht.uml.hl7.vocab:jar is missing. @ line 94, column 17 [ERROR] 'dependencies.dependency.version' for org.eclipse.mdht:org.eclipse.mdht.uml.hl7.datatypes:jar is missing. @ line 98, column 17 [ERROR] 'dependencies.dependency.version' for org.eclipse.mdht:org.eclipse.mdht.uml.hl7.rim:jar is missing. @ line 102, column 17 [ERROR] 'dependencies.dependency.version' for org.eclipse.mdht:org.eclipse.mdht.uml.cda:jar is missing. @ line 106, column 17 [ERROR] 'dependencies.dependency.version' for org.openhealthtools.mdht.cda:org.openhealthtools.mdht.uml.cda.consol2:jar is missing. @ line 111, column 17
It seems to want version numbers. In pom.xml, I had no version numbers:
<dependencies> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-bundle</artifactId> <version>1.19.1</version> </dependency> <!-- MDHT third-party dependencies --> <dependency> <groupId>lpg.runtime.java</groupId> <artifactId>lpg.runtime.java</artifactId> </dependency> <dependency> <groupId>org.eclipse.emf.ecore.xmi</groupId> <artifactId>org.eclipse.emf.ecore.xmi</artifactId> </dependency> . . . <!-- MDHT Core dependencies --> <dependency> <groupId>org.eclipse.mdht</groupId> <artifactId>org.eclipse.mdht.emf.runtime</artifactId> </dependency> <!-- MDHT CDA dependencies --> <dependency> <groupId>org.eclipse.mdht</groupId> <artifactId>org.eclipse.mdht.cda-runtime</artifactId> <version>3.0.0-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.eclipse.mdht</groupId> <artifactId>org.eclipse.mdht.uml.hl7.vocab</artifactId> </dependency> . . . <!-- Various other dependencies --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> </dependencies>
Now, looking carefully again at pom.xml in a working sample project, I see some special, dependency-management sauce:
<dependencyManagement> <dependencies> <dependency> <groupId>org.eclipse.mdht</groupId> <artifactId>org.eclipse.mdht.cda-runtime</artifactId> <version>3.0.0-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> . . .
...where org.eclipse.mdht.cda-runtime does not appear in the simple dependencies list. Restoring this combination to mdht-restlet, things start to work. Now, just what is going on here? There is the presence of <dependencyManagement> and <scope>. I'm not certain how to describe what I've accomplished by this.
In the maven-war-plugin <configuration>, add these lines. The highlighted line will lead to a Build-Time: timestamp.
<!-- Magic for maintaining a build timestamp in MANIFEST.MF: --> <archive> <manifest> <addDefaultImplementationEntries>true</addDefaultImplementationEntries> <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries> </manifest> <manifestEntries> <Build-Time>${maven.build.timestamp}</Build-Time> </manifestEntries> </archive>
Here's the resulting mdht-restlet.war/META-INF/MANIFEST.MF file:
Manifest-Version: 1.0 Implementation-Title: mdht-restlet Implementation-Version: 1.0.0-SNAPSHOT Built-By: russ Specification-Title: mdht-restlet Implementation-Vendor-Id: com.acme.mdht Build-Time: 2018-06-29T20:08:22Z Created-By: Apache Maven 3.3.9 Build-Jdk: 1.8.0_144 Specification-Version: 1.0
Now, in order to communicate this timestamp at runtime, you use this code to read the manifest file:
private String getBuildTimestamp( ServletContext servletContext ) { try { Manifest manifest = new Manifest( servletContext.getResourceAsStream( "/META-INF/MANIFEST.MF" ) ); Attributes attributes = manifest.getMainAttributes(); return attributes.getValue( "Build-Time" ); } catch( IOException ex ) { ... } }
See note just below.
This usually happens when using Maven to manage hierarchical projects with multiple submodules (IntelliJ IDEA parlance) or [sub]projects (Eclipse parlance).
There are a number of things to look for here, the most important being the exact relationship between the "parent" and "child" (or subdirectory) pom.xml files. The elements of this cooperative synchronization are (in this NiFi archive example):
<groupId>com.windofkeltia.nifi.processor</groupId> <artifactId>my-nifi-processor</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging>
<artifactId>my-processor</artifactId> <packaging>jar</packaging> <parent> <groupId>com.windofkeltia.nifi.processor</groupId> <artifactId>my-nifi-processor</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent>
<artifactId>my-nar</artifactId> <packaging>nar</packaging> <parent> <groupId>com.windofkeltia.nifi.processor</groupId> <artifactId>my-nifi-processor</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent>
In this example, a NiFi archive (.nar) is built from one or more .jar files containing the processor code as governed by Maven through the root pom.xml file (cf. the packaging specifications).
Assume a multimodule project structure like this one. It's to build a custom NiFi processor (.nar) and then put it into an RPM for distribution (.rpm).
project ├── nar │ └── pom.xml ├── pom.xml └── processor ├── pom.xml └── src
It's important not to give conflicting artifactIds to these modules. For example, the above could be named (should be named something like):
Here are some essentials (not full POMs) in the pom.xml files:
Note properties for entire project (because root), module list and build statements.
<groupId>com.company.project</groupId> <artifactId>project</artifactId> <version>1.0.0</version> <packaging>pom</packaging> <description>Project for custom processor</description> <url>http://company.com</url> <modules> <module>processor</module> <module>nar</module> </modules> . . . <properties> <nifi-nar-maven-plugin.version>1.3.1</nifi-nar-maven-plugin.version> <rpm-maven-plugin.version>2.2.0</rpm-maven-plugin.version> <versions-maven-plugin.version>2.7</versions-maven-plugin.version> </properties> . . . <build> <plugins> <plugin> <!-- What builds the NAR file... --> <groupId>org.apache.nifi</groupId> <artifactId>nifi-nar-maven-plugin</artifactId> <version>${nifi-nar-maven-plugin.version}</version> <extensions>true</extensions> <executions> <execution> <id>default-nar</id> <phase>package</phase> <goals> <goal>nar</goal> </goals> </execution> </executions> </plugin> </plugins> <pluginManagement> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>rpm-maven-plugin</artifactId> <version>${rpm-maven-plugin.version}</version> <executions> <execution> <id>generate-rpm</id> <phase>package</phase> <goals> <goal>rpm</goal> </goals> </execution> </executions> <configuration> <name>(whatever—includes an artifactId)</name> <version>(version)</version> <release>(release)</release> <group>Application/System</group> <vendor>Company name</vendor> <license>Proprietary</license> <url>https://company.com</url> <summary>(what this custom NiFi processor does)</summary> <mappings> <mapping> <directory>/opt/nifi/lib</directory> <directoryIncluded>false</directoryIncluded> <filemode>444</filemode> <username>root</username> <groupname>root</groupname> <sources> <source> <location>${project.build.directory}</location> <includes> <include>*.nar</include> </includes> </source> </sources> </mapping> </mappings> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>versions-maven-plugin</artifactId> <version>${versions-maven-plugin.version}</version> <configuration> <excludes> <exclude>org.apache.commons:commons-collections4</exclude> </excludes> </configuration> </plugin> </plugins> </pluginManagement> </build>
This builds the NAR and also the RPM.
<artifactId>project-nar</artifactId> <version>1.0.0</version> <packaging>nar</packaging> . . . <parent> <groupId>com.company.project</groupId> <artifactId>project</artifactId> <version>1.0.0</version> <relativePath>../pom.xml</relativePath> </parent> . . . <properties> <code.root>../..</code.root> <maven.javadoc.skip>true</maven.javadoc.skip> <source.skip>true</source.skip> </properties> . . . <dependencies> <dependency> <groupId>com.company.project</groupId> <artifactId>processor</artifactId> <version>${project.version}</version> </dependency> </dependencies> . . . <build> <plugins> <!-- Bundle this all up in an RPM... --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>rpm-maven-plugin</artifactId> <version>${rpm-maven-plugin.version}</version> <executions> <execution> <id>generate-rpm</id> <goals> <goal>rpm</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
<artifactId>processor</artifactId> <packaging>jar</packaging> . . . <parent> <groupId>com.windofkeltia.leapyear</groupId> <artifactId>project</artifactId> <version>1.0.0</version> <relativePath>../pom.xml</relativePath> </parent> . . . <properties> <code.root>../..</code.root> </properties>
When you run, you wonder what, for example, ${project.build.directory} or similar macros evaluate to. How to see even an ugly display of their values when Maven is running? (Refer to the project shown just above for an understanding of the multiple modules you see here.)
A solution is to add the following paragraph as a plugin to the build section of pom.xml. It's <echoproperties /> that does it.
<plugin> <!-- How to see properties (values for macros, variables; whatever you wish to call them)... --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>${maven-antrun-plugin.version}</version> <executions> <execution> <phase>validate</phase> <goals> <goal>run</goal> </goals> <configuration> <tasks> <echoproperties /> </tasks> </configuration> </execution> </executions> </plugin>
Here's a taste of what it looks like:
russ@nargothrond ~/dev/project $ mvn validate [INFO] Scanning for projects... [INFO] ------------------------------------------------------------------------ [INFO] Reactor Build Order: [INFO] [INFO] project [pom] [INFO] processor [jar] [INFO] project-nar [nar] [INFO] [INFO] ----------------< com.windofkeltia.project >---------------------------- [INFO] Building project 1.0.0 [1/3] [INFO] --------------------------------[ pom ]--------------------------------- [INFO] [INFO] --- maven-antrun-plugin:1.8:run (default) @ project --- [WARNING] Parameter tasks is deprecated, use target instead [INFO] Executing tasks main: [echoproperties] #Ant properties [echoproperties] #Wed Feb 19 10:03:24 MST 2020 [echoproperties] ant.core.lib=/home/russ/.m2/repository/org/apache/ant/ant/1.9.4/ant-1.9.4.jar [echoproperties] ant.file=/home/russ/dev/project/pom.xml [echoproperties] ant.file.maven-antrun-=/home/russ/dev/project/target/antrun/build-main.xml [echoproperties] ant.file.type.maven-antrun-=file [echoproperties] ant.java.version=1.8 [echoproperties] ant.project.default-target=main [echoproperties] ant.project.name=maven-antrun- [echoproperties] ant.version=Apache Ant(TM) version 1.9.4 compiled on April 29 2014 [echoproperties] asm\:asm\:jar=/home/russ/.m2/repository/asm/asm/3.3.1/asm-3.3.1.jar [echoproperties] awt.toolkit=sun.awt.X11.XToolkit [echoproperties] basedir=/home/russ/dev/project . . . [echoproperties] os.arch=amd64 [echoproperties] os.name=Linux [echoproperties] os.version=4.15.0-74-generic [echoproperties] path.separator=\: [echoproperties] project.artifactId=project [echoproperties] project.build.directory=/home/russ/dev/project/target . . . [echoproperties] targeted.build.arch=linux_x86-64 [echoproperties] user.country=US [echoproperties] user.dir=/home/russ/dev/project [echoproperties] user.home=/home/russ [echoproperties] user.language=en [echoproperties] user.name=russ [echoproperties] user.timezone=America/Denver [echoproperties] versions-maven-plugin.version=2.7 [INFO] Executed tasks [INFO] [INFO] ----------------< com.windofkeltia.project >---------------------------- [INFO] Building processor 1.0.0 [2/3] [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-antrun-plugin:1.8:run (default) @ processor --- [WARNING] Parameter tasks is deprecated, use target instead [INFO] Executing tasks . . .
For example, you could do this:
russ@nargothrond ~/dev/project $ mvn validate [echoproperties] project.build.directory=/home/russ/sandboxes/project/target [echoproperties] project.build.directory=/home/russ/sandboxes/project/processor/target
A shorter version that doesn't run everything else is:
<!-- How to see properties (values for macros, variables... --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.8</version> <executions> <execution> <id>get-properties</id> <phase>validate</phase> <goals> <goal>run</goal> </goals> <configuration> <tasks> <!-- show values of macros/variables... --> <echoproperties/> </tasks> </configuration> </execution> </executions> </plugin> $ mvn antrun:run@get-properties
project ├── pom.xml ├── application │ └── pom.xml └── shared └── pom.xml
Googling around this morning, I finally solved a problem that has plagued my multimodule projects forever: I need to develop and maintain my eternal TestUtilities class in one place (and project), for example,
project/shared/src/test/java/com/windofkeltia/utilities/TestUtilities.java
Yet, I need to be able to consume it from tests written not only in the shared project, but others as well, let's say application,
project/application/src/test/java/com/windofkeltia/SomeTest.java
application's production code already depends upon shared's production code, but, until this solution, there's been no way for appliction's test code to depend upon shared's test code.
Here's how to remedy the problem. This is real—not a hack.
First, add the following lines to share's pom.xml. Under <build>, find the plug-in named, maven-jar-plugin, because we're going to force the generation of a JAR. Here's the whole maven-jar-plugin after editing. Note: you will still only have a single instance of maven-jar-plugin after this edit, which only adds an execution clause to what other execution clauses are alread there (highlighted here):
<modelVersion>4.0.0</modelVersion> <artifactId>shared</artifactId> <packaging>jar</packaging> <parent> <groupId>com.windofkeltia</groupId> <artifactId>project</artifactId> <version>1.0</version> <relativePath>../pom.xml</relativePath> </parent> . . . <build> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>2.3.1</version> <executions> <execution> <id>Generate a test JAR for sharing test utilities</id> <phase>package</phase> <goals> <goal>test-jar</goal> </goals> </execution> </executions> </plugin> ... </build> </project>
Next, let's tackle what is a sibling module to shared in the greater project, named application. This module's test code must access the code from shared's aspect. We'll add, beside the dependency for shared's production code (first dependency, likely already there), another for its test code. Add the highlighted lines to application's pom.xml dependencies:
<modelVersion>4.0.0</modelVersion> <artifactId>application</artifactId> <packaging>jar</packaging> <parent> <groupId>com.windofkeltia</groupId> <artifactId>project</artifactId> <version>1.0</version> <relativePath>../pom.xml</relativePath> </parent> . . . <dependencies> <dependency> <groupId>com.windofkeltia.project</groupId> <artifactId>shared</artifactId> <version>${shared.version}</version> </dependency> <dependency> <groupId>com.windofkeltia.project</groupId> <artifactId>shared</artifactId> <version>${shared.version}</version> <type>test-jar</type> <scope>test</scope> </dependency> ... </dependencies> ... </build> </project>
From this point on, test code in application should be able to access the TestUtilities class I have in shared from application's test code.
A note on the groupId above: in this project, all the submodules inherit the parent's (project) groupId, com.windofkeltia. Not all multimodule projects may be built so; mine tend to be. (Your mileage may vary.)
This can be done. In IntelliJ IDEA, right-click the module in the Project pane and choose Refactor → Rename, then click Rename module and directory.
It's probably best not to allow the IDE to look for occurrences in comments and strings because it will only mess up coincidental conflicts. It's better to detect and fix these by hand.
Open the newly renamed modules's pom.xml and fix its <artifactId>.
Open the root-level pom.xml and hand-fix the name of the <module> you renamed.
Think of any other Maven references to the newly renamed module and fix them.
Rebuild doing this to force Maven to think harder about repository implications:
$ mvn clean -U package
Profiles are defined in pom.xml. Triggering a profile during a build (Maven invocation) is done...
$ mvn -P build-jar,build-app
<settings> <activeProfiles> <activeProfile>build-jar</activeProfile> </activeProfiles> </settings>
$ export ENVIRONMENT="debug"
<profiles> <profile> <activation> <property> <name>env.ENVIRONMENT</name> <value>debug</value> </property> </activation> </profile> </profiles>
or
$ mvn -DENVIRONMENT=debug
<profiles> <profile> <activation> <property> <name>ENVIRONMENT</name> <value>debug</value> </property> </activation> </profile> </profiles>
linux unix amd64 5.4.0-73 ...
<profiles> <profile> <activation> <file> <missing>target/my-jar.jar</missing> </file> </activation> </profile> </profiles>
Java is 25 years old. This process has been done 9 ways from Sunday over that time. Most of the examples you'll find are confusing, outdated or will simply and mysteriously not work. In 2020, using the latest versions, here is what I am doing that works. I hope when you try this that it will just work for you.
What's missing from this tutorial? I pretend that MANIFEST.mf is of no value and I don't document it fully nor how to use that instead of the maven-assembly-plugin configuration that dispenses with it. I will give you some tips, however.
First, let's get some fundamentals out of the way:
~/dev/project $ mvn clean package
<modelVersion>4.0.0</modelVersion> <groupId>com.windofkeltia</groupId> <artifactId>project</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>jar</packaging>
<properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <properties>
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.2:compile \ (default-compile) on project mds-extract: Compilation failure: Compilation failure: [ERROR] Source option 5 is no longer supported. Use 6 or later. [ERROR] Target option 1.5 is no longer supported. Use 1.6 or later.
<build> <sourceDirectory>src/main/java</sourceDirectory> <testSourceDirectory>src/test/java</testSourceDirectory> <resources> <resource> <directory>src</directory> <excludes> <exclude>**/*.java</exclude> </excludes> </resource> </resources> ...
Main-Class: com.windofkeltia.ProjectMain (all kinds of other labels and values; IntelliJ IDEA will help you) (make certain you have a blank line at the end of this file)
My purpose was to reduce a real, working executable JAR from the smallest possible amount of code and its build file. This is the bare minimum; the result is a JAR that you can execute. Let's start with some context so that you know where everything starts and the JAR lands:
~/dev/project $ ll drwxrwxr-x 5 russ russ 4096 Oct 22 18:55 . drwxrwxr-x 52 russ russ 4096 Oct 22 15:34 .. drwxrwxr-x 4 russ russ 4096 Oct 22 18:55 .idea (IntelliJ IDEA file) -rw-rw-r-- 1 russ russ 1389 Oct 22 17:33 project.iml (IntelliJ IDEA file) -rw-r--r-- 1 russ russ 1954 Oct 22 18:55 pom.xml drwxrwxr-x 4 russ russ 4096 Oct 22 15:35 src drwxrwxr-x 9 russ russ 4096 Oct 22 18:49 target ~/dev/project $ ll ./target total 48 drwxrwxr-x 9 russ russ 4096 Oct 22 18:49 . drwxrwxr-x 5 russ russ 4096 Oct 22 18:55 .. drwxrwxr-x 3 russ russ 4096 Oct 22 18:49 classes drwxrwxr-x 3 russ russ 4096 Oct 22 18:49 generated-sources drwxrwxr-x 3 russ russ 4096 Oct 22 18:49 generated-test-sources drwxrwxr-x 2 russ russ 4096 Oct 22 18:49 maven-archiver drwxrwxr-x 3 russ russ 4096 Oct 22 18:49 maven-status -rw-rw-r-- 1 russ russ 11357 Oct 22 18:49 project-1.0.0-SNAPSHOT.jar drwxrwxr-x 2 russ russ 4096 Oct 22 18:49 surefire-reports drwxrwxr-x 4 russ russ 4096 Oct 22 18:49 test-classes ~/dev/project $ which java /usr/bin/java ~/dev/project $ java -jar ./target/project-1.0.0-SNAPSHOT.jar arguments
This is your production (as opposed to test-) class that contains public void main( String[] args ):
package com.windofkeltia; public class ProjectMain { public static void main( String[] args ) { String pathname = null; if( args.length > 0 ) pathname = args[ 0 ]; if( isNull( pathname ) ) { System.err.println( "No document was specified as input." ); return; } // whatever else your application wants to do: this tutorial isn't about that } }
You'll write a JUnit test suite for your application in files like this:
package com.windofkeltia; public class ProjectMainTest { @Test public void test() { String[] args = { "this", "is", "a", "test" ); ProjectMain.main( args ); } }
If I neglect to pass a first argument, which I don't in the unit test, but let's pretend that, from the command line, I do forget, I will get something on stderr (see code in main()):
~/dev/project $ java -jar ./target/project-1.0.0-SNAPSHOT.jar arguments No document specified as input.
(I think I have been thorough enough so that the idea of a project with a Java main() that does something at the command line is no longer confusing. And, I resisted using the greeting, "Hello World," anywhere. Doh! Except just now.)
Here's the Maven build file. You only need the maven-assembly-plugin to produce the executable JAR including to ensure third-party dependencies get linked into the final JAR. This Maven file is the rock-bottom simplest which will produce your application as an executable JAR. Telling Maven what the class that contains main() is of crucial importance.
<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.windofkeltia</groupId> <artifactId>project</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>jar</packaging> <properties> <junit.version>4.12</junit.version> <maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <!-- other dependencies as you need: this tutorial isn't about those --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <!-- Ensure third-party JARs get into the executable JAR --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>${maven-assembly-plugin.version}</version> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> <configuration> <archive> <manifest> <mainClass>com.windofkeltia.ProjectMain</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <appendAssemblyId>false</appendAssemblyId> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
Nota bene: If you do not include <appendAssemblyId>false</appendAssemblyId> in the configuration of maven-assembly-plugin, then you'll get two JARs:
The second one, much bigger, will contain whatever third-party JARs you've included in the creation of your application. You probably do not need both; this configuration setting will leave you with the second one, but under the first one's name.
This isn't really possible, but it's possible to change the name of the artifact through the <finalName> element under the pom.xml's <build> paragraph:
<project> ... <artifactId>foo</artifactId> <version>1.0.0</version> <packaging>jar</packaging> ... <build> ... <finalName>${project.artifactId}</finalName> ... </build> </project>
The result will be foo.jar instead of foo-1.0.0.jar.
It's possible to add an artifact, in this case one I have build from my own source code, into the local, Maven repository (at ~/.m2/repository). It's easier than you might think.
All that must be done is to build using
$ mvn install
This results in the JAR finding its way to the local, Maven repository.
<build> ... <plugins> ... <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>${maven-source-plugin.version}</version> <executions> <execution> <id>attach-sources</id> <goals> <goal>jar</goal> </goals> </execution> </executions> </plugin> ...
The result is something like this:
$ ll target drwxrwxr-x 12 russ russ 4096 Jun 7 15:30 . drwxrwxr-x 5 russ russ 4096 Jun 7 15:30 .. drwxrwxr-x 2 russ russ 4096 Jun 7 15:30 antrun drwxrwxr-x 5 russ russ 4096 Jun 7 15:30 apidocs drwxrwxr-x 3 russ russ 4096 Jun 7 15:30 classes drwxrwxr-x 3 russ russ 4096 Jun 7 15:30 generated-sources drwxrwxr-x 3 russ russ 4096 Jun 7 15:30 generated-test-sources -rw-rw-r-- 1 russ russ 154945 Jun 7 15:30 abc-1.0.0.jar -rw-rw-r-- 1 russ russ 110877 Jun 7 15:30 abc-1.0.0-sources.jar drwxrwxr-x 2 russ russ 4096 Jun 7 15:30 javadoc-bundle-options drwxrwxr-x 2 russ russ 4096 Jun 7 15:30 maven-archiver -rw-rw-r-- 1 russ russ 3672 Jun 7 15:30 maven-javadoc-plugin-stale-data.txt drwxrwxr-x 3 russ russ 4096 Jun 7 15:30 maven-status drwxrwxr-x 2 russ russ 4096 Jun 7 15:30 surefire-reports drwxrwxr-x 4 russ russ 4096 Jun 7 15:30 test-classes
...and the install command will result in the JAR reaching ~/.m2/repository alongside the normal JAR.
Add this to the <build> paragraph of pom.xml. If your Javadoc is crap or doesn't meet latest HTML standards and you get errors, you can shut them up using the highlighted line below. (The name of this varies slightly depending on the version of maven-javadoc-plugin.) In so far as Javadoc display, your mileage may vary by doing this.
<build> ... <plugins> ... <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> <version>${maven-javadoc-plugin.version}</version> <executions> <execution> <id>attach-javadocs</id> <goals> <goal>jar</goal> </goals> <configuration> <doclint>none</doclint> </configuration> </execution> </executions> </plugin> ...
$ ll target drwxrwxr-x 12 russ russ 4096 Jun 7 15:30 . drwxrwxr-x 5 russ russ 4096 Jun 7 15:30 .. drwxrwxr-x 2 russ russ 4096 Jun 7 15:30 antrun drwxrwxr-x 5 russ russ 4096 Jun 7 15:30 apidocs drwxrwxr-x 3 russ russ 4096 Jun 7 15:30 classes drwxrwxr-x 3 russ russ 4096 Jun 7 15:30 generated-sources drwxrwxr-x 3 russ russ 4096 Jun 7 15:30 generated-test-sources -rw-rw-r-- 1 russ russ 154945 Jun 7 15:30 abc-1.0.0.jar -rw-rw-r-- 1 russ russ 920963 Jun 7 15:30 abc-1.0.0-javadoc.jar drwxrwxr-x 2 russ russ 4096 Jun 7 15:30 javadoc-bundle-options drwxrwxr-x 2 russ russ 4096 Jun 7 15:30 maven-archiver -rw-rw-r-- 1 russ russ 3672 Jun 7 15:30 maven-javadoc-plugin-stale-data.txt drwxrwxr-x 3 russ russ 4096 Jun 7 15:30 maven-status drwxrwxr-x 2 russ russ 4096 Jun 7 15:30 surefire-reports drwxrwxr-x 4 russ russ 4096 Jun 7 15:30 test-classes
When you build
The artifact built is "installed" into ~/.m2/repository (the local repository). However, from then on, changes to code will never reach the local repositor unless...
$ rm -rf ~/.m2/repository/path to parent of artifact
Surely there is a more elegant way, but I have searched yet never found it.
You may wish to version your product as major.minor.build or major.minor.revision.build, etc. For instance, perhaps you wish to create WAR files whose naming follows the pattern:
web-application##major.minor.revision-build.war
Such as (for example) my-app##3.3.4-109.war. See Naming the WAR file in Maven.
The solution isn't the most elegant approach to this (some will tell you to adopt Gradle). However, I have used this solution, first in ant (see How to manage buildnumber), then in Maven (what you're about to see). This solution's lately been touched (version 3.0.0, December 2021) and is named buildnumber-maven-plugin from Code Haus. I used it with ant over a decade ago.
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>buildnumber-maven-plugin</artifactId> <version>3.0.0</version> <executions> <execution> <id>buildnumber</id> <phase>test</phase> <!-- See note below. --> <goals> <goal>create</goal> </goals> </execution> </executions> <configuration> <format>{0,number}</format> <items> <item>buildNumber</item> </items> <doCheck>false</doCheck> <doUpdate>false</doUpdate> <revisionOnScmFailure>true</revisionOnScmFailure> </configuration> </plugin>
<scm> <connection> scm:svn:http://127.0.0.1/dummy </connection> <developerConnection> scm:svn:https://127.0.0.1/dummy </developerConnection> <tag>HEAD</tag> <url> http://127.0.0.1/dummy </url> </scm>
Higher up in pom.xml, you should have version defined thus:
<version>3.3.4-${buildNumber}</version>
Notice that I do not ask the plug-in to mess with my major, minor and revision versions. I don't even know if that's something the plug-in will do for me (see the plug-in's <configuration ... />); I don't care to have it do that anyway.
The way the auto-incrementation works is very straightforward. It requires a file, buildNumber.properties in the same subdirectory as pom.xml. After the fifth invocation of Maven, this file contains:
$ mvn clean package . . . $ cat buildNumber.properties #maven.buildNumber.plugin properties file #Wed Dec 08 09:43:44 MST 2021 buildNumber=5
It follows, therefore, that if you wish to back up to an earlier build number, all you need do is modify the third line.
I have found that the build number in ./buildNumber.properties gets incremented even when unit tests fail if phase validate is specified. If one or more unit tests fail, I do not want the build number incremented since my next working build should be the same number as the previously deployed build plus 1 arithmentically.
Most googling on this issue stumbles upon pom.xml examples that usually show
<phase>validate</phase>
used. This is not what I want.
Since, on a very bad day, it could take me several dozen invocations of Maven before I get confirmation that all the errors are fixed, I don't want build number auto-incremented each time, but only after there are no more errors.
Even then, I might have to adjust the build number by editing ./buildNumber.properties, but asking the plug-in to way until after successfully running the JUnit tests usually works as I want it to.
Each Maven life cycle consists of a sequence of phases. For instance, the default build life cycle, which is Maven's main build life cycle, consists of 23 phases.
The clean life cycle consists of 3 phases, the site lifecycle of 4 phases.
Here are the 3, built-in life cycles:
I believe that the name of the life cycle never appears on the Maven command line.
Or, almost never. It's unclear to me whether clean is (also) a goal or only a life cycle. (I think it's both and, when used on the command line, indicates a goal.)
The life cycle consistently appears, however, in plug-in configuration—usually in <executions>, something like this:
<executions> <execution> <id>default</id> <goals> <goal>build</goal> </goals> </execution> </executions>
$ mvn phase
..., for instance, will cause the requested phase to run, but only after the other phases on which it is dependent have been run. For example, deploy, the last phase of the default build life cycle, will entail the entire default life cycle's phases to be run.
The phase appears most frequently on the Maven command line.
Goals are made up by plug-in writers. Plug-in configuration is correspondingly confusing and chaotic using <phase>, <goal>, <id> and other terminology, often in error. For example, Code Haus authors think build a phase while Spotify developers think it's goal.
Each phase consists of a sequence of one or more goals, each of which is responsible for a specific task. When a phase is run (as deploy in the example above), all goals bound to this phase are executed in a prescribed (and natural) order.
For instance, compiler:compile. The compile goal from a compiler plug-in like org.apache.maven.plugins.maven-compiler-plugin, is bound to the compile phase.
The test goal, of surefire:test, may be bound to the test phase. Note that phases and goals may be named identically. It's up to your skill and familiarity with Maven to discern between them (yeah, to hell with that, right?).
The jar goal, of jar:jar, or the war goal of war:war, is bound to the package phase.
The goal appears occasionally on the Maven command line, usually after a plug-in name and separated from it by a colon (:).
...allow for the specification of (a group of) goals. Confusingly (and importantly), these goals aren't always bound to the same phase. This is probably due to there never being, in the mind of the plug-in's author perhaps, any ambiguity because of the plug-in's obvious semantics. Here's a simple configuration of the maven-failsafe-plugin created to aid in running integration tests:
<plugin> <artifactId>maven-failsafe-plugin</artifactId> <version>${maven.failsafe.version}</version> <executions> <execution> <goals> <goal>integration-test</goal> <goal>verify</goal> </goals> </execution> </executions> </plugin>
This plug-in, configured as shown, specifies two main goals, integration-test, to run integration tests, and verify, to ensure that all integration tests pass (before releasing Maven execution as being successful).
Note that in more complex Maven commands, you will tend to see a plug-in specified, when specified at all, suffixed by a colon (:). Find an example below of the maven-javadoc-plugin. You will note that what appears on the command line is the name of plugin without prefix maven- and without the suffix -plugin. It will always be followed by a goal name.
to list all goals for a given plug-in, do this:
$ mvn plugin-name:help
To build a Maven project, simply execute the intended life cycle by running one of the phases:
$ mvn deploy // the whole enchilada $ mvn install // stop at the install phase $ mvn clean package // build only the JAR, WAR, NAR, etc. $ mvn compiler:compile // just see if stuff compiles $ mvn compile // (ditto) $ mvn install javadoc:javadoc javadoc:aggregate // build and install Javadoc locally
In multimodule projects, such as the need to build a custom NAR for Apache NiFi, and to bundle that NAR in a Docker container, (pom.xml) module names must be twisted in order for the NAR to have been fully built before the Spotify Docker plug-in is unleashed on it because to do so too early results in
[ERROR] ADD failed: no source files were specified [INFO] ------------------------------------------------------------------------ [INFO] Reactor Summary for Custom NAR 4.1.0: [INFO] [INFO] Custom NAR ......................................... FAILURE [02:56 min] [INFO] shared ............................................. SKIPPED [INFO] custom ............................................. SKIPPED [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 02:57 min [INFO] Finished at: 2022-01-28T09:50:06-07:00 [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal com.spotify:dockerfile-maven-plugin:1.4.13:build (default) on project custom-parent: \ Could not build image: ADD failed: no source files were specified org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal com.spotify:dockerfile-maven-plugin:1.4.13:build ...
The NAR file hasn't been created yet, so isn't in nar/target/custom-nar-4.1.0.nar. Goal build clearly isn't the right one to specify in the Spotify plug-in.
Trying separately:
$ mvn clean package // builds the custom NAR $ mvn dockerfile:build // invokes the Spotify plug-in afterward
The problem is that the second invocation wants to walk all the modules all over again. It fails to find a Dockerfile in the modules. Of course, I don't need it to walk the submodules. The product of all of those is already encased in the NAR file. I just need to copy the NAR file into an Apache NiFi Docker container.
[ERROR] Missing Dockerfile in context directory: /home/russ/sandboxes/nifi-pipeline/shared . . . [ERROR] Failed to execute goal com.spotify:dockerfile-maven-plugin:1.4.13:build (default-cli) on project shared: \ Missing Dockerfile in context directory: /home/russ/sandboxes/nifi-pipeline/shared
It's ugly, but skipping modules when building the Docker container appears to work; both these command lines use the -pl option to skip the modules. Obviously the second one is better and works since Maven knows how to determine the submodules in the current subdirectory (.) and we don't have to list them by hand.
$ mvn -pl '!shared, !custom' dockerfile:build $ mvn -pl . dockerfile:build
First, realize that, despite using make-repo.sh to create a Maven filesystem in the local repository in lib off the project root, Maven wants to use ~/.m2/repository to build with. In order to get the JARs from lib to ~/.m2/repository, use this command:
Do not use the example suggested by Generate source code JAR for Maven-based project. This will only create two JARs, both with only source code inside. The link errors of missing symbols will make you pull your head out until you realize this.
Instead, do this:
org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} 1.8 1.8
src/main/resources src/main/java **/*.java
#!/bin/sh #------------------------------------------------------------------------------- # From a JAR file, create a Maven repository filesystem in imitation of what's # on ~/.m2/repository. #------------------------------------------------------------------------------- if [ -n "$1" ]; then if [ $1 = "--help" -o $1 = "-h" ]; then echo "Create local artifactory repository, arguments:" echo " 1: path to repository subdirectory" echo " 2: path to JAR" echo " 3: groupId" echo " 4: artifactId" echo " 5: version" exit 0 fi fi TARGET_REPO=$1 JAR_FILENAME=$2 GROUPID=$3 ARTIFACTID=$4 VERSION=$5 HARDCODED_ARGUMENTS="-DcreateChecksum=true \ -Dpackaging=jar \ -DgeneratePom=true" SOFT_ARGUMENTS="-DlocalRepositoryPath=$TARGET_REPO \ -Dfile=$JAR_FILENAME \ -DgroupId=$GROUPID \ -DartifactId=$ARTIFACTID \ -Dversion=$VERSION" #echo "$HARDCODED_ARGUMENTS $SOFT_ARGUMENTS" errors= if [ -z "$TARGET_REPO" ]; then echo "Missing path to target repository" errors=1 fi if [ -z "$JAR_FILENAME" ]; then echo "Missing path to JAR" errors=2 fi if [ -z "$GROUPID" ]; then echo "Missing JAR's groupId" errors=3 fi if [ -z "$ARTIFACTID" ]; then echo "Missing JAR's artifactId" errors=4 fi if [ -z "$VERSION" ]; then echo "Missing JAR version" errors=5 fi if [ $errors -gt 0 ]; then exit 1 fi if [ -n "$SOFT_ARGUMENTS" ]; then mvn -e install:install-file $HARDCODED_ARGUMENTS $SOFT_ARGUMENTS fi # vim: set tabstop=2 shiftwidth=2 noexpandtab:
...containing not only class files (compiled Java code), but also sources to those class files.
This is more about IntelliJ IDEA perhaps than about Maven. You should assimilate the information in the immediately previous note on source-code in JARs before tackling what follows here.
Let's imagine you have three private library JARs and an application. Each is in its own IDEA (and Maven) project, but, for our "trick" here, I'm adding srclib.*
Each project has a filesystem containing about what you'd expect for a proper Maven project in IntelliJ IDEA, principally (because we're only interested in subdirectories here):
~/sandboxes $ tree base-library ├── lib ├── src ├── srclib └── update-consumers.sh # copies base-library.jar to utilities-library/srclib, business-logic/srclib and application/srclib utilities-library ├── lib ├── src ├── srclib # acquires base-library.jar └── update-consumers.sh # copies utilities-library.jar to business-logic/srclib and application/srclib business-logic ├── lib ├── src ├── srclib # acquires base-library.jar and utilities-library.jar └── update-consumers.sh # copies business-logic.jar to application/srclib application ├── lib ├── src └── srclib # acquires base-library.jar, utilities-library.jar and business-logic.jar
In order for utilities-library, business-logic and application to build, the libraries upon which they depend must find their way into ~/.m2/repository.
Here's how to get base-library into utilities-library as well as into ~/.m2/repository, which is essential for displaying changed code in the IDE (debugger, etc.). This will have to happen for business-logic and, later, nifi-pipeline too.
$ make-repo.sh ../lib ./base-library-4.1.jar com.windofkeltia.base-library base-library 4.1
~/sandboxes/utilities-library $ tree lib/com/windofkeltia/base-library/ lib/com/windofkeltia/base-library/ └── base-library ├── 4.1 │ ├── base-library-4.1.jar │ ├── base-library-4.1.jar.md5 │ ├── base-library-4.1.jar.sha1 │ ├── base-library-4.1.pom │ ├── base-library-4.1.pom.md5 │ └── base-library-4.1.pom.sha1 ├── maven-metadata-local.xml ├── maven-metadata-local.xml.md5 └── maven-metadata-local.xml.sha1 2 directories, 9 files
~/.m2/repository/com/windofkeltia $ ll total 16 drwxrwxr-x 4 russ russ 4096 Feb 22 12:08 . drwxrwxr-x 55 russ russ 4096 Feb 21 15:04 .. drwxrwxr-x 3 russ russ 4096 Feb 22 12:08 base-library ~/.m2/repository/com/windofkeltia $ rm -rf base-library
$ mvn dependency:resolve -U