I am fairly new to working with Maven to build my Java projects, and have run into a situation I don't know how to handle.
I have a Java application that has 3 dependencies, let's call them a, b, and c. However, c will be a different artifact depending on the platform we are building on, so I've used profiles to achieve this. Here is a snippet from my pom.xml:
<profiles>
<profile>
<id>win32</id>
<activation>
<os>
<family>windows</family>
<arch>x86</arch>
</os>
</activation>
<dependencies>
<dependency>
<groupId>com.seanbright</groupId>
<artifactId>c-win32-x86</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</profile>
<profile>
<id>win64</id>
<activation>
<os>
<family>windows</family>
<arch>amd64</arch>
</os>
</activation>
<dependencies>
<dependency>
<groupId>com.seanbright</groupId>
<artifactId>c-win32-x86_64</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</profile>
</profiles>
The a and b artifacts are listed as dependencies at the POM level as they are platform agnostic and aren't activated along with the profile. They aren't shown here for the sake of brevity.
Now I want to build an executable JAR of my project, and include a, b, and c in a lib/ directory along side the generated JAR from my code, so I would end up with something like this:
target/my-project-1.0.0.jar
target/lib/a-1.0.0.jar
target/lib/b-1.0.0.jar
target/lib/c-1.0.0.jar
The manifest in my-project-1.0.0.jar will have the appropriate classpath so that it can be double clicked on and the application will launch. I use the dependency:copy-dependencies and jar:jar goals to make all of this work:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>copy-dependencies</id>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
<includeScope>runtime</includeScope>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<mainClass>com.seanbright.myproject.Launch</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
And... it works. The only problem, is that c is copied to the lib/ directory (and added to the Class-Path in the manifest) as c-win32-x86-1.0.0.jar or c-win32-x86_64-1.0.0.jar depending on the active profile, and I want it to end up as c-1.0.0.jar instead.
Using dependency:copy with destFileName instead of dependency:copy-dependencies results in the correct filename, but the entry in the Class-Path still refers to the "fully qualified" artifact name (i.e. lib/c-win32-x86-1.0.0.jar).
Am I going about this the wrong way? Is there an easier way to accomplish what I am trying to do?
The Set Up The Classpath:Altering The Classpath: Using a Custom Classpath Format told us as the following: -
At times, you may have dependency archives in a custom format within your own archive, one that doesn't conform to any of the above classpath layouts. If you wish to define a custom layout for dependency archives within your archive's manifest classpath, try using the <classpathLayoutType> element with a value of 'custom', along with the <customClasspathLayout> element, like this:
<project>
...
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathLayoutType>custom</classpathLayoutType>
<customClasspathLayout>WEB-INF/lib/$${artifact.groupIdPath}/$${artifact.artifactId}-$${artifact.version}$${dashClassifier?}.$${artifact.extension}</customClasspathLayout>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
...
<dependencies>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
...
</project>
This classpath layout is a little more involved than the previous examples. To understand how the value of the <customClasspathLayout> configuration is interpreted, it's useful to understand the rules applied when resolving expressions within the value:
If present, trim off the prefix 'artifact.' from the expression.
Attempt to resolve the expression as a reference to the Artifact using reflection (eg. 'artifactId' becomes a reference to the method 'getArtifactId()').
Attempt to resolve the expression as a reference to the ArtifactHandler of the current Artifact, again using reflection (eg. 'extension' becomes a reference to the method 'getExtension()').
Attempt to resolve the expression as a key in the special-case Properties instance, which contains the following mappings:
'dashClassifier': If the Artifact has a classifier, this will be '- $artifact.classifier', otherwise this is an empty string.
'dashClassifier?': This is a synonym of 'dashClassifier'.
'groupIdPath': This is the equivalent of '$artifact.groupId', with all '.'characters replaced by '/'.
The manifest classpath produced using the above configuration would look like this:
Class-Path: WEB-INF/lib/org/codehaus/plexus/plexus-utils-1.1.jar WEB-INF/lib/commons-lang/commons-lang-2.1.jar
I hope this may help.
Related
I have a maven project which extends an existing parent project (It's the "standard product" from which my product will be a "Customized product").
The parent declares a org.codehaus.cargo / cargo-maven2-plugin and passes it some VM args under configuration / cargo.jvmargs. Like this:
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.4.18</version>
<configuration>
<container>
<containerId>tomcat8x</containerId>
[...]
<dependencies>
[...]
</dependencies>
</container>
<configuration>
<properties>
<cargo.jvmargs>-ArgA -ArgB -ArgC</cargo.jvmargs>
</properties>
<configfiles>
[...]
</configfiles>
<files>
[...]
</files>
</configuration>
</configuration>
</plugin>
Now in my custom project, I want to extend these jvm args with one more argument (Let's say -ArgD), so that the args are -ArgA -ArgB -ArgC -ArgD. I don't want to override the entire plugin only to do this one little change.
I know that I can specify this: cargo:run -Dcargo.jvmargs="-ArgD" but the problem here is: All other args (ArgA, ArgB, ArgC) get overridden/removed, only ArgD will remain. What I need is something like cargo:run -Dcargo.jvmargs="current_cargo.jvmargs + -ArgD".
Is this possible somehow?
The cleanest possibility would be to move jvmargs in parent pom to maven property. Then in your custom project you would be able combine jvmargs using maven property with your custom values. For example:
Parent pom:
<properties>
<cargo.base.jvmargs>-ArgA -ArgB -ArgC</cargo.base.jvmargs>
</properties>
[...]
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.5.0</version>
<configuration>
[...]
<configuration>
<properties>
<cargo.jvmargs>${cargo.base.jvmargs}</cargo.jvmargs>
</properties>
[...]
</configuration>
</configuration>
</plugin>
Your custom pom:
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<configuration>
[...]
<configuration>
<properties>
<cargo.jvmargs>${cargo.base.jvmargs} -ArgD</cargo.jvmargs>
</properties>
[...]
</configuration>
</configuration>
</plugin>
If there isn't possibility to modify parent pom you may use Cargo property cargo.start.jvmargs (see this page). This property add java arguments to container when it is started.
I'm using liquibase maven plugin to update the database changes via jenkins automated builds.
I have this in my pom.xml
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>3.4.2</version>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.5</version>
</dependency>
</dependencies>
<configuration>
<changeLogFile>${basedir}/src/main/resources/schema.sql</changeLogFile>
<changeLogFile>${basedir}/src/main/resources/data.sql</changeLogFile>
<driver>org.postgresql.Driver</driver>
<url>jdbc:postgresql://${db.url}</url>
<promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
</configuration>
</plugin>
I need to run schema.sql before data.sql. When I run this locally it works. When I run it via jenkins the schema changeLogFile executes second, so in order to make it work I reversed the commads.
Question: What's the order of execution? Am I doing something wrong?
The official goal documentation specify that only one entry is foreseen:
changeLogFile:
Specifies the change log file to use for Liquibase.
Type: java.lang.String
Required: No
Expression: ${liquibase.changeLogFile}
You can add further entries, but they will be ignored and maven will not complain: it doesn't validate plugin configuration' content, it cannot, because that part is up to the plugin and not known upfront by maven. That is, is generic.
To ensure a deterministic order and have two changeLogFile executed, you should specify several plugin executions as following:
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>3.4.2</version>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.5</version>
</dependency>
</dependencies>
<configuration>
<changeLogFile>${basedir}/src/main/resources/schema.sql</changeLogFile>
<changeLogFile>${basedir}/src/main/resources/data.sql</changeLogFile>
<driver>org.postgresql.Driver</driver>
<url>jdbc:postgresql://${db.url}</url>
<promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
</configuration>
<executions>
<execution>
<id>update-schema</id>
<phase>process-resources</phase>
<goals>
<goal>update</goal>
</goals>
<configuration>
<changeLogFile>${basedir}/src/main/resources/schema.sql</changeLogFile>
</configuration>
</execution>
<execution>
<id>update-data</id>
<phase>process-resources</phase>
<goals>
<goal>update</goal>
</goals>
<configuration>
<changeLogFile>${basedir}/src/main/resources/data.sql</changeLogFile>
</configuration>
</execution>
</executions>
</plugin>
Note: we are specifying a common configuration for all executions outside of the executions section, then per each execution we are only defining the additional configuration, which is every time the different file.
The deterministic order is guaranteed by Maven: for the same plugin, for the same phase, the order of declaration in the POM will be respected.
However, this executions will be part of your build now as part of the process-resources phase, which is probably not what you want. So in this case, better to move it to a profile as following:
<profiles>
<profile>
<id>liquibase-executions</id>
<build>
<defaultGoal>process-resources</defaultGoal>
<plugins>
<!-- MOVE HERE liquibase plugin configuration and executions -->
</plugins>
</build>
</profile>
</profiles>
And then execute the following (according also to your comment):
mvn -Pliquibase-executions -Ddb.url=IP:PORT/DB -Dliquibase.username=USERNAME
I use maven-jgit-buildnumber-plugin(github)
In one case I define it in profile:
<profile>
<id>make-buildnumber</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>ru.concerteza.buildnumber</groupId>
<artifactId>maven-jgit-buildnumber-plugin</artifactId>
<executions>
<execution>
<id>make-buildnumber</id>
<goals>
<goal>extract-buildnumber</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
In other case I point it in build section of the pom.xml.
In pluginManagement section I try to use variables from plugin:
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<manifestSections>
<manifestSection>
<name>SCM</name>
<manifestEntries>
<Branch>${git.branch}</Branch>
</manifestEntries>
</manifestSection>
</manifestSections>
</archive>
</configuration>
</plugin>
In first case it is not working, variable is null. Why???
Looking at the linked github documentation, the plugin execution is bounded to the prepare-package phase, which is missing in the snippet code you posted. So probably missing the phase, it is not executed before the maven-jar-plugin and not setting the variable properly (given that its default phase should follow the default phase of the maven-jar-plugin). As such, you might get it at null.
Try to add the <phase>prepare-package</phase> element to the execution of the plugin.
Moreover, pluginConfiguration is just effectively used if the same plugin is then mentioned in the build/plugins section. Hence, in the build/plugins section you should also add as following:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
It will then pick up the configuration you specified in the pluginManagement section.
From Maven documentation:
pluginManagement: is an element that is seen along side plugins. Plugin Management contains plugin elements in much the same way, except that rather than configuring plugin information for this particular project build, it is intended to configure project builds that inherit from this one. However, this only configures plugins that are actually referenced within the plugins element in the children. The children have every right to override pluginManagement definitions.
I set up the docbkx-maven-plugin to generate PDF documentation for a source project. In the parent pom I specified the version to be used as well as the reference to the docbook version to use:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>com.agilejava.docbkx</groupId>
<artifactId>docbkx-maven-plugin</artifactId>
<version>2.0.14</version>
<dependencies>
<dependency>
<groupId>net.sf.docbook</groupId>
<artifactId>docbook-xml</artifactId>
<version>5.0-all</version>
<type>zip</type>
<classifier>resources</classifier>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</pluginManagement>
In the actual project I use the configuration:
<build>
<plugins>
<plugin>
<groupId>com.agilejava.docbkx</groupId>
<artifactId>docbkx-maven-plugin</artifactId>
<executions>
<execution>
<id>usersmanual</id>
<phase>generate-resources</phase>
<goals>
<goal>generate-pdf</goal>
</goals>
<configuration>
<includes>${basedir}/UserManual/*.xml</includes>
<includes>${basedir}/UserManual/src/*.xml</includes>
<targetDirectory>${project.build.directory}/UserManual</targetDirectory>
<chunkedOutput>true</chunkedOutput>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
No matter what goal(s) I specify or what includes I provide, the plugin performs no(!) operation. There is no target directory created and I do not see any meaningful output on the command line. The result looks like:
[INFO] --- docbkx-maven-plugin:2.0.14:generate-pdf (usersmanual) # documentation ---
[INFO]
I use Maven 3.0.3.
What do I miss here? Is there any configuration not provided, yet, which will start the plugin to do some work?
UPDATE:
This is what I have now:
<plugin>
<groupId>com.agilejava.docbkx</groupId>
<artifactId>docbkx-maven-plugin</artifactId>
<version>2.0.14</version>
<dependencies>
<dependency>
<groupId>net.sf.docbook</groupId>
<artifactId>docbook-xml</artifactId>
<version>5.0-all</version>
<type>zip</type>
<classifier>resources</classifier>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>generate-pdf</goal>
</goals>
<phase>prepare-package</phase>
<configuration>
<sourceDirectory>${project.basedir}/UserManual</sourceDirectory>
<xincludeSupported>true</xincludeSupported>
<includes>${project.basedir}/UserManual/UserManual.xml</includes>
<includes>${project.basedir}/UserManual/**/*.xml</includes>
<targetDirectory>${project.build.directory}/UserManual</targetDirectory>
</configuration>
</execution>
</executions>
</plugin>
At least the directory target/Usermanual is greated, but it is still empty. Why is there still not output? Do I have a chance to get a meaningful log file from docbkx? mvn ... -X does not tell much.
Your latest example contains two includes configuration options which is not valid maven configuration.
My recommendation is to stop trying to override all these defaults and accept the default location for the docbook source xml, at least initially while you get comfortable with the plugin and can diagnose what issues are from what changes.
Anyway, your <includes> should be just the root xml file of the documentation you're trying to generate as it exists in the <sourceDirectory>. You do not need to include all of the xml files, you instead need to follow the xincludes approach since you're declaring its usage. There are a number of projects using this mechanism that you can review and copy the usage of.
Ours is: https://github.com/jetty-project/jetty-documentation
We have a bit more complex usage since we use the maven filtering plugin to avoid having to mess with entities and the like. Getting back to your includes usage, if your top level docbook file is index.xml then your includes would simply be:
<includes>index.xml</includes>
No expressions or globs needed, you bring in the other xml documents with the <xi:include> tags where needed.
I've got a taglib project that I use the TLDGen library to help build my TLD files from annotations in my classes. I've then got it plugged into the Maven JavaDoc plugin to have it build the TLD files via the javadoc:javadoc Maven goal. Pom portion that handles this is as follows:
<build>
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<configuration>
<doclet>org.tldgen.TldDoclet</doclet>
<docletArtifact>
<groupId>com.google.code.tldgen</groupId>
<artifactId>tldgen-all</artifactId>
<version>1.0.0</version>
</docletArtifact>
<show>private</show>
<additionalparam>-name test
-uri "http://www.mycompany.com/tags/wibble"
-tldFile ..\..\..\src\main\resources\META-INF\w.tld
</additionalparam>
<useStandardDocletOptions>true</useStandardDocletOptions>
<author>false</author>
<encoding>utf-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
And this works fantastically. Trouble is that I know want to create 2 TLD's from this project. I can pass a -subpackages attribute in th addtionalparam element so I can produce a TLD with exactly what I want.
But I can only have one configuration element at that point. I've tried moving the configuration into the reporting section in my pom with two reportsets to see if that helps but no luck.
Has anyone ever attempted this before and can help point me in the right direction for getting it right? Cheers!
It's been a while since this question was posted, but here's how I did multiple tld generation with TLDGen. I started from your question, since the guys over at the project used your answer as a reference :).
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<configuration>
<includes>
<include>**</include>
</includes>
<doclet>org.tldgen.TldDoclet</doclet>
<docletArtifacts>
<!-- listing all dependencies for tldgen:
the tldgen library, commons-logging, commons-io,
commons-lang, geronimo-jsp_2.1_spec, log4j, saxon, stax
not sure if they have to be listed here, will have to check; if I
don't set them I get class not found errors, but I'm guessing I
have a misconfiguration -->
</docletArtifacts>
<show>private</show>
<additionalparam>
-htmlFolder ${basedir}/target/docs
-tldFolder ${basedir}/src/main/java/META-INF
-license NONE
</additionalparam>
<useStandardDocletOptions>true</useStandardDocletOptions>
<author>false</author>
<encoding>utf-8</encoding>
</configuration>
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jsr173_api</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>javadoc</goal>
</goals>
</execution>
</executions>
</plugin>