Nightly build - do we delete files in "target" first? - java

Folks,
As part of my nightly build for a Java application, I do the following:
$ cd dirContainingPOM.XML
$ svn update
$ rm -rf target
$ mvn package
Essentially, I get the latest version of the source code, delete all the files in the target directory, and build the jar package.
The question I have is if it is a good practice to delete everything in "target" directory or is it just an overkill. Is maven doing it automatically for me?
Thank you in advance for your help.
Regards,Peter

It's a good practice but you easily achieve the same thing by specifying the clean target in your maven command.
Instead of performing ...
mvn package
Try the following ...
mvn clean package
This will effectively negate the need to perform the rm -rf on your target directory.

Related

Maven file and profile at the same time not working?

I'm trying to execute a maven command like the following:
mvn clean install -f ../<some-path>/pom.xml -Pear -DskipTests
For some reason this command is not working correctly. The profile is not getting applied. What is the problem here?
I think maybe -f doesn't work as you think it does. See this answer: How to run Maven from another directory (without cd to project dir)?
If your project uses relative pom locations, this breaks.
See also: https://maven.apache.org/plugin-developers/common-bugs.html#Resolving_Relative_Paths
cd ..
call mvn -DskipTests -Pear clean install
cd <some-path>
I did it like this now and it works. Thanks for #Adriaan Koster for the hint with the relative poms. That seems to have been it. And maybe i should use absolute paths instead :)

Maven docker cache dependencies

I'm trying to use docker to automate maven builds. The project I want to build takes nearly 20 minutes to download all the dependencies, so I tried to build a docker image that would cache these dependencies, but it doesn't seem to save it. My Dockerfile is
FROM maven:alpine
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
ADD pom.xml /usr/src/app
RUN mvn dependency:go-offline
The image builds, and it does download everything. However, the resulting image is the same size as the base maven:alpine image, so it doesn't seem to have cached the dependencies in the image. When I try to use the image to mvn compile it goes through the full 20 minutes of redownloading everything.
Is it possible to build a maven image that caches my dependencies so they don't have to download everytime I use the image to perform a build?
I'm running the following commands:
docker build -t my-maven .
docker run -it --rm --name my-maven-project -v "$PWD":/usr/src/mymaven -w /usr/src/mymaven my-maven mvn compile
My understanding is that whatever RUN does during the docker build process becomes part of the resulting image.
Usually, there's no change in pom.xml file but just some other source code changes when you're attempting to start docker image build. In such circumstance you can do this:
FYI:
FROM maven:3-jdk-8
ENV HOME=/home/usr/app
RUN mkdir -p $HOME
WORKDIR $HOME
# 1. add pom.xml only here
ADD pom.xml $HOME
# 2. start downloading dependencies
RUN ["/usr/local/bin/mvn-entrypoint.sh", "mvn", "verify", "clean", "--fail-never"]
# 3. add all source code and start compiling
ADD . $HOME
RUN ["mvn", "package"]
EXPOSE 8005
CMD ["java", "-jar", "./target/dist.jar"]
So the key is:
add pom.xml file.
then mvn verify --fail-never it, it will download maven dependencies.
add all your source file then, and start your compilation(mvn package).
When there are changes in your pom.xml file or you are running this script for the first time, docker will do 1 -> 2 -> 3. When there are no changes in pom.xml file, docker will skip step 1、2 and do 3 directly.
This simple trick can be used in many other package management circumstances(gradle、yarn、npm、pip).
Edit:
You should also consider using mvn dependency:resolve or mvn dependency:go-offline accordingly as other comments & answers suggest.
Using BuildKit
From Docker v18.03 onwards you can use BuildKit instead of volumes that were mentioned in the other answers. It allows mounting caches that can persist between builds and you can avoid downloading contents of the corresponding .m2/repository every time.
Assuming that the Dockerfile is in the root of your project:
# syntax = docker/dockerfile:1.0-experimental
FROM maven:3.6.0-jdk-11-slim AS build
COPY . /home/build
RUN mkdir /home/.m2
WORKDIR /home/.m2
USER root
RUN --mount=type=cache,target=/root/.m2 mvn -f /home/build/pom.xml clean compile
target=/root/.m2 mounts cache to the specified place in maven image Dockerfile docs.
For building you can run the following command:
DOCKER_BUILDKIT=1 docker build --rm --no-cache .
More info on BuildKit can be found here.
It turns out the image I'm using as a base has a parent image which defines
VOLUME "$USER_HOME_DIR/.m2"
see: https://github.com/carlossg/docker-maven/blob/322d0dff5d0531ccaf47bf49338cb3e294fd66c8/jdk-8/Dockerfile
The result is that during the build, all the files are written to $USER_HOME_DIR/.m2, but because it is expected to be a volume, none of those files are persisted with the container image.
Currently in Docker there isn't any way to unregister that volume definition, so it would be necessary to build a separate maven image, rather than use the official maven image.
I don't think the other answers here are optimal. For example, the mvn verify answer executes the following phases, and does a lot more than just resolving dependencies:
validate - validate the project is correct and all necessary information is available
compile - compile the source code of the project
test - test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployed
package - take the compiled code and package it in its distributable format, such as a JAR.
verify - run any checks on results of integration tests to ensure quality criteria are met
All of these phases and their associated goals don't need to be ran if you only want to resolve dependencies.
If you only want to resolve dependencies, you can use the dependency:go-offline goal:
FROM maven:3-jdk-12
WORKDIR /tmp/example/
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src/ src/
RUN mvn package
There are two ways to cache maven dependencies:
Execute "mvn verify" as part of a container execution, NOT build, and make sure you mount .m2 from a volume.
This is efficient but it does not play well with cloud build and multiple build slaves
Use a "dependencies cache container", and update it periodically. Here is how:
a. Create a Dockerfile that copies the pom and build offline dependencies:
FROM maven:3.5.3-jdk-8-alpine
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline
b. Build it periodically (e.g. nightly) as "Deps:latest"
c. Create another Dockerfile to actually build the system per commit (preferably use multi-stage) - and make sure it is FROM Deps.
Using this system you will have fast, reconstruct-able builds with a mostly good-enough cache.
#Kim is closest, but it's not quite there yet. I don't think adding --fail-never is correct, even through it get's the job done.
The verify command causes a lot of plugins to execute which is a problem (for me) - I don't think they should be executing when all I want is to install dependencies! I also have a multi-module build and a javascript sub-build so this further complicates the setup.
But running only verify is not enough, because if you run install in the following commands, there will be more plugins used - which means more dependencies to download - maven refuses to download them otherwise. Relevant read: Maven: Introduction to the Build Lifecycle
You basically have to find what properties disable each plugin and add them one-by-one, so they don't break your build.
WORKDIR /srv
# cache Maven dependencies
ADD cli/pom.xml /srv/cli/
ADD core/pom.xml /srv/core/
ADD parent/pom.xml /srv/parent/
ADD rest-api/pom.xml /srv/rest-api/
ADD web-admin/pom.xml /srv/web-admin/
ADD pom.xml /srv/
RUN mvn -B clean install -DskipTests -Dcheckstyle.skip -Dasciidoctor.skip -Djacoco.skip -Dmaven.gitcommitid.skip -Dspring-boot.repackage.skip -Dmaven.exec.skip=true -Dmaven.install.skip -Dmaven.resources.skip
# cache YARN dependencies
ADD ./web-admin/package.json ./web-admin/yarn.lock /srv/web-admin/
RUN yarn --non-interactive --frozen-lockfile --no-progress --cwd /srv/web-admin install
# build the project
ADD . /srv
RUN mvn -B clean install
but some plugins are not that easily skipped - I'm not a maven expert (so I don't know why it ignores the cli option - it might be a bug), but the following works as expected for org.codehaus.mojo:exec-maven-plugin
<project>
<properties>
<maven.exec.skip>false</maven.exec.skip>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.3.2</version>
<executions>
<execution>
<id>yarn install</id>
<goals>
<goal>exec</goal>
</goals>
<phase>initialize</phase>
<configuration>
<executable>yarn</executable>
<arguments>
<argument>install</argument>
</arguments>
<skip>${maven.exec.skip}</skip>
</configuration>
</execution>
<execution>
<id>yarn run build</id>
<goals>
<goal>exec</goal>
</goals>
<phase>compile</phase>
<configuration>
<executable>yarn</executable>
<arguments>
<argument>run</argument>
<argument>build</argument>
</arguments>
<skip>${maven.exec.skip}</skip>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
please notice the explicit <skip>${maven.exec.skip}</skip> - other plugins pick this up from the cli params but not this one (neither -Dmaven.exec.skip=true nor -Dexec.skip=true work by itself)
Hope this helps
Similar with #Kim answer but I use dependency:resolve mvn command. So here's my complete Dockerfile:
FROM maven:3.5.0-jdk-8-alpine
WORKDIR /usr/src/app
# First copy only the pom file. This is the file with less change
COPY ./pom.xml .
# Download the package and make it cached in docker image
RUN mvn -B -f ./pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve
# Copy the actual code
COPY ./ .
# Then build the code
RUN mvn -B -f ./pom.xml -s /usr/share/maven/ref/settings-docker.xml package
# The rest is same as usual
EXPOSE 8888
CMD ["java", "-jar", "./target/YOUR-APP.jar"]
After a few days of struggling, I managed to do this caching later using intermediate contrainer, and I'd like to summarize my findings here as this topic is so useful and being frequently shown in Google search frontpage:
Kim's answer is only working to a certain condition: pom.xml cannot be changed, plus Maven do a regular update daily basis by default
mvn dependency:go-offline -B --fail-never has a similar drawback, so if you need to pull fresh code from repo, high chances are Maven will trigger a full checkout every time
Mount volume is not working as well because we need to resolve the dependencies during image being built
Finally, I have a workable solution combined(May be not working to others):
Build an image to resolve all the dependencies first(Not intermediate image)
Create another Dockerfile with intermediate image, sample dockerfiles like this:
#docker build -t dependencies .
From ubuntu
COPY pom.xml pom.xml
RUN mvn dependency:go-offline -B --fail-never
From dependencies as intermediate
From tomcat
RUN git pull repo.git (whatsoever)
RUN mvn package
The idea is to keep all the dependencies in a different image that Maven can use immediately
It could be other scenarios I haven't encountered yet, but this solution relief me a bit from download 3GB rubbish every time
I cannot imagine why Java became such a fat whale in today's lean world
I had to deal with the same issue.
Unfortunately, as just said by another contributor, dependency:go-offline and the other goals, don't fully solve the problem: many dependencies are not downloaded.
I found a working solution as follow.
# Cache dependencies
ADD settings.xml .
ADD pom.xml .
RUN mvn -B -s settings.xml -Ddocker.build.skip=true package test
# Build artifact
ADD src .
RUN mvn -B -s settings.xml -DskipTests package
The trick is to do a full build without sources, which produces a full dependency scan.
In order to avoid errors on some plugins (for example: OpenAPI maven generator plugin or Spring Boot maven plugin) I had to skip its goals, but letting it to download all the dependencies by adding for each one a configuration settings like follow:
<configuration>
<skip>${docker.build.skip}</skip>
</configuration>
Regards.
I think the general game plan presented among the other answers is the right idea:
Copy pom.xml
Get dependencies
Copy source
Build
However, exactly how you do step #2 is the real key. For me, using the same command I used for building to fetch dependencies was the right solution:
FROM java/java:latest
# Work dir
WORKDIR /app
RUN mkdir -p .
# Copy pom and get dependencies
COPY pom.xml pom.xml
RUN mvn -Dmaven.repo.local=./.m2 install assembly:single
# Copy and build source
COPY . .
RUN mvn -Dmaven.repo.local=./.m2 install assembly:single
Any other command used to fetch dependencies resulted in many things needing to be download during the build step. It makes sense the running the exact command you plan on running will you get you the closest to everything you need to actually run that command.
I had this issue just a litle while ago. The are many solutions on the web, but the one that worked for me is simply mount a volume for the maven modules directory:
mkdir /opt/myvolumes/m2
then in the Dockerfile:
...
VOLUME /opt/myvolumes/m2:/root/.m2
...
There are better solutions, but not as straightforward.
This blog post goes the extra mile in helping you to cache everything:
https://keyholesoftware.com/2015/01/05/caching-for-maven-docker-builds/
A local Nexus 3 Image running in Docker and acting as a local Proxy is an acceptable solution:
The idea is similar to Dockerize an apt-cacher-ng service apt-cacher-ng
here you can find a comprehensive step by step. github repo
Its really fast.
Another Solution would be using a repository manger such as Sonar Nexus or Artifactory. You can set a maven proxy inside the registry then use the registry as your source of maven repositories.
Here my working solution.
The tricks are:
use docker multi-stage build
don't copy the project source in the image created in the first stage, but only pom (or poms in case your project is multi-module)
Here my solution for a multi-module project using openjdk11
## stage 1
FROM adoptopenjdk/maven-openjdk11 as dependencies
ENV HOME=/usr/maven
ENV MVN_REPO=/usr/maven/.m3/repository
RUN mkdir -p $HOME
RUN mkdir -p $MVN_REPO
WORKDIR $HOME
## copy all pom files of the modules tree with the same directory tree of the project
#reactor
ADD pom.xml $HOME
## api module
RUN mkdir -p $HOME/api
ADD api/pom.xml $HOME/api
## application module
RUN mkdir -p $HOME/application
ADD application/pom.xml $HOME/application
## domain module
RUN mkdir -p $HOME/domain
ADD domain/pom.xml $HOME/domain
## service module
RUN mkdir -p $HOME/service
ADD service/pom.xml $HOME/service
## download all dependencies in this docker image. The goal "test" is needed to avoid download of dependencies with <scope>test</scope> in the second stage
RUN mvn -Dmaven.repo.local=$MVN_REPO dependency:go-offline test
## stage 2
FROM adoptopenjdk/maven-openjdk11 as executable
ENV APP_HOME=/usr/app
ENV MVN_REPO=/usr/maven/.m3/repository
ENV APP_MVN_REPO=$MVN_REPO
RUN mkdir -p $APP_HOME
RUN mkdir -p $APP_MVN_REPO
WORKDIR $APP_HOME
ADD . $APP_HOME
## copy the dependecies tree from "stage 1" dependencies image to this image
COPY --from=dependencies $MVN_REPO $APP_MVN_REPO
## package the application, skipping test
RUN mvn -Dmaven.repo.local=$APP_MVN_REPO package -DskipTests
## set ENV values
ENV NAME=VALUE
## copy the jar in the WORKDIR folder
RUN cp $APP_HOME/application/target/*.jar $APP_HOME/my-final-jar-0.0.1-SNAPSHOT.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar","/usr/app/my-final-jar-0.0.1-SNAPSHOT.jar" ,"--spring.profiles.active=docker"]
This one did the trick very well for me:
edit config.toml
[runner.docker]
...
volumes = ["/cache","m2:/root/.m2"]
...
it will create "m2" volume that will persists across builds and you guys knows the rest :)
If the dependencies are downloaded after the container is already up, then you need to commit the changes on this container and create a new image with the downloaded artifacts.

What is the purpose of mvnw and mvnw.cmd files?

When I created a Spring Boot application I could see mvnw and mvnw.cmd files in the root of the project. What is the purpose of these two files?
These files are from Maven wrapper. It works similarly to the Gradle wrapper.
This allows you to run the Maven project without having Maven installed and present on the path. It downloads the correct Maven version if it's not found (as far as I know by default in your user home directory).
The mvnw file is for Linux (bash) and the mvnw.cmd is for the Windows environment.
To create or update all necessary Maven Wrapper files execute the following command:
mvn -N io.takari:maven:wrapper
To use a different version of maven you can specify the version as follows:
mvn -N io.takari:maven:wrapper -Dmaven=3.3.3
Both commands require maven on PATH (add the path to maven bin to Path on System Variables) if you already have mvnw in your project you can use ./mvnw instead of mvn in the commands.
Command mvnw uses Maven that is by default downloaded to ~/.m2/wrapper on the first use.
URL with Maven is specified in each project at .mvn/wrapper/maven-wrapper.properties:
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip
To update or change Maven version invoke the following (remember about --non-recursive for multi-module projects):
./mvnw io.takari:maven:wrapper -Dmaven=3.3.9
or just modify .mvn/wrapper/maven-wrapper.properties manually.
To generate wrapper from scratch using Maven (you need to have it already in PATH run:
mvn io.takari:maven:wrapper -Dmaven=3.3.9
The Maven Wrapper is an excellent choice for projects that need a specific version of Maven (or for users that don't want to install Maven at all). Instead of installing many versions of it in the operating system, we can just use the project-specific wrapper script.
mvnw: it's an executable Unix shell script used in place of a fully installed Maven
mvnw.cmd: it's for Windows environment
Use Cases
The wrapper should work with different operating systems such as:
Linux
OSX
Windows
Solaris
After that, we can run our goals like this for the Unix system:
./mvnw clean install
And the following command for Batch:
./mvnw.cmd clean install
If we don't have the specified Maven in the wrapper properties, it'll be downloaded and installed in the folder $USER_HOME/.m2/wrapper/dists of the system.
Maven Wrapper plugin
Maven Wrapper plugin to make auto installation in a simple Spring Boot project.
First, we need to go in the main folder of the project and run this command:
mvn -N io.takari:maven:wrapper
We can also specify the version of Maven:
mvn -N io.takari:maven:wrapper -Dmaven=3.5.2
The option -N means –non-recursive so that the wrapper will only be applied to the main project of the current directory, not in any submodules.
Source 1 (further reading): https://www.baeldung.com/maven-wrapper
short answer: to run Maven and Gradle in the terminal without following manual installation processes.
Gradle example:
./gradlew clean build
./gradlew bootRun
Maven example:
./mvnw clean install
./mvnw spring-boot:run
"The recommended way to execute any Gradle build is with the help of the Gradle Wrapper (in short just “Wrapper”). The Wrapper is a script that invokes a declared version of Gradle, downloading it beforehand if necessary. As a result, developers can get up and running with a Gradle project quickly without having to follow manual installation processes saving your company time and money."
Gradle would also add some specific files corresponding to the Maven files Gradlew and Gradle.bat
In the windows OS, mvnw clean install is used for the maven clean and install activity, and mvnw spring-boot:run is used to run the Spring boot application from Command Prompt.
For Eaxmple:
C:\SamplesSpringBoot\demo>mvnw clean install
C:\SamplesSpringBoot\demo>mvnw spring-boot:run
By far the best option nowadays would be using a maven container as a builder tool. A mvn.sh script like this would be enough:
#!/bin/bash
docker run --rm -ti \
-v $(pwd):/opt/app \
-w /opt/app \
-e TERM=xterm \
-v $HOME/.m2:/root/.m2 \
maven mvn "$#"

How do I build this java project with maven?

I honestly don't know anything about java, I'm just trying to build the latest version of this project to try and work around bugs in the version that is packaged by my OS. The project in question:
https://sourceforge.net/projects/tuxguitar/
I tried running mvn compile, mvn clean package, and mvn clean install but no jar file was produced in the source tree. The readme has no instructions for building. Any help would be greatly appreciated!
Thanks!
To produce jar you need at least package phase, you can get there by doing:
mvn clean package
(clean is to remove stale data, it is a good practice to use it always with maven) and you jar will be in target directory.
mvn complite just complite the project and no package.
answer : mvn clean package.this will clean,complite and package the project,after finished, you can found the package(jar or war) into the $PROJECT_HOME/target/ folder.
I usually run mvn clean install - which in addition will add the built jar into your local maven artifact repository.

How can I run a Java class inside a Maven artifact, automatically resolving dependencies?

If I know the coordinates of an artifact, and a name of the class inside that artifact, can I make Maven run the class, including all of its dependencies on the Java classpath?
For example, suppose a coworker told me about a tool I can run, which is published to our internal Nexus with the artifact coordinates example:cool-tools:1.0.0. I used this answer to download the artifact. Now, I know that the main class name is example.Main. But if I just go to the artifact's download location and run java -cp cool-tools-1.0.0.jar example.Main, I get NoClassDefFoundErrors for any dependencies of cool-tools.
I'm aware of the maven-exec-plugin, but as far as I can tell that's only for projects where you have the source. Suppose I don't have access to the source, only the Nexus containing the tool (and all its dependencies). Ideally, I'd do something like mvn exec:exec -DmainArtifact='example:cool-tools:1.0.0' -DmainClass='example.Main', but I don't think the exec plugin is actually capable of this.
ETA: To be clear, I do not have a local project / POM. I want to do this using only the command line, without writing a POM, if possible.
There is no out-of-the-box solution for your task. But you can create a simple script to solve it:
Download pom.xml of your tool from the repo.
Download jar of your tool.
Download all its dependencies.
Run java against resolved libraries.
Command line:
> mvn dependency:copy -Dartifact=<tool.group.id>:<tool.artifact.id>:<tool.version>:pom -DoutputDirectory=target
> mvn dependency:copy -Dartifact=<tool.group.id>:<tool.artifact.id>:<tool.version> -DoutputDirectory=target
> mvn dependency:copy-dependencies -f target/<tool.artifact.id>-<tool.version>.pom -DoutputDirectory=target
> java -cp target/* <tool.main.class>
Directory ./target will contain your tool + all dependencies.
See details on dependency:copy and dependency:copy-dependencies mojos.
Edit
As alternative, you can build classpath using libraries in the local repo by:
> mvn dependency:build-classpath -DincludeScope=runtime -f target/<tool.artifact.id>-<tool.version>.pom [-Dmdep.outputFile=/full/path/to/file]
See details on build-classpath mojo.
You could download the pom from the repository using wget, for instance. Then resolve the dependencies, and build the classpath exporting it to a file using Maven. Finally, execute with Java and the built classpath using something like bash backticks to use the content of the file.
Just like in this answer.
For me the first anwer almost worked, but I needed to slightly adjust the script. In the end I came (on windows machine) to following solution:
> mvn dependency:copy -Dartifact=<tool.group.id>:<tool.artifact.id>:<tool.version>:pom -DoutputDirectory=target
> mvn dependency:copy -Dartifact=<tool.group.id>:<tool.artifact.id>:<tool.version> -DoutputDirectory=target
> mvn dependency:copy-dependencies -f target/<tool.artifact.id>-<tool.version>.pom -DoutputDirectory=target
> cd target
> java -cp target/*;<tool.artifact.id>-<tool.version>.jar <tool.main.class>
On Unix/Linux machine in the last command the semicolon ";" must be replaced with colon ":".
When input arguments must be provided, just put them in the last script line:
> java -cp target/*;<tool.artifact.id>-<tool.version>.jar <tool.main.class> arg1 arg2 ...
u can use IDEs like Intellij idea which automatically resolve dependencies as u write them in your pom
As it has been mentioned by others already there is no solution without creating an extra POM.
One solution could be to use the Maven Shade Plugin in this POM: "This plugin provides the capability to package the artifact in an uber-jar, including its dependencies"
I think the Executable JAR is close to that what you'd like to achieve.

Categories

Resources