You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/10/22 16:38:47 UTC
(maven) branch master updated: Fix #13459: support package-private configuration classes in plugins (#13470)
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/master by this push:
new d560929d3e Fix #13459: support package-private configuration classes in plugins (#13470)
d560929d3e is described below
commit d560929d3ed7f485f05d31b37c0d86434c8916e4
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Thu Oct 22 18:38:39 2026 +0200
Fix #13459: support package-private configuration classes in plugins (#13470)
* Fix #13459: support package-private configuration classes in EnhancedConfigurationConverter
The Plexus AbstractConfigurationConverter.instantiateObject() uses
Class.newInstance() which requires a public no-arg constructor.
This prevents using package-private classes (or inner classes) as
@Parameter configuration types in Maven 4 API plugins.
EnhancedCompositeBeanHelper.setFieldValue() already handles non-public
fields by calling field.setAccessible(true). This commit makes object
instantiation symmetric: override the call site in
EnhancedConfigurationConverter.fromConfiguration() with a local
instantiate() method that uses getDeclaredConstructor() +
setAccessible(true), consistent with the field injection strategy.
* test: add regression test for #13459 (package-private constructor instantiation)
---
.../internal/EnhancedConfigurationConverter.java | 15 ++-
.../EnhancedConfigurationConverterTest.java | 105 +++++++++++++++++++++
2 files changed, 119 insertions(+), 1 deletion(-)
diff --git a/impl/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedConfigurationConverter.java b/impl/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedConfigurationConverter.java
index 3f49cfcdba..2a581e83a1 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedConfigurationConverter.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedConfigurationConverter.java
@@ -94,7 +94,7 @@ public Object fromConfiguration(
if (null == value && implType.isInterface() && configuration.getChildCount() == 0) {
return null; // nothing to process
}
- final Object bean = instantiateObject(implType);
+ final Object bean = instantiate(implType, configuration);
if (null == value) {
processConfiguration(lookup, bean, loader, configuration, evaluator, listener);
} else {
@@ -111,6 +111,19 @@ public Object fromConfiguration(
}
}
+ private static Object instantiate(Class<?> type, PlexusConfiguration configuration)
+ throws ComponentConfigurationException {
+ try {
+ var ctor = type.getDeclaredConstructor();
+ if (!ctor.canAccess(null)) {
+ ctor.setAccessible(true);
+ }
+ return ctor.newInstance();
+ } catch (ReflectiveOperationException e) {
+ throw new ComponentConfigurationException(configuration, "Cannot create instance of class " + type, e);
+ }
+ }
+
private Class<?> resolveClassForImplementationHint(
final Class<?> type, final PlexusConfiguration configuration, final ClassLoader loader)
throws ComponentConfigurationException {
diff --git a/impl/maven-core/src/test/java/org/apache/maven/configuration/internal/EnhancedConfigurationConverterTest.java b/impl/maven-core/src/test/java/org/apache/maven/configuration/internal/EnhancedConfigurationConverterTest.java
new file mode 100644
index 0000000000..7cdc1cbf6c
--- /dev/null
+++ b/impl/maven-core/src/test/java/org/apache/maven/configuration/internal/EnhancedConfigurationConverterTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.configuration.internal;
+
+import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
+import org.codehaus.plexus.component.configurator.ConfigurationListener;
+import org.codehaus.plexus.component.configurator.converters.lookup.DefaultConverterLookup;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
+import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Tests for {@link EnhancedConfigurationConverter}, focusing on instantiation of
+ * classes with non-public constructors (fix for #13459).
+ */
+class EnhancedConfigurationConverterTest {
+
+ private final EnhancedConfigurationConverter converter = new EnhancedConfigurationConverter();
+ private final DefaultConverterLookup lookup = new DefaultConverterLookup();
+ private final ExpressionEvaluator evaluator = mock(ExpressionEvaluator.class);
+ private final ConfigurationListener listener = mock(ConfigurationListener.class);
+
+ @AfterEach
+ void tearDown() {
+ EnhancedConfigurationConverter.clearCaches();
+ }
+
+ /**
+ * Regression test for #13459: a class with a package-private no-arg constructor must be
+ * instantiable via {@code fromConfiguration()}. Before the fix, the inherited
+ * {@code instantiateObject()} used {@code Class.newInstance()} which requires a public
+ * constructor and would throw {@link IllegalAccessException} for package-private classes.
+ */
+ @Test
+ void testFromConfigurationWithPackagePrivateConstructor() throws Exception {
+ XmlPlexusConfiguration config = new XmlPlexusConfiguration("config");
+ config.addChild("value", "hello");
+
+ Object result = converter.fromConfiguration(
+ lookup, config, PackagePrivateBean.class, null, getClass().getClassLoader(), evaluator, listener);
+
+ assertNotNull(result, "fromConfiguration must return a non-null instance");
+ assertInstanceOf(PackagePrivateBean.class, result, "Result must be a PackagePrivateBean");
+ }
+
+ /**
+ * Verify that a class with no no-arg constructor at all still throws
+ * {@link ComponentConfigurationException} (not an unrelated reflection error).
+ */
+ @Test
+ void testFromConfigurationWithNoDefaultConstructorThrows() {
+ XmlPlexusConfiguration config = new XmlPlexusConfiguration("config");
+
+ assertThrows(
+ ComponentConfigurationException.class,
+ () -> converter.fromConfiguration(
+ lookup,
+ config,
+ NoDefaultConstructorBean.class,
+ null,
+ getClass().getClassLoader(),
+ evaluator,
+ listener));
+ }
+
+ // -------------------------------------------------------------------------
+ // Helper types — same package so the package-private constructor is valid
+ // -------------------------------------------------------------------------
+
+ /** A bean with a package-private no-arg constructor: the exact scenario fixed by #13459. */
+ static class PackagePrivateBean {
+ String value;
+
+ // package-private constructor — inaccessible via Class.newInstance()
+ PackagePrivateBean() {}
+ }
+
+ /** A bean with no no-arg constructor at all. */
+ static class NoDefaultConstructorBean {
+ @SuppressWarnings("unused")
+ NoDefaultConstructorBean(String required) {}
+ }
+}