cowwoc commented on code in PR #389:
URL:
https://github.com/apache/maven-build-cache-extension/pull/389#discussion_r3291944534
##########
src/site/markdown/how-to.md:
##########
@@ -160,6 +160,172 @@ Add `executionControl/runAlways` section:
</executionControl>
```
+### Default Reconciliation Behavior
+
+The build cache extension automatically tracks certain critical plugin
properties by default, even without explicit
+`executionControl` configuration. These defaults are loaded from
`default-reconciliation/defaults.xml`:
Review Comment:
Good catch. The old reference:
```text
default-reconciliation/defaults.xml
```
was stale; the defaults are now derived from the plugin parameter
descriptors under:
```text
plugin-parameters/maven-compiler-plugin.xml
plugin-parameters/maven-install-plugin.xml
```
I updated this section to point to those descriptors.
##########
src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java:
##########
@@ -422,8 +430,18 @@ boolean isParamsMatched(
} catch (IllegalAccessException e) {
LOGGER.error("Cannot extract plugin property {} from mojo {}",
propertyName, mojo, e);
return false;
+ } catch (Exception e) {
+ // Catch all exceptions including NullPointerException when
property doesn't exist in mojo
+ LOGGER.warn(
+ "Property '{}' not found in mojo {} - treating as
null",
+ propertyName,
+ mojo.getClass().getSimpleName());
+ currentValue = "null";
}
Review Comment:
Addressed in 25e09ea: unexpected extraction failures are no longer treated
as:
```text
null
```
They log and return false, so the cache consistency check falls back to a
full build. The existing handling for:
```java
IllegalAccessException
```
remains unchanged.
##########
src/main/java/org/apache/maven/buildcache/xml/PluginParameterLoader.java:
##########
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.buildcache.xml;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import
org.apache.maven.buildcache.xml.PluginParameterDefinition.GoalParameterDefinition;
+import
org.apache.maven.buildcache.xml.PluginParameterDefinition.ParameterDefinition;
+import org.apache.maven.buildcache.xml.PluginParameterDefinition.ParameterType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * Loads plugin parameter definitions from classpath resources.
+ * Definitions are stored in
src/main/resources/plugin-parameters/{artifactId}.xml
+ */
+public class PluginParameterLoader {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(PluginParameterLoader.class);
+ private static final String PARAMETER_DIR = "plugin-parameters/";
+
+ private final Map<String, PluginParameterDefinition> definitions = new
ConcurrentHashMap<>();
+
+ /**
+ * Load parameter definitions for a plugin by artifact ID only (no version
matching)
+ */
+ public PluginParameterDefinition load(String artifactId) {
+ return load(artifactId, null);
+ }
+
+ /**
+ * Load parameter definitions for a plugin by artifact ID and version.
+ * If version is provided, finds the best matching definition (highest
minVersion <= actual version).
+ * If version is null, returns any definition for the artifactId.
+ */
+ public PluginParameterDefinition load(String artifactId, String
pluginVersion) {
+ String cacheKey = artifactId + (pluginVersion != null ? ":" +
pluginVersion : "");
+
+ if (definitions.containsKey(cacheKey)) {
+ return definitions.get(cacheKey);
+ }
+
+ String resourcePath = PARAMETER_DIR + artifactId + ".xml";
+ InputStream is =
getClass().getClassLoader().getResourceAsStream(resourcePath);
+
+ if (is == null) {
+ LOGGER.debug("No parameter definition found for plugin: {}",
artifactId);
+ return null;
+ }
+
+ try {
+ java.util.List<PluginParameterDefinition> allDefinitions =
parseDefinitions(is, artifactId);
+
+ PluginParameterDefinition bestMatch =
findBestMatch(allDefinitions, pluginVersion);
+
+ if (bestMatch != null) {
+ definitions.put(cacheKey, bestMatch);
+ LOGGER.info(
+ "Loaded parameter definition for {}:{} (minVersion:
{}): {} goals, {} total parameters",
+ artifactId,
+ pluginVersion != null ? pluginVersion : "any",
+ bestMatch.getMinVersion() != null ?
bestMatch.getMinVersion() : "none",
+ bestMatch.getGoals().size(),
+ bestMatch.getGoals().values().stream()
+ .mapToInt(g -> g.getParameters().size())
+ .sum());
+ }
+
+ return bestMatch;
+ } catch (Exception e) {
+ LOGGER.warn("Failed to load parameter definition for {}: {}",
artifactId, e.getMessage(), e);
+ return null;
+ }
Review Comment:
Addressed in 25e09ea: the descriptor parser now uses Java
try-with-resources, so the parameter-definition:
```java
InputStream
```
is closed on every path.
##########
src/main/java/org/apache/maven/buildcache/xml/PluginParameterLoader.java:
##########
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.buildcache.xml;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import
org.apache.maven.buildcache.xml.PluginParameterDefinition.GoalParameterDefinition;
+import
org.apache.maven.buildcache.xml.PluginParameterDefinition.ParameterDefinition;
+import org.apache.maven.buildcache.xml.PluginParameterDefinition.ParameterType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * Loads plugin parameter definitions from classpath resources.
+ * Definitions are stored in
src/main/resources/plugin-parameters/{artifactId}.xml
+ */
+public class PluginParameterLoader {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(PluginParameterLoader.class);
+ private static final String PARAMETER_DIR = "plugin-parameters/";
+
+ private final Map<String, PluginParameterDefinition> definitions = new
ConcurrentHashMap<>();
+
+ /**
+ * Load parameter definitions for a plugin by artifact ID only (no version
matching)
+ */
+ public PluginParameterDefinition load(String artifactId) {
+ return load(artifactId, null);
+ }
+
+ /**
+ * Load parameter definitions for a plugin by artifact ID and version.
+ * If version is provided, finds the best matching definition (highest
minVersion <= actual version).
+ * If version is null, returns any definition for the artifactId.
+ */
+ public PluginParameterDefinition load(String artifactId, String
pluginVersion) {
+ String cacheKey = artifactId + (pluginVersion != null ? ":" +
pluginVersion : "");
+
+ if (definitions.containsKey(cacheKey)) {
+ return definitions.get(cacheKey);
+ }
+
+ String resourcePath = PARAMETER_DIR + artifactId + ".xml";
+ InputStream is =
getClass().getClassLoader().getResourceAsStream(resourcePath);
+
+ if (is == null) {
+ LOGGER.debug("No parameter definition found for plugin: {}",
artifactId);
+ return null;
+ }
+
+ try {
+ java.util.List<PluginParameterDefinition> allDefinitions =
parseDefinitions(is, artifactId);
+
+ PluginParameterDefinition bestMatch =
findBestMatch(allDefinitions, pluginVersion);
+
+ if (bestMatch != null) {
+ definitions.put(cacheKey, bestMatch);
+ LOGGER.info(
+ "Loaded parameter definition for {}:{} (minVersion:
{}): {} goals, {} total parameters",
+ artifactId,
+ pluginVersion != null ? pluginVersion : "any",
+ bestMatch.getMinVersion() != null ?
bestMatch.getMinVersion() : "none",
+ bestMatch.getGoals().size(),
+ bestMatch.getGoals().values().stream()
+ .mapToInt(g -> g.getParameters().size())
+ .sum());
Review Comment:
Addressed in 25e09ea: the parameter-definition load message is now logged at:
```text
DEBUG
```.
##########
src/main/resources/plugin-parameters/maven-compiler-plugin.xml:
##########
@@ -0,0 +1,327 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<plugin xmlns="http://maven.apache.org/PLUGIN-PARAMETERS/1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/PLUGIN-PARAMETERS/1.0.0
plugin-parameters.xsd">
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <minVersion>3.11.0</minVersion>
+
+ <goals>
+ <goal>
+ <name>compile</name>
+ <parameters>
+ <!-- FUNCTIONAL: Affect compiled bytecode or compilation behavior -->
+ <parameter>
+ <name>source</name>
+ <type>functional</type>
+ <description>Source JDK version for compilation</description>
+ </parameter>
+ <parameter>
+ <name>target</name>
+ <type>functional</type>
+ <description>Target JDK version for compiled bytecode</description>
+ </parameter>
+ <parameter>
+ <name>release</name>
+ <type>functional</type>
+ <description>JDK release version (combines source and
target)</description>
+ </parameter>
+ <parameter>
+ <name>encoding</name>
+ <type>functional</type>
+ <description>Source file encoding</description>
+ </parameter>
+ <parameter>
+ <name>debug</name>
+ <type>functional</type>
+ <description>Include debugging information in compiled
bytecode</description>
+ </parameter>
+ <parameter>
+ <name>debuglevel</name>
+ <type>functional</type>
+ <description>Level of debugging information (lines, vars,
source)</description>
+ </parameter>
+ <parameter>
+ <name>optimize</name>
+ <type>functional</type>
+ <description>Optimize compiled bytecode</description>
+ </parameter>
+ <parameter>
+ <name>compilerArgs</name>
+ <type>functional</type>
+ <description>Additional compiler arguments</description>
+ </parameter>
+ <parameter>
+ <name>compilerArgument</name>
+ <type>functional</type>
+ <description>Single additional compiler argument</description>
+ </parameter>
+ <parameter>
+ <name>annotationProcessorPaths</name>
+ <type>functional</type>
+ <description>Classpath for annotation processors</description>
+ </parameter>
+ <parameter>
+ <name>annotationProcessors</name>
+ <type>functional</type>
+ <description>Annotation processors to run</description>
+ </parameter>
+ <parameter>
+ <name>proc</name>
+ <type>functional</type>
+ <description>Annotation processing mode (none, only,
proc)</description>
+ </parameter>
+ <parameter>
+ <name>executable</name>
+ <type>functional</type>
+ <description>Path to javac executable (different compiler may
produce different output)</description>
+ </parameter>
+ <parameter>
+ <name>parameters</name>
+ <type>functional</type>
+ <description>Generate metadata for method parameters</description>
+ </parameter>
+ <parameter>
+ <name>enablePreview</name>
+ <type>functional</type>
+ <description>Enable preview features</description>
+ </parameter>
+
+ <!-- BEHAVIORAL: Affect how compilation runs but not the output -->
Review Comment:
Addressed in 25e09ea: the XML comment now states that:
```text
behavioral
```
parameters are the default and output-affecting exceptions are marked:
```text
FUNCTIONAL
```.
##########
src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java:
##########
@@ -401,6 +401,14 @@ boolean isParamsMatched(
CompletedExecution completedExecution) {
List<TrackedProperty> tracked =
cacheConfig.getTrackedProperties(mojoExecution);
Review Comment:
Addressed in ea9634b by removing the redundant null check. The generated:
```java
GoalReconciliation#getReconciles()
```
initializes the list when null; the cache configuration accessor is already
non-null.
##########
src/main/resources/plugin-parameters/plugin-parameters.xsd:
##########
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://maven.apache.org/PLUGIN-PARAMETERS/1.0.0"
+ xmlns="http://maven.apache.org/PLUGIN-PARAMETERS/1.0.0"
+ elementFormDefault="qualified">
+
+ <xs:element name="plugin" type="PluginType"/>
+
Review Comment:
Addressed in ea9634b: the schema:
```text
plugin-parameters.xsd
```
now declares a namespaced:
```xml
<plugins>
```
root and supports repeated plugin entries. The versioned test descriptor is
namespaced, and schema validation passes.
##########
src/site/markdown/how-to.md:
##########
@@ -169,6 +169,175 @@ Add `executionControl/runAlways` section:
</executionControl>
```
+### Default Reconciliation Behavior
+
+The build cache extension automatically tracks certain critical plugin
properties by default, even without explicit
+`executionControl` configuration. These defaults are derived from plugin
parameter descriptors under
+`plugin-parameters/`:
+
+* **maven-compiler-plugin** (`compile` and `testCompile` goals): Tracks
functional parameters defined in
+ `plugin-parameters/maven-compiler-plugin.xml`
+* **maven-install-plugin** (`install` and `install-file` goals): Tracks
functional parameters defined in
+ `plugin-parameters/maven-install-plugin.xml`
+
+This default behavior prevents common cache invalidation issues, particularly
in multi-module JPMS (Java Platform Module System)
+projects where compiler version changes can cause compilation failures.
+
+**Overriding Defaults:** When you explicitly configure `executionControl` for
a plugin, your explicit configuration completely
+overrides the defaults for that plugin. For example, to track only the
`release` property for maven-compiler-plugin instead
+of the functional parameters from
`plugin-parameters/maven-compiler-plugin.xml`:
+
+```xml
+<cache xmlns="http://maven.apache.org/BUILD-CACHE-CONFIG/1.2.0">
+ <configuration>
+ ...
+ </configuration>
+ <executionControl>
+ <reconcile>
+ <plugins>
+ <plugin artifactId="maven-compiler-plugin" goal="compile">
+ <reconciles>
+ <reconcile propertyName="release"/>
+ </reconciles>
+ </plugin>
+ </plugins>
+ </reconcile>
+ </executionControl>
+</cache>
+```
+
+This configuration in your `.mvn/maven-build-cache-config.xml` file replaces
the built-in defaults. You can also define
+reconciliation configurations for plugins that don't have built-in defaults
using the same syntax.
+
+### Parameter Validation and Categorization
+
+The build cache extension includes a parameter validation system that
categorizes plugin parameters and validates
+reconciliation configurations against known parameter definitions.
+
+#### Parameter Categories
+
+All plugin parameters are categorized into two types:
+
+* **Functional Parameters**: Affect the compiled output or build artifacts
(e.g., `source`, `target`, `release`, `encoding`)
+* **Behavioral Parameters**: Affect how the build runs but not the output
(e.g., `verbose`, `fork`, `maxmem`, `skip`)
+
+Only **functional** parameters should be tracked in reconciliation
configurations, as behavioral parameters don't affect
+the build output and shouldn't invalidate the cache.
+
+#### Validation Features
+
+The extension automatically validates reconciliation configurations and logs
warnings/errors for:
+
+* **Unknown parameters**: Parameters not defined in the plugin's parameter
definition (ERROR level)
+ - May indicate a plugin version mismatch or renamed parameter
+ - Suggests updating parameter definitions or removing the parameter from
reconciliation
+
+* **Behavioral parameters in reconciliation**: Parameters categorized as
behavioral (WARN level)
+ - Suggests that the parameter likely shouldn't affect cache invalidation
+ - Consider removing if it doesn't actually affect build output
+
+#### Adding Parameter Definitions for New Plugins
+
+Parameter definitions are stored in
`src/main/resources/plugin-parameters/{artifactId}.xml`. To add validation for
a new plugin:
+
+1. Create an XML file following the schema in `plugin-parameters.xsd`:
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin xmlns="http://maven.apache.org/PLUGIN-PARAMETERS/1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/PLUGIN-PARAMETERS/1.0.0
plugin-parameters.xsd">
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-example-plugin</artifactId>
+
+ <goals>
+ <goal>
+ <name>example-goal</name>
+ <parameters>
+ <parameter>
+ <name>outputDirectory</name>
+ <type>functional</type>
+ <description>Directory where output is written</description>
+ </parameter>
+ <parameter>
+ <name>verbose</name>
+ <type>behavioral</type>
+ <description>Enable verbose logging</description>
+ </parameter>
+ </parameters>
+ </goal>
+ </goals>
+</plugin>
+```
+
+2. Place the file in the classpath at `plugin-parameters/{artifactId}.xml`
+
+3. The extension will automatically load and validate against this definition
+
+#### Version-Specific Parameter Definitions
+
+The parameter validation system supports version-specific definitions to
handle plugins that change parameters across versions. This allows accurate
validation even when plugin APIs evolve.
+
+**How Version Matching Works:**
+
+- Definitions include a `minVersion` element specifying the minimum plugin
version they apply to
+- At runtime, the extension selects the definition with the highest
`minVersion` that is ≤ the actual plugin version
+- Multiple version-specific definitions can exist in a single file
+
+**Example with version-specific parameters:**
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<plugins>
+ <!-- Parameters for versions 1.0.0 through 2.x -->
+ <plugin xmlns="http://maven.apache.org/PLUGIN-PARAMETERS/1.0.0">
Review Comment:
Addressed in ea9634b: the version-specific example now declares the
plugin-parameters namespace on its:
```xml
<plugins>
```
root.
##########
src/test/projects/default-reconciliation-with-other-plugin/.mvn/maven-build-cache-config.xml:
##########
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<cache xmlns="http://maven.apache.org/BUILD-CACHE-CONFIG/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/BUILD-CACHE-CONFIG/1.0.0
https://maven.apache.org/xsd/build-cache-config-1.0.0.xsd">
+ <!-- Has executionControl but only for surefire plugin, not compiler -
defaults should still apply -->
+ <executionControl>
+ <reconcile>
+ <plugins>
+ <plugin artifactId="maven-surefire-plugin">
Review Comment:
Addressed in ea9634b: the fixture now specifies:
```xml
goal="test"
```
for the Maven Surefire plugin.
##########
src/site/markdown/how-to.md:
##########
@@ -169,6 +169,175 @@ Add `executionControl/runAlways` section:
</executionControl>
```
+### Default Reconciliation Behavior
+
+The build cache extension automatically tracks certain critical plugin
properties by default, even without explicit
+`executionControl` configuration. These defaults are derived from plugin
parameter descriptors under
+`plugin-parameters/`:
+
+* **maven-compiler-plugin** (`compile` and `testCompile` goals): Tracks
functional parameters defined in
+ `plugin-parameters/maven-compiler-plugin.xml`
+* **maven-install-plugin** (`install` and `install-file` goals): Tracks
functional parameters defined in
+ `plugin-parameters/maven-install-plugin.xml`
+
+This default behavior prevents common cache invalidation issues, particularly
in multi-module JPMS (Java Platform Module System)
+projects where compiler version changes can cause compilation failures.
+
+**Overriding Defaults:** When you explicitly configure `executionControl` for
a plugin, your explicit configuration completely
+overrides the defaults for that plugin. For example, to track only the
`release` property for maven-compiler-plugin instead
+of the functional parameters from
`plugin-parameters/maven-compiler-plugin.xml`:
+
+```xml
+<cache xmlns="http://maven.apache.org/BUILD-CACHE-CONFIG/1.2.0">
+ <configuration>
+ ...
+ </configuration>
+ <executionControl>
+ <reconcile>
+ <plugins>
+ <plugin artifactId="maven-compiler-plugin" goal="compile">
+ <reconciles>
+ <reconcile propertyName="release"/>
+ </reconciles>
+ </plugin>
+ </plugins>
+ </reconcile>
+ </executionControl>
+</cache>
+```
+
+This configuration in your `.mvn/maven-build-cache-config.xml` file replaces
the built-in defaults. You can also define
+reconciliation configurations for plugins that don't have built-in defaults
using the same syntax.
+
+### Parameter Validation and Categorization
+
+The build cache extension includes a parameter validation system that
categorizes plugin parameters and validates
+reconciliation configurations against known parameter definitions.
+
+#### Parameter Categories
+
+All plugin parameters are categorized into two types:
+
+* **Functional Parameters**: Affect the compiled output or build artifacts
(e.g., `source`, `target`, `release`, `encoding`)
+* **Behavioral Parameters**: Affect how the build runs but not the output
(e.g., `verbose`, `fork`, `maxmem`, `skip`)
+
+Only **functional** parameters should be tracked in reconciliation
configurations, as behavioral parameters don't affect
+the build output and shouldn't invalidate the cache.
+
+#### Validation Features
+
+The extension automatically validates reconciliation configurations and logs
warnings/errors for:
+
+* **Unknown parameters**: Parameters not defined in the plugin's parameter
definition (ERROR level)
Review Comment:
Addressed in ea9634b: the documentation now says:
```text
WARN
```
matching the logging call in:
```java
CacheConfigImpl.validateReconciliationConfig()
```.
##########
src/main/java/org/apache/maven/buildcache/xml/PluginParameterLoader.java:
##########
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.buildcache.xml;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import
org.apache.maven.buildcache.xml.PluginParameterDefinition.GoalParameterDefinition;
+import
org.apache.maven.buildcache.xml.PluginParameterDefinition.ParameterDefinition;
+import org.apache.maven.buildcache.xml.PluginParameterDefinition.ParameterType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * Loads plugin parameter definitions from classpath resources.
+ * Definitions are stored in
src/main/resources/plugin-parameters/{artifactId}.xml
+ */
+public class PluginParameterLoader {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(PluginParameterLoader.class);
+ private static final String PARAMETER_DIR = "plugin-parameters/";
+
+ private final Map<String, PluginParameterDefinition> definitions = new
ConcurrentHashMap<>();
+
+ /**
+ * Load parameter definitions for a plugin by artifact ID only (no version
matching)
+ */
+ public PluginParameterDefinition load(String artifactId) {
+ return load(artifactId, null);
+ }
+
+ /**
+ * Load parameter definitions for a plugin by artifact ID and version.
+ * If version is provided, finds the best matching definition (highest
minVersion <= actual version).
+ * If version is null, returns any definition for the artifactId.
+ */
+ public PluginParameterDefinition load(String artifactId, String
pluginVersion) {
+ String cacheKey = artifactId + (pluginVersion != null ? ":" +
pluginVersion : "");
+
+ if (definitions.containsKey(cacheKey)) {
+ return definitions.get(cacheKey);
+ }
+
+ String resourcePath = PARAMETER_DIR + artifactId + ".xml";
+ InputStream is =
getClass().getClassLoader().getResourceAsStream(resourcePath);
+
+ if (is == null) {
+ LOGGER.debug("No parameter definition found for plugin: {}",
artifactId);
+ return null;
+ }
+
+ try (InputStream inputStream = is) {
+ java.util.List<PluginParameterDefinition> allDefinitions =
parseDefinitions(inputStream, artifactId);
+
+ PluginParameterDefinition bestMatch =
findBestMatch(allDefinitions, pluginVersion);
+
+ if (bestMatch != null) {
+ definitions.put(cacheKey, bestMatch);
+ LOGGER.debug(
+ "Loaded parameter definition for {}:{} (minVersion:
{}): {} goals, {} total parameters",
+ artifactId,
+ pluginVersion != null ? pluginVersion : "any",
+ bestMatch.getMinVersion() != null ?
bestMatch.getMinVersion() : "none",
+ bestMatch.getGoals().size(),
+ bestMatch.getGoals().values().stream()
+ .mapToInt(g -> g.getParameters().size())
+ .sum());
+ }
+
+ return bestMatch;
+ } catch (Exception e) {
+ LOGGER.warn("Failed to load parameter definition for {}: {}",
artifactId, e.getMessage(), e);
+ return null;
+ }
+ }
+
+ /**
+ * Find the best matching definition for a plugin version.
+ * Returns the definition with the highest minVersion that is <=
pluginVersion.
+ * If pluginVersion is null, returns the first definition (or the one
without minVersion).
+ */
+ private PluginParameterDefinition findBestMatch(
+ java.util.List<PluginParameterDefinition> definitions, String
pluginVersion) {
+ if (definitions.isEmpty()) {
+ return null;
+ }
+
+ if (pluginVersion == null) {
+ // No version specified, prefer definition without minVersion,
otherwise return first
+ return definitions.stream()
+ .filter(d -> d.getMinVersion() == null)
+ .findFirst()
+ .orElse(definitions.get(0));
+ }
+
+ // Find highest minVersion that's <= pluginVersion
+ PluginParameterDefinition bestMatch = null;
+ String bestMinVersion = null;
+
+ for (PluginParameterDefinition def : definitions) {
+ String minVersion = def.getMinVersion();
+
+ // Definition without minVersion applies to all versions
+ if (minVersion == null) {
+ if (bestMatch == null) {
+ bestMatch = def;
+ }
+ continue;
+ }
+
+ // Check if this definition applies to the plugin version
+ if (compareVersions(minVersion, pluginVersion) <= 0) {
+ // minVersion <= pluginVersion, so this definition applies
+ if (bestMinVersion == null || compareVersions(minVersion,
bestMinVersion) > 0) {
+ // This is a better match (higher minVersion)
+ bestMatch = def;
+ bestMinVersion = minVersion;
+ }
+ }
+ }
+
+ return bestMatch;
+ }
+
+ /**
+ * Compare two version strings.
+ * Returns: negative if v1 < v2, zero if v1 == v2, positive if v1 > v2
+ */
+ private int compareVersions(String v1, String v2) {
+ String[] parts1 = v1.split("\\.");
+ String[] parts2 = v2.split("\\.");
+
+ int maxLength = Math.max(parts1.length, parts2.length);
+
+ for (int i = 0; i < maxLength; i++) {
+ int num1 = i < parts1.length ? parseVersionPart(parts1[i]) : 0;
+ int num2 = i < parts2.length ? parseVersionPart(parts2[i]) : 0;
+
+ if (num1 != num2) {
+ return Integer.compare(num1, num2);
+ }
+ }
+
+ return 0;
+ }
+
+ private int parseVersionPart(String part) {
+ try {
+ // Handle qualifiers like "3.8.0-SNAPSHOT" - just use numeric part
+ int dashIndex = part.indexOf('-');
+ if (dashIndex > 0) {
+ part = part.substring(0, dashIndex);
+ }
+ return Integer.parseInt(part);
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Parse plugin parameter definitions from XML.
+ * Supports multiple <plugin> elements in a single file for
version-specific definitions.
+ */
+ private java.util.List<PluginParameterDefinition>
parseDefinitions(InputStream is, String artifactId)
+ throws Exception {
+ java.util.List<PluginParameterDefinition> definitions = new
java.util.ArrayList<>();
+
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document doc = builder.parse(is);
+
+ Element root = doc.getDocumentElement();
+
+ // Check if root is a single <plugin> or if we need to look for
multiple
+ if ("plugin".equals(root.getLocalName())) {
+ // Single plugin definition
+ definitions.add(parsePluginDefinition(root));
+ } else {
+ // Look for multiple <plugin> elements
+ NodeList pluginNodes = root.getElementsByTagName("plugin");
+ for (int i = 0; i < pluginNodes.getLength(); i++) {
+ Element pluginElement = (Element) pluginNodes.item(i);
Review Comment:
Addressed in 057770e: plugin discovery now uses:
```java
getElementsByTagNameNS("*", "plugin")
```
and the new prefixed-namespace regression test verifies the loader.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]