Best practices Maven archetypes
This project maintains some Maven archetypes.
fr.ebruno.maven.archetypes:simple
a simple standalone Java project.fr.ebruno.maven.archetypes:simple-withparent
a CI ready one with a complete parent POM.
An archetype to generate a simple standalone maven project.
To generate from the archetype :
mvn archetype:generate \
-DarchetypeGroupId=fr.ebruno.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-simple
parameters can be passed directly :
mvn archetype:generate \
-DarchetypeGroupId=fr.ebruno.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-simple \
-DgroupId=demo.pkg \
-DartifactId=MyApp \
-Dversion=0.1.0-SNAPSHOT
In in the project directory, a maven wrapper should be generated to control Maven version for this project.
cd MyApp
mvn wrapper:wrapper
The result can be compiled with maven
cd MyApp
./mvnw verify
and executed with maven :
./mvnw exec:java -Dexec.mainClass="demo.pkg.App"
but as the default App
class is defined in a property in the ``pom.xml`, this could be simply :
./mvnw exec:java"
The jar can be launch with java but the slim jar fails because of missing dependencies.
java -cp target/MyApp-0.1.0-SNAPSHOT.jar demo.pkg.App
java -jar target/MyApp-0.1.0-SNAPSHOT.jar
A complete packaging (javadoc jar and shadedjar) can be done with :
./mvnw --activate-profiles javadoc,shadedjar verify
java -cp target/MyApp-0.1.0-SNAPSHOT-withdependencies.jar demo.pkg.App
java -jar target/MyApp-0.1.0-SNAPSHOT-withdependencies.jar
Thanks to Jlink and a modular java project (jigsaw), it is possible to generate a minimal JRE with needed modules and dependencies.
./mvnw --activate-profiles javadoc,jlink clean verify
du -hs target/image
It is interesting to compare sizes of the shaded jar, the Jlink distribution (with an embeded JRE)
du -hs target/*-withdependencies.jar
du -hs target/image
a web site can be generated in target/site/index.html
with :
mvn site
mvn archetype:generate \
-DarchetypeGroupId=fr.ebruno.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-withparent \
-DgroupId=demo.pkg \
-DartifactId=MyApp \
-Dversion=0.1.0-SNAPSHOT
A parent pom is defined.
By default the jar is “runnable” (see app.main.class
in pom.xml
)
cd Myapp
mvn verify
java -jar target/MyApp-0.1.0-SNAPSHOT.jar
WARNING: STILL WORK IN PROGESS
This project generates a complete Java+Maven project ready for Continuous Integration (CI). It is ready for GitFlow, SonarQube (tests, code coverage, …). It can produce signed artifacts, fat jars, slim runtime with jLink, native executables with GraalVM and container images. The build itself can also be done in a container.
The configuration is done with environment variables. For GitHub : GITHUBORG (GitHub account or organisation), GITHUBACTOR, GITHUBTOKEN, GITHUBORG (org or account) and optionally for SonarQube SONAR_URL and SONAR_TOKEN (Needs an instance of SonarQube)
For GitHub the CLI is needed.
Those variables have to be stored on the CI server (see GitHub Encrypted secrets). The script below transforms the local variables in GitHub secrets.
bash -c 'for secret in GITHUBACTOR GITHUBTOKEN SONAR_URL SONAR_TOKEN; do \
eval gh secret set $secret --app actions \
--body ${!secret} \
--org ${GITHUBORG} \
--visibility all; \
done'
NOTE: Short version, a wrapper script is provided (READ IT BEFORE USE).
source <(curl -s https://raw.githubusercontent.com/ebpro/demomavenarchetype/develop/src/main/resources/archetype-resources/ci-wrappers.sh)
new-java-project <projectName> <groupId>
Generate each new project with this maven archetype (adapt at least the bold parameters).
mvn --batch-mode archetype:generate \
-DarchetypeGroupId=fr.ebruno.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-withparent \
-DgithubAccount=<b>$GITHUBORG</b> \
-DgroupId=<b>demo.pkg</b> \
-DartifactId=<b>MyApp</b> \
-Dversion=0.1.0-SNAPSHOT
To enable deployment in the repo during the CI a deployment key is needed for each repository.
tmpKeydir=$(mktemp --directory /tmp/ci-wrappers.XXXXXX)
ssh-keygen -q -t ed25519 -C "An autogenerated deploy key" -N "" -f ${tmpKeydir}/key
gh repo deploy-key add --allow-write "${tmpKeydir}/key.pub"
gh secret set SECRET_DEPLOY_KEY < "${tmpKeydir}/key"
rm -rf tmpKeydir
- Initialize git environment for GitFlow (develop and master branches).
- Make a first commit.
- Create the GitHub repository with the GitHub Command line Interface.
- Create an orphan gh-pages branch for the website.
- Open the repository in a web browser.
git flow init -d && touch README.md && git add . && git commit -m "sets initial release." && \
gh repo create ${GITHUBORG}/${PWD##*/} --disable-wiki --private --source=. --push && \
git checkout --orphan gh-pages && \
git rm -rf . && touch index.html && \
git add index.html && \
git commit -m "sets initial empty site." && \
git push --all \
&& git checkout develop && \
gh repo view --web
The project uses the gitflow-maven-plugin to manage GitFlow for java+Maven projet (branches and artifact version).
It is possible to easily start and finish a feature (see version in pom.xml).
mvn -B gitflow:feature-start -DpushRemote=true -DfeatureName=UI
mvn -B gitflow:feature-start -DpushRemote=true -DfeatureName=DB
mvn -B gitflow:feature-finish -DpushRemote=true -DfeatureName=DB
mvn -B gitflow:feature-finish -DpushRemote=true -DfeatureName=UI
mvn -B gitflow:release-start -DpushRemote=true -DallowSnapshots=true -DuseSnapshotInRelease=true
mvn -B gitflow:release-finish -DpushRemote=true -DallowSnapshots=true
You can compile and package with unit tests.
mvn package
You can compile and package with unit tests and integration tests.
mvn verify
You can execute with maven (see app.mainClass property in pom.xml).
mvn exec:java
The profile shadedjar
generates a fat jar with all the classes from the transitive dependencies.
mvn -Pshadedjar clean verify
ls -lh target/*.jar
-rw-r--r--@ 1 bruno wheel 3.1K May 20 16:09 target/MyApp-0.1.0-UI-SNAPSHOT-sources.jar
-rw-r--r--@ 1 bruno wheel 2.6K May 20 16:09 target/MyApp-0.1.0-UI-SNAPSHOT-test-sources.jar
-rw-r--r--@ 1 bruno wheel 38K May 20 16:09 target/MyApp-0.1.0-UI-SNAPSHOT-withdependencies.jar
-rw-r--r--@ 1 bruno wheel 4.2K May 20 16:09 target/MyApp-0.1.0-UI-SNAPSHOT.jar
The application can then be executed without maven :
java -jar target/*-withdependencies.jar
Thanks to Jlink and a modular java project (jigsaw), it is possible to generate a minimal JRE with needed modules and dependencies.
mvn -Pjlink clean verify
du -hs target/image
44M target/image
The application can then be launched without a JRE installed.
./target/image/bin/myapp
Dec 02, 2022 11:18:56 PM fr.univtln.bruno.demos.App main
INFOS: Hello World! []
It is also possible to generate a native binary with GraalVM. An installation of GraalVM is needed and the package build-essential libz-dev and zlib1g-dev (zlib-devel, zlib-static et glibc-static). Only tested in linux.
sdk install java 22.3.r19-grl
sdk use java 22.3.r19-grl
mvn -Pnative clean verify
ls -lh target/testci
-rwxr-xr-x 1 bruno users 13M 3 déc. 00:12 target/testci
./target/testci
déc. 03, 2022 12:15:25 AM fr.univtln.bruno.demos.App main
INFO: Hello World! []
It is possible to build and run the project with just docker installed. A wrapper to run maven and java in a container but to work with the current directory is proposed. ~/.m2, ~/.ssh, ~/.gitconfig and the src directories are mounted. The environment variables needed for the project are also transmitted. The UID and GID are the one of the current user.
docker-mvn clean -P shadedjar package
docker run --rm \
--mount type=bind,source="$(PWD)"/target/,target=/app,readonly \
eclipse-temurin:17-jre-alpine \
sh -c "java -jar /app/*-withdependencies.jar"
The file docker\Dockerfile
is a multistage Dockerfile to build and deliver the application with several strategies (shaded jar, jlink, GraalVM) on several distributions (debian and alpine). To ease the use a wrapper for docker commands is provided in dockerw.sh
. ./ci-wrappers.sh
docker-wrapper-build
docker-wrapper-run
It is also possible to build all final target (Warning graalvm takes a long time to compile).
docker-wrapper-build-all
the result show the images and their size.
ebpro/testci develop-finalShadedjarDebian af3e072b35f7 2 hours ago 266MB
ebpro/testci develop-finalShadedjarAlpine 38973a2aa588 2 hours ago 170MB
ebpro/testci develop-finaljLinkDebian db847ca5b281 2 hours ago 133MB
ebpro/testci develop-finalJLinkAlpine 0cba42a81a33 2 hours ago 58.2MB
ebpro/testci develop-finalGraalvmDebian f5607a1e055f 2 hours ago 93.7MB
ebpro/testci develop-finalGraalvmAlpine d9c0573e4750 2 hours ago 18.8MB
It is also possible to run all the images :
docker-wrapper-run-all
INFO: Hello World! []
0,06s user 0,04s system 6% cpu 1,529 total
Running ebpro/testci:develop-finalShadedjarAlpine
Dec 05, 2022 4:37:06 PM fr.univtln.bruno.demos.App main
INFO: Hello World! []
0,05s user 0,05s system 5% cpu 1,724 total
Running ebpro/testci:develop-finaljLinkDebian
Dec 05, 2022 4:37:07 PM fr.univtln.bruno.demos.App main
INFO: Hello World! []
0,05s user 0,05s system 5% cpu 1,690 total
Running ebpro/testci:develop-finalJLinkAlpine
Dec 05, 2022 4:37:09 PM fr.univtln.bruno.demos.App main
INFO: Hello World! []
0,04s user 0,05s system 5% cpu 1,723 total
Running ebpro/testci:develop-finalGraalvmDebian
Dec 05, 2022 4:37:10 PM fr.univtln.bruno.demos.App main
INFO: Hello World! []
0,05s user 0,03s system 7% cpu 1,161 total
Running ebpro/testci:develop-finalGraalvmAlpine
Dec 05, 2022 4:37:12 PM fr.univtln.bruno.demos.App main
INFO: Hello World! []
If a sonarqube server is available (i.e with https://github.com/ebpro/sonarqube). Set the variable SONAR_URL and SONAR_TOKEN
mvn -P jacoco,sonar \
-Dsonar.branch.name=$(git rev-parse --abbrev-ref HEAD | tr / _) \
verify sonar:sonar
mvn clean verify
mvn -DskipTests=true \
-Dsonar.branch.name=$(git rev-parse --abbrev-ref HEAD | tr / _) \
-P jacoco,sonar \
sonar:sonar
mvn site:site