Build lifecycle

Logging Parent extends the standard Maven default (build) lifecycle that runs on a plain

./mvnw verify

with three kinds of additions:

Some of these additions come from Logging Parent itself, some are inherited from the ASF Parent POM, and some are activated only when a particular activator file is present in your project. Each origin is called out below.

Beyond a plain ./mvnw verify, the parent also offers two opt-in mechanisms:

Overview

The following table maps each addition to the Maven phase it binds to. Everything listed here is triggered by a plain ./mvnw verify, unless it is marked as conditional on an activator file.

Phase Origin Additions

validate

ASF Parent POM

validate

Logging Parent

generate-resources

ASF Parent POM

process-resources

Logging Parent

compile

Logging Parent

process-classes

Logging Parent

package

Logging Parent

verify

Logging Parent

Additional checks

The checks below run automatically during ./mvnw verify. The trickier ones link to a debugging recipe in Troubleshooting.

Maven & Java version

Phase: validate. Enforced by the ASF Parent POM.

To build an Apache Logging Services project you need:

  • a JDK 17 (the build pins the major version 17, not a later one), and

  • Maven 3.8.1 or later, most conveniently through the bundled Maven Wrapper (./mvnw).

Both requirements are checked by the maven-enforcer-plugin inherited from the ASF Parent POM, through its requireJavaVersion and requireMavenVersion rules. These rules can be modified using the properties below:

Property Controls Default

minimalJavaBuildVersion

Range of JDK versions allowed to run the build

[17,18)

minimalMavenBuildVersion

Range of Maven versions allowed to run the build

3.8.1

maven.compiler.release

Java bytecode level the sources are compiled to

8

Although most artifacts still target Java 8 bytecode (through the javac --release 8 flag), JDK 17 is required to run the Maven plugins in use and to keep builds reproducible. Bytecode generation can differ between JDK major versions, so pinning a single one keeps the output byte-for-byte stable.

JDK 17 also requires the --add-exports and --add-opens flags declared in .mvn/jvm.config, needed by tools that reach into JDK compiler internals (such as Error Prone). See Usage.

Some projects (currently only Log4j) additionally run their test suites against a real JDK 8 through Maven Toolchains in CI. This proves runtime compatibility with the Java 8 platform, beyond the bytecode and API-surface guarantees that --release 8 provides on its own.

Order-independent dependency resolution

Phase: validate.

Maven’s default conflict mediation (nearest-wins, ties broken by declaration order) can resolve a dependency to a version lower than some transitive dependency requires, and the outcome can shift when dependencies are reordered. The maven-enforcer-plugin requireUpperBoundDeps rule forbids this: it fails whenever a resolved version is lower than the highest one required across the graph.

The fix is to pin that dependency to at least the highest required version, making resolution order-independent. See the Order-independent dependency resolution recipe for how to read the error and adjust the pin.

Pinning a transitive dependency in your <dependencyManagement> only affects your build. It has no influence on the versions resolved by downstream consumers of your artifacts.

Code style

Code style is enforced by the spotless-maven-plugin in the verify phase, with one exception: the ban on wildcard imports is a maven-enforcer-plugin rule that runs earlier, in the validate phase. The following rules apply:

All files

Line endings are normalized to UNIX (LF) and the ASF license header is required. Set a .gitattributes file so that Git checks files out with LF on every platform. For example:

*.properties text lf
*.java       text lf
*.yaml       text lf
*.yml        text lf
*.xml        text lf
# Only this file needs to have CRLF line endings
/mvnw.cmd text crlf
Java

Sources follow the Palantir Java format, and imports must be explicit (wildcard imports are banned).

XML, POM, .properties & YAML

pom.xml is sorted with the Spotless sortPom step; XML and YAML files also have trailing whitespace trimmed and a final newline enforced.

Most violations are fixed automatically by:

./mvnw spotless:apply

Anything the formatter cannot fix must be corrected by hand.

The Spotless configuration must stay in sync with the project’s .editorconfig.

Changelog validation

Phase: validate. Activated by src/changelog/.

Apache Logging Services projects track their release notes as individual XML files under src/changelog/, managed by the log4j-changelog-maven-plugin. When that directory is present, the xml-maven-plugin validates every entry against the Log4j Changelog schema, so a malformed entry fails the build early instead of breaking release-note generation later.

The schema is documented on the Log4j XML schema page.

Static analysis

Two static analysis tools scan the code on every build.

Error Prone

Phase: compile. Bundled into the maven-compiler-plugin (alongside -Xlint:all), it catches common Java mistakes as the code compiles. Its checks are fixed by Logging Parent and are not configurable per project.

SpotBugs + FindSecBugs

Phase: verify. The spotbugs-maven-plugin runs SpotBugs together with the FindSecBugs plugin for security-oriented bug patterns. False positives can be suppressed by adding a spotbugs-exclude.xml file to the project root; its presence alone activates the exclusion filter.

License validation

Phase: verify.

The apache-rat-plugin verifies that the project complies with the ASF Source Header and Copyright Notice Policy, failing the build when a file is missing the required ASF license header.

Files that legitimately cannot carry a header or have been exempted from carrying one have been added to an exclude list.

API compatibility

Phase: verify.

Public APIs must not change without an explicit version bump. The bnd-baseline-maven-plugin compares each module’s exported packages against their last release and enforces OSGi semantic versioning: a backward-compatible addition needs a minor increment, a breaking change a major one, and an internal-only fix a micro one. Package versions are declared with the @Version annotation in each package’s package-info.java.

When a change is not matched by the right increment, the build fails with a table reporting the detected delta (MICRO, MINOR, or MAJOR) and the version the plugin suggests. The rules for choosing the increment, and how to resolve such a failure, are explained in the API compatibility recipe.

Additional generated artifacts

Beyond the main JAR, the build attaches or embeds the following.

OSGi manifest

Phase: process-classes.

The bnd-maven-plugin generates an OSGi META-INF/MANIFEST.MF and hands it to the maven-jar-plugin, which packages it instead of its own default manifest. BND derives the manifest from the compiled bytecode:

  • Bundle-SymbolicName is computed from the groupId and artifactId;

  • Export-Package lists the packages annotated with org.osgi.annotation.bundle.Export in their package-info.java;

  • Import-Package is inferred from the packages the code actually references.

This automatic inference is correct in the vast majority of cases, but a few situations must be declared explicitly, for example an optional dependency, whose import BND marks as mandatory unless told otherwise. Such cases can be tuned through the following properties, without redefining the whole BND configuration:

Property Purpose Default

bnd-extra-package-options

Extra Import-Package instructions.

(empty)

bnd-bundle-symbolicname

Override the computed Bundle-SymbolicName

derived from groupId and artifactId

bnd-multi-release

Value of the Multi-Release manifest header

false

bnd-extra-config

Arbitrary extra BND instructions, appended last.

(empty)

For the full set of bnd-* properties and worked examples, see Usage.

JPMS module descriptor

Phase: process-classes.

The same bnd-maven-plugin execution that produces the OSGi manifest also synthesizes a JPMS module-info descriptor from the same metadata. This gives each module a proper, stable module name and its requires, exports, provides, and uses directives without a hand-written module-info.java, which could not target Java 8 anyway.

The module name defaults to the Bundle-SymbolicName, and the requires directives are inferred from the imported packages. When that inference needs adjusting, use the following properties:

Property Purpose Default

bnd-module-name

Override the generated JPMS module name

the Bundle-SymbolicName

bnd-extra-module-options

Refine the computed requires directives (see the BND JPMS options); use only when bnd-extra-package-options cannot express the change

(empty)

To keep incremental recompiles working, a maven-clean-plugin execution deletes any stale module-info.class from the output directory before each compilation. This works around a javac limitation and is not something you need to configure.

The same deletion, however, makes the classes folder look modified on every invocation. Use -Dbnd.skip to suppress the BND processing when you do not need to modify the descriptors.

ServiceLoader descriptor

Phase: process-classes.

java.util.ServiceLoader requires a provider-registration file under META-INF/services/ and, on the module path, matching provides and uses directives in module-info. Rather than maintaining these by hand, Logging Parent lets BND generate them from two annotations:

aQute.bnd.annotation.spi.ServiceProvider

Placed on a service implementation, it registers the class in the appropriate META-INF/services/<service> file and adds a provides <service> with <implementation> directive to the JPMS descriptor.

aQute.bnd.annotation.spi.ServiceConsumer

Placed on a type that looks services up through ServiceLoader, it adds the matching uses <service> directive to the JPMS descriptor.

Driving both the class-path (META-INF/services/) and module-path (module-info) registrations from a single annotation keeps the two representations from drifting apart.

CycloneDX SBOM

Phase: package.

The cyclonedx-maven-plugin generates a CycloneDX Software Bill of Materials (SBOM) for every module, describing its resolved dependencies. Each SBOM is attached as an XML artifact with the cyclonedx classifier (<artifactId>-<version>-cyclonedx.xml) and carries a vulnerability-assertion external reference to the shared Apache Logging Services VDR. See features.adoc#cyclonedx-sbom for details.

Embedded LICENSE, NOTICE, and DEPENDENCIES

Phase: generate-resources.

The maven-remote-resources-plugin, configured by the ASF Parent POM and its shared resource bundle, contributes three files under the META-INF/ directory of every produced JAR:

META-INF/LICENSE

the Apache License 2.0 text;

META-INF/NOTICE

the artifact’s attribution notices;

META-INF/DEPENDENCIES

a list of the module’s transitive dependencies, grouped by organization and annotated with their licenses.

META-INF/DEPENDENCIES is always generated from the resolved dependency tree. META-INF/LICENSE and META-INF/NOTICE, on the other hand, are only defaults: a module’s own src/main/resources/META-INF/LICENSE or META-INF/NOTICE takes precedence, so a module that records third-party attributions ships its curated files instead of the generated ones.

Artifact transformations

POM flattening

Phase: process-resources.

The POM you write is not the POM that gets published. Logging Parent uses CI-friendly versioning, so the project version is the ${revision} property, and a POM still holding an unresolved ${revision} is not consumable by Maven. The flatten-maven-plugin therefore rewrites the POM into a .flattened-pom.xml, which is the file actually installed and deployed. The rewrite runs in one of two modes:

Default (resolveCiFriendliesOnly)

Only the CI-friendly properties (${revision} and friends) are resolved; the rest of the POM, including its parent, is preserved as-is.

BOM (bom)

Activated by .logging-parent-bom-activator. The POM is reduced to a self-contained Bill of Materials: besides resolving the version, the parent, build, properties, and profiles sections are removed, leaving only the managed dependencies. This keeps consumers of the BOM free of unrelated build configuration.

A BOM must therefore not reference a property defined solely in an ancestor POM: bom-mode interpolation resolves variables from the module’s own POM only, so an inherited property would be left unresolved after flattening.

Optional lifecycle extensions

The following profiles are not activated automatically; you enable them on the command line to add behavior to an otherwise normal build.

-Prelease

Generates the additional artifacts a release requires: it attaches the -sources JAR and enforces that neither the project version nor any of its dependencies is a SNAPSHOT.

Both conditions must hold: the project version itself must be a release version, and every dependency must be a non-SNAPSHOT version.

Apache Logging Service projects do not use the ASF Parent POM’s apache-release profile; this release profile, together with the deploy or distribution commands, replace it.
-Pcoverage

Runs the jacoco-maven-plugin to collect test coverage during the build and write an HTML report under target/site/jacoco.

-Dapache.snapshots

Activates the ASF Parent POM’s use-apache-snapshots profile, adding the Apache snapshots repository. It lets you build only some modules of a project and resolve the rest from the regularly published SNAPSHOT artifacts, instead of building every upstream module locally.

Additional commands

A few profiles are not lifecycle extensions but commands: each carries a defaultGoal, so enabling the profile without naming a phase runs a single task and nothing else. They are normally invoked by the reusable deployment workflows, but can also be run by hand.

./mvnw -Pchangelog-release

Moves the pending changelog entries from src/changelog/.<major>.x.x/ into the released version’s directory and regenerates the release notes.

./mvnw -Pdistribution

Builds the source (src.zip) and binary (bin.zip) distribution archives from the Git-tracked files. Requires the attachmentFilepathPattern and attachmentCount properties and a prior package.

./mvnw -Pdeploy

Signs the artifacts and deploys them to the Apache Nexus staging repository via the nexus-staging-maven-plugin (skipping tests, SpotBugs, and Spotless).