Skip to content

jj3l/maven-example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Maven Example

This is an example project to demonstrate Maven usage and configuration.

Project setup

  • Create README.md.
  • Generate .gitignore
  • Make initial commit with README.md and .gitignore.

Generate Maven project

mvn \
  archetype:generate \
  -DgroupId=com.github.jj3l.maven-example \
  -DartifactId=maven-example \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DinteractiveMode=false

Move to polyglot Maven with pom.yml

Replace pom.xml with less noisy pom.yml. This requires the Polyglot Maven core extension as available with Maven v3.3.1.

mvn \
  io.takari.polyglot:polyglot-translate-plugin:translate \
  -Dinput=pom.xml \
  -Doutput=pom.yml
rm pom.xml
mkdir -p .mvn
cat << 'EOF' > .mvn/extensions.xml
<?xml version="1.0" encoding="UTF-8"?>
<extensions>
  <extension>
    <groupId>io.takari.polyglot</groupId>
    <artifactId>polyglot-yaml</artifactId>
    <version>0.2.1</version>
  </extension>
</extensions>
EOF

The last line from pom.yml (pomFile: {}) must be removed.

Minimal project configurations

Maven is not able to run with minimal POM without errors and warnings. The following adjustments to the super POM are necessary:

  • Set Java version to 1.6 for the Java Compiler.
  • Specify the Version of the project info report plugin.
    build:
      plugins:
      - groupId: org.apache.maven.plugins
        artifactId: maven-project-info-reports-plugin
        version: 2.8.1
      - groupId: org.apache.maven.plugins
        artifactId: maven-compiler-plugin
        version: 3.1
        configuration:
          source: 1.6
          target: 1.6
  • Add informations required to deploy to the Maven Central Repository
    description: This is an example project to demonstrate Maven usage and configuration.
    developers:
    - id: jjaekel
      name: Jonathan Jäkel
      email: j@j3l.de
      organization: Jonathan Jäkel
      organizationUrl: 'https://www.j3l.de/'
      roles: [Developer]
      timezone: +1
    licenses:
    - name: Apache License, Version 2.0
      url: https://www.apache.org/licenses/LICENSE-2.0.txt
      distribution: repo

Create sources JAR

Add to pom.yml:

build:
  plugins:
  - groupId: org.apache.maven.plugins
    artifactId: maven-source-plugin
    executions:
    - id: attach-sources
      goals:
      - jar

Create Java-Doc JAR

Add to pom.yml:

build:
  plugins:
  - groupId: org.apache.maven.plugins
    artifactId: maven-javadoc-plugin
    executions:
    - id: attach-javadocs
      goals:
      - jar

OWASP Dependency check

The OWASP Dependency check checks all dependencies for known security risks.

Add to pom.yml:

build:
  plugins:
  - groupId: org.owasp
    artifactId: dependency-check-maven
    version: 3.0.2
    executions:
    - goals:
      - check

See https://jeremylong.github.io/DependencyCheck/dependency-check-maven/ and Automatisierte Überprüfung von Sicherheitslücken in Abhängigkeiten von Java-Projekten (german).

Enforce project model

Certain aspects of the project model can be enforced. There are even more aspect which could be enforced but are not supported by the plugin.

  • Normalization of the POM (to reduce merge conflicts, make POM better readable). Partly solved by the tidy-maven-plugin.
  • Deprecated/vulnerable dependencies/plugins could be bannend.
  • Version is compliant to semantic versioning.
  • There is a changelog compliant to keep a changelog.

Add to pom.yml:

build:
  plugins:
  - groupId: org.apache.maven.plugins
    artifactId: maven-enforcer-plugin
    version: 3.0.0-M1
    executions:
    - id: no-duplicate-declared-dependencies
      goals:
      - enforce
      configuration:
        rules:
          banDuplicatePomDependencyVersions: ''
    - id: enforce
      goals:
      - enforce
      configuration:
        rules:
          dependencyConvergence: ''

Add license informations

Add license informations to each file and add a license file.

Add to pom.yml:

build:
  plugins:
  - groupId: org.codehaus.mojo
    artifactId: license-maven-plugin
    version: 1.14
    configuration:
      licenseName: apache_v2
      addJavaLicenseAfterPackage: false
      canUpdateCopyright: true
      canUpdateDescription: true
      canUpdateLicense: true
      emptyLineAfterHeader: true
      extraFiles:
        DockerFile: properties
      processStartTag: 'LICENSE_START'
      processEndTag: 'LICENSE_END'
      sectionDelimiter: '**'
    executions:
    - id: first
      goals:
      - update-file-header
      phase: process-sources
    - id: second
      goals:
      - update-project-license      

It would be appreciated to remove processStartTag, processEndTag and sectionDelimitersectionDelimiter (the first section is not required). Trailing spaces should be removed. A LICENSE.md would be preferred over LICENSE.txt. The placeholder Copyright [yyyy] [name of copyright owner] will not be replaced.

Use jUnit 5

To switch from jUnit 4 to jUnit 5 replace

dependencies:
-- groupId: junit
   artifactId: junit
   version: 4.12

by

dependencies:
- groupId: org.junit.jupiter
  artifactId: junit-jupiter-api
  version: 5.1.1
  scope: test

in your POM. And add required dependencies to the maven-surefire-plugin:

build:
  pluginManagement:
    plugins:
    - artifactId: maven-surefire-plugin
      version: 2.19.1
      dependencies:
      - groupId: org.junit.platform
        artifactId: junit-platform-surefire-provider
        version: 1.1.1
      - groupId: org.junit.jupiter
        artifactId: junit-jupiter-engine
        version: 5.1.1

Links:

Note: To run the Tests in Eclipse with a module descriptor present it's required to add jUnit to the classpath in the Eclipse project. Eclipse provides a jUnit library for this (jUnit v5.1.0 currently).

  • Right click on Project, select "Build Path" -> "Add Libraries..." -> "JUnit" -> Click "Next". Select "JUnit library version": "JUnit 5". Eclipse will no longer show compile errors for the test class.
  • Right click on Project, select "Run As" -> "JUnit Test". Will be successful.

Switch to Java 10

You can use release instead of source and target to configure the Java version for the maven-compiler-plugin now. Unfortunately the latest release v3.7.0 of the maven-compiler-plugin is from 2017-09-01. To work with Java 10 a newer version of the asm dependency must be configured explicitly to workaround this bug.

- artifactId: maven-compiler-plugin
  version: 3.7.0
  configuration:
    release: 10
  dependencies:
  - groupId: org.ow2.asm
    artifactId: asm
    version: 6.2

Release process

One of the most powerful features of Maven is the repository concept to store packaged Java artefacts like JAR files. The default repository where dependencies are looked up is known as Maven Central Repository. By default the Maven Central Repository is available at https://repo.maven.apache.org/maven2. There are multiple mirrors like https://repo1.maven.org/maven2 or (local) company repositories.

Publish to Maven Central Repository

To publish artefacts to Maven Central some requirements need to be met.

  1. Choose a groupId as your global unique namespace. Similar to the Java package naming convention it reuses the DNS in a reversed manner. This means you need a domain name you control. This could be any registered second level Domain. But a third level domain from your GitHub account like jj3l.github.com will be sufficient. Having a groupId you have to use the groupId in your POM and to signup for Sonatype Jira and register the groupId to be used publishing artefacts in the Maven Central Repository with.
  2. Make your Sonatype Jira credentials available to Maven as they are used for OSSRH as well. Add to ~/.m2/settings.xml:
    <settings>
      <servers>
        <server>
          <id>ossrh</id>
          <username>jjaekel</username>
          <password>1password</password>
        </server>
      </servers>
    </settings>
  3. The Sonatype Jira credentials will be referenced by the id ossrh. One time from the maven-deploy-plugin via the distributionManagement releasing snapshot releases and one time from the nexus-staging-maven-plugin configuration releasing production releases (see Snapshot Artefact vs. Release Artefacts for details). Add to pom.yml:
    distributionManagement:
      snapshotRepository:
        id: ossrh
        url: https://oss.sonatype.org/content/repositories/snapshots
    build:
      plugins:
      - groupId: org.sonatype.plugins
        artifactId: nexus-staging-maven-plugin
        version: 1.6.7
        extensions: true
        configuration:
          serverId: ossrh
          nexusUrl: https://oss.sonatype.org/
          autoReleaseAfterClose: true
  4. Add to pom.yml:
    build:
      plugins:
      - groupId: org.apache.maven.plugins
        artifactId: maven-release-plugin
        version: 2.5.3
        configuration:
          autoVersionSubmodules: true
          useReleaseProfile: false
          releaseProfiles: release
          goals: deploy
  5. Generate a GPG key pair and publish the pubic key so everybody can verify the artefacts you signed with your private key.
  6. Install GPG Suite with MacGPG. The Maven GPG plugin does not have a Java GPG implementation and needs the gpg executeable on the PATH.
  7. Generate GPG key pair:
    gpg --batch --gen-key << EOF
    Key-Type: RSA
    Key-Length: 4096
    Subkey-Type: RSA
    Subkey-Length: 4096
    Name-Real: Jonathan Jäkel
    Name-Email: j@j3l.de
    Expire-Date: 0
    Passphrase: <1password>
    EOF
  8. Get key ID:
    gpg --list-keys
    pub   rsa4096 2017-12-03 [SCEA]
          183303E02CCE0907450FE9370046FC88CB105E68 <-- Key ID
  9. Publish public key
    gpg --keyserver hkp://pgp.mit.edu --send-keys 183303E02CCE0907450FE9370046FC88CB105E68
  10. Configure the Maven GPG Plugin in your pom.yml:
    build:
      plugins:
      - groupId: org.apache.maven.plugins
        artifactId: maven-gpg-plugin
        executions:
        - id: sign-artifacts
          phase: verify
          goals:
          - sign
          configuration:
            keyname: ${gpg.keyname}
            passphraseServerId: ${gpg.keyname}
  11. To sign the GPG key passphrase has to specified as command line argument mvn verify -Dgpg.passphrase=thephrase or it will be prompted for. Even better would be to define the passphrase in the settings.xml. Add to ~/.m2/settings.xml:
    <settings>
      <servers>
        <server>
          <id>183303E02CCE0907450FE9370046FC88CB105E68</id>
          <passphrase>1password</passphrase>
        </server>
      </servers>
      <profiles>
        <profile>
          <activation>
            <activeByDefault>true</activeByDefault>
          </activation>
          <properties>
            <gpg.keyname>183303E02CCE0907450FE9370046FC88CB105E68</gpg.keyname>
          </properties>
        </profile>
      </profiles>
    </settings>
    You can sign explicitly by running JAVA_HOME=$(/usr/libexec/java_home -v 10) mvn clean verify but you will sign regularly implicit using JAVA_HOME=$(/usr/libexec/java_home -v 10) mvn release:perform later. You will find the generated signatures in target/maven-example-*.*.asc.
  12. Using semantic versioning is not required but recommended.

Snapshot Artefact vs. Release Artefacts

Maven distinguish between snapshot and production releases. Production releases use common versioning schemes ( semantic versioning in best case). Snapshot releases use the same versioning scheme but add a -SNAPSHOT suffix. A snapshot release prepares the production release with the same prefix. Whereas a production release denotes a immutable version with certain assertion like QA process or semantic versioning the snapshot release can be assigned to multiple versions as floating tag.

Publish Snapshot Artefacts

Artifact build on a snapshot version can be deployed to the Sonatype snapshot repository but will not be synchronized to Maven Central. The version in the pom.yml will not be changed and no tag at GitHub will be set. For the artifacts stored in the repository the -SNAPSHOT suffix will be replaced with a timestamp. Maven references the latest timestamp for a given SNAPSHOT automatically.

For snapshot releases the default Maven process is used. The snapshot repository is configured under distributionManagement which will be looked up by the maven-deploy-plugin.

To deploy a snapshot version run:

JAVA_HOME=$(/usr/libexec/java_home -v 10) mvn clean deploy

Publish Release Artefacts

To issue a production release some more steps are required as it must be switched from current snapshot version to a production release version (remove the -SNAPSHOT suffix) and the next snapshots version must be bumped. Additionally there may be rules like reviews and QA for the release process as for Maven Central. The QA is based on the final release artifacts and can not be done before having the release artifacts available. But if the QA fails the release artifacts should not be published.

For this reason it's not possible to publish into the Maven Central repository directly. Rather you have to publish your production release artifacts into the Sonatype Open Source Software Repository Hosting (OSSRH). The OSSRH make use of staging releases as a Nexus Repository Pro feature. A staging release is a temporary staging repository with the artifacts of a release candidate. The release candidate artifacts can be reviewed and discarded or promoted to the final release repository. Only the final release repository will be synchronized to the Maven Central Repository.

This makes it difficult to deploy production release artefacts to Maven Central using the maven-deploy-plugin. Nexus introduces the nexus-staging-maven-plugin for this case(reference). Unfortunately the nexus-staging-maven-plugin does not recognize the distributionManagement. So the URL of the OSSRH repository must be configured with the plugin configuration (as shown above).

To deploy a production version run:

git diff-index --quiet HEAD -- || echo 'There are local modifications!'
sed -i '' -E 's/^(version: ([0-9]{1,}\.){1,}[0-9]{1,})(.*)/\1/' pom.yml
grep '\-SNAPSHOT' pom.yml && echo 'There are SNAPSHOT dependencies!'
JAVA_HOME=$(/usr/libexec/java_home -v 10) mvn test
JAVA_HOME=$(/usr/libexec/java_home -v 10) mvn site-deploy
git add pom.yml
git commit -m 'Create production release'
git push
git tag "v$(grep '^version:' pom.yml|sed -E 's/^version: (.*)/\1/')"
git push origin "v$(grep '^version:' pom.yml|sed -E 's/^version: (.*)/\1/')"
JAVA_HOME=$(/usr/libexec/java_home -v 10) mvn deploy
# Change version number to next relase manually.
sed -i '' -E 's/^version: .*/&-SNAPSHOT/' pom.yml
git add pom.yml
git commit -m 'Bump to next snapshot release'
git push

Login at https://oss.sonatype.org/ with your Sonatype Jira credentials. Go to stagingRepositories, select your distributes staging repository and click Release button.

The disqualification of the maven-release-plugin

The maven-release-plugin does not work together with polyglot Maven(reference).

There is the criticism the maven-release-plugin tries to make things simpler than they are(reference). A release process has a intrinsic complexity which can not be eliminated by a tool.

Releases have to be carefully planned. They have to be announced and there may be strategies required to handle incompatible changes. And there may be tradeoffs to be considered (e.g tollerate downtimes vs. migration costs). Last but not least multiple conventions apply (like semantic versioning) and a QA process is required.

So what you want to do would be probably something like this:

  1. Checkout a clean copy of the current master branch.
  2. Ensure there are no local modifications.
  3. Remove the -SNAPSHOT suffix from the version in the POM. Commit and push the changes.
  4. Ensure there are no other snapshot dependencies as they are floating tags and you can't guarantee the released software will later behave as you tested.
  5. Run tests.
  6. Build and publish the project website.
  7. Tag a GitHub release.
  8. Upload the production release artifacts to the production release repository.
  9. Bump to a new snapshot version. Commit and push the changes.

Links

Deploy project website to GitHub repository

You need an OAuth2 access token to allow Maven to publish the project website to GitHub on a behalf of you. Each commit will be done in your name. Consider using a dedicated "bot" user for this.

  1. Login at https://github.com/ with your user.
  2. Go to "Settings" -> "Developer settings" -> "Personal access tokens"
  3. Click on "Generate new token".
  4. Choose a token description like "Maven bot to publish project website."
  5. Select at least public_repo and user:emailuser:email scope. This limits the access to the minimum needed to publish the website at GitHub (Documentation).

Write the token into your settings.xml:

cat << 'EOF' > ~/.m2/settings.xml
<settings
  xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd"
>
  <servers>
    <server>
      <id>github</id>
      <password>GENERATED_TOKEN</password>
    </server>
  </servers>
</settings>
EOF

There are two things important here:

  1. The credentials will be referenced by the ID github.
  2. Through the absence of an username the password is interpreted as OAuth2 token.

See https://developer.github.com/apps/building-integrations/setting-up-and-registering-oauth-apps/registering-oauth-apps/ for details to register an oauth2 app.

The maven-site-plugin is not able to to push the generated site into a git repository. But GitHub provides a plugin for this. Skip site deployment by the maven-site-plugin and configure the GitHub plugin to push the site to GitHub in pom.yml:

  - groupId: org.apache.maven.plugins
    artifactId: maven-site-plugin
    version: 3.4
    configuration:
      skipDeploy: true
  - groupId: com.github.github
    artifactId: site-maven-plugin
    version: 0.12
    executions:
    - goals:
      - site
      phase: site-deploy
      configuration:
        server: github
        message: Create project website for ${project.version}.

The maven-site-plugin is available at Maven Central and refers https://repo.eclipse.org/content/repositories/egit-releases/ as external repository which is discouraged and needs a workaround on CircleCI. As a workaround the external repository must be referenced by the project using the maven-site-plugin. So we have to add additionally in pom.yml:

# Required by com.github.github:github-maven-plugins-parent
# https://github.com/github/maven-plugins/blob/master/pom.xml
repositories:
- id: egit
  name: Eclipse egit
  url: https://repo.eclipse.org/content/repositories/egit-releases/

Unfortunately GitHub plugin does not use the /project/distributionManagement/site information like the maven-site-plugin. Instead the project URL, the SCM metadata or an explicit configuration is used (first wins)

For the project URL this pattern should be used: https://github.com/REPOSITORY_OWNER/REPOSITORY_NAME But better would be to use the SCM metadata and point the project URL to https://REPOSITORY_OWNER.github.io/REPOSITORY_NAME. This is the location there the generated project website will be found.

Weblinks:

Create Site Descriptor

Run mvn site:effective-site to view the default site descriptor:

<project
  xsi:schemaLocation="http://maven.apache.org/DECORATION/1.7.0 http://maven.apache.org/xsd/decoration-1.7.0.xsd"
  name="maven-example"
  xmlns="http://maven.apache.org/DECORATION/1.7.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 >
  <bannerLeft>
    <name>maven-example</name>
  </bannerLeft>
  <publishDate />
  <version />
  <body>
    <links>
      <item name="maven-example" href="./" />
    </links>
    <menu ref="parent" />
    <menu ref="reports" />
  </body>
</project>

Add a contemporary skin built on Bootstrap:

  <skin>
    <groupId>org.apache.maven.skins</groupId>
    <artifactId>maven-fluido-skin</artifactId>
    <version>1.5</version>
  </skin>

There is another skin built on Bootstrap. But even this one was last edited three years ago and has some issues with TLS resources. So best for now will be to choose maven-fluido-skin.

See Site Descriptor documentation for further details.

Since v3.3 of the maven-site-plugin Markdown syntax is supported without declaring a Doxia dependency explicitly. Add a Markdown document to src/site/markdown. With an additional suffix .vm filtering is supported (resolve e.g. ${project.name}).

Configure Reports

The maven-project-info-reports-plugin genartes the default reports under "Project Documentation" -> "Project Information" in the left navigation.

  1. Dependencies
  2. Dependency Information
  3. Distribution Management
  4. About
  5. Project License
  6. Plugin Management
  7. Project Plugins
  8. Project Team
  9. Source Repository
  10. Project Summary

Additional reports configured will be shown under "Project Documentation" -> "Project Reports" in the left navigation.

versions-maven-plugin

Reports available updates of Properties, Plugins and Dependencies.

CircleCI

Run tests at CircleCI. See .circleci/config.yml for CircleCI configuration.

Run the app as modular JAR

java \
--module-path target/maven-example-1.1.0-SNAPSHOT.jar \
-m com.github.jj3l.maven.example/com.github.jj3l.maven.example.App
``

## Create a Custom-Runtime-Image and run the app as jImage

Create Custom-Runtime-Image:

```bash
$(/usr/libexec/java_home -v 10)/bin/jlink \
--module-path target/maven-example-1.1.0-SNAPSHOT.jar \
--add-modules com.github.jj3l.maven.example \
--launcher app=com.github.jj3l.maven.example/com.github.jj3l.maven.example.App \
--strip-debug \
--compress=2 \
--output target/maven-example-1.1.0-SNAPSHOT

Run the app:

target/maven-example-1.1.0-SNAPSHOT/bin/app

There is a Maven plugin to create Modular Runtime Images. The plugin has alpha status currently and need a multi module build.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages