Skip to content

Commit 1597091

Browse files
gnodetVincent Potucek
authored and
Vincent Potucek
committed
[MNG-8572] Support DI beans in build extensions (apache#2274)
This commit adds support for using the Maven API DI system in build extensions (plugins with extensions=true). It enables build extensions to provide custom artifact type handlers using the new DI mechanism. Key changes: - Added an integration test that demonstrates a custom artifact type handler using the Maven API DI system in a build extension - The test shows how to implement a TypeProvider using @nAmed annotation - The annotation processor automatically generates the DI index file - The custom type handler is properly discovered and used during the build This enhancement allows build extensions to leverage the new Maven API DI system, making it easier to create and maintain extensions that provide custom artifact type handlers.
1 parent 9bfb35c commit 1597091

File tree

15 files changed

+556
-15
lines changed

15 files changed

+556
-15
lines changed

impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultTypeRegistry.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,15 @@
2222
import javax.inject.Named;
2323
import javax.inject.Singleton;
2424

25-
import java.util.List;
26-
import java.util.Map;
25+
import java.util.Objects;
2726
import java.util.Optional;
2827
import java.util.concurrent.ConcurrentHashMap;
29-
import java.util.stream.Collectors;
3028

3129
import org.apache.maven.api.JavaPathType;
3230
import org.apache.maven.api.Type;
3331
import org.apache.maven.api.annotations.Nonnull;
3432
import org.apache.maven.api.services.LanguageRegistry;
33+
import org.apache.maven.api.services.Lookup;
3534
import org.apache.maven.api.services.TypeRegistry;
3635
import org.apache.maven.api.spi.TypeProvider;
3736
import org.apache.maven.artifact.handler.ArtifactHandler;
@@ -41,12 +40,11 @@
4140
import org.apache.maven.impl.resolver.type.DefaultType;
4241

4342
import static java.util.Objects.requireNonNull;
44-
import static java.util.function.Function.identity;
4543

4644
@Named
4745
@Singleton
4846
public class DefaultTypeRegistry extends AbstractEventSpy implements TypeRegistry {
49-
private final Map<String, Type> types;
47+
private final Lookup lookup;
5048

5149
private final LanguageRegistry languageRegistry;
5250

@@ -55,11 +53,8 @@ public class DefaultTypeRegistry extends AbstractEventSpy implements TypeRegistr
5553
private final LegacyArtifactHandlerManager manager;
5654

5755
@Inject
58-
public DefaultTypeRegistry(
59-
List<TypeProvider> providers, LanguageRegistry languageRegistry, LegacyArtifactHandlerManager manager) {
60-
this.types = requireNonNull(providers, "providers cannot be null").stream()
61-
.flatMap(p -> p.provides().stream())
62-
.collect(Collectors.toMap(Type::id, identity()));
56+
public DefaultTypeRegistry(Lookup lookup, LanguageRegistry languageRegistry, LegacyArtifactHandlerManager manager) {
57+
this.lookup = requireNonNull(lookup, "lookup cannot be null");
6358
this.languageRegistry = requireNonNull(languageRegistry, "languageRegistry cannot be null");
6459
this.usedTypes = new ConcurrentHashMap<>();
6560
this.manager = requireNonNull(manager, "artifactHandlerManager cannot be null");
@@ -84,7 +79,11 @@ public Optional<Type> lookup(String id) {
8479
public Type require(String id) {
8580
requireNonNull(id, "id cannot be null");
8681
return usedTypes.computeIfAbsent(id, i -> {
87-
Type type = types.get(id);
82+
Type type = lookup.lookupList(TypeProvider.class).stream()
83+
.flatMap(p -> p.provides().stream())
84+
.filter(t -> Objects.equals(id, t.id()))
85+
.findFirst()
86+
.orElse(null);
8887
if (type == null) {
8988
// Copy data as the ArtifactHandler is not immutable, but Type should be.
9089
ArtifactHandler handler = manager.getArtifactHandler(id);

impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import org.apache.maven.internal.impl.DefaultLog;
6464
import org.apache.maven.internal.impl.DefaultMojoExecution;
6565
import org.apache.maven.internal.impl.InternalMavenSession;
66+
import org.apache.maven.internal.impl.SisuDiBridgeModule;
6667
import org.apache.maven.internal.xml.XmlPlexusConfiguration;
6768
import org.apache.maven.model.Plugin;
6869
import org.apache.maven.plugin.ContextEnabled;
@@ -430,6 +431,7 @@ private void createPluginRealm(
430431
private void discoverPluginComponents(
431432
final ClassRealm pluginRealm, Plugin plugin, PluginDescriptor pluginDescriptor)
432433
throws PluginContainerException {
434+
ClassLoader prevTccl = Thread.currentThread().getContextClassLoader();
433435
try {
434436
if (pluginDescriptor != null) {
435437
for (MojoDescriptor mojo : pluginDescriptor.getMojos()) {
@@ -440,18 +442,22 @@ private void discoverPluginComponents(
440442
}
441443
}
442444

445+
Thread.currentThread().setContextClassLoader(pluginRealm);
443446
((DefaultPlexusContainer) container)
444447
.discoverComponents(
445448
pluginRealm,
446449
new SessionScopeModule(container.lookup(SessionScope.class)),
447450
new MojoExecutionScopeModule(container.lookup(MojoExecutionScope.class)),
448-
new PluginConfigurationModule(plugin.getDelegate()));
451+
new PluginConfigurationModule(plugin.getDelegate()),
452+
new SisuDiBridgeModule(true));
449453
} catch (ComponentLookupException | CycleDetectedInComponentGraphException e) {
450454
throw new PluginContainerException(
451455
plugin,
452456
pluginRealm,
453457
"Error in component graph of plugin " + plugin.getId() + ": " + e.getMessage(),
454458
e);
459+
} finally {
460+
Thread.currentThread().setContextClassLoader(prevTccl);
455461
}
456462
}
457463

its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8525MavenDIPlugin.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public void testMavenDIPlugin() throws Exception {
4848
Verifier v0 = newVerifier(testDir.getAbsolutePath());
4949
v0.setAutoclean(false);
5050
v0.deleteDirectory("target");
51-
v0.deleteArtifacts("org.apache.maven.its");
51+
v0.deleteArtifacts("org.apache.maven.its.mng8525");
5252
v0.addCliArgument("install");
5353
v0.execute();
5454
v0.verifyErrorFreeLog();
@@ -58,7 +58,7 @@ public void testMavenDIPlugin() throws Exception {
5858
//
5959
Verifier v1 = newVerifier(testDir.getAbsolutePath());
6060
v1.setAutoclean(false);
61-
v1.addCliArgument("org.apache.maven.its:mavendi-maven-plugin:0.0.1-SNAPSHOT:hello");
61+
v1.addCliArgument("org.apache.maven.its.mng8525:mavendi-maven-plugin:0.0.1-SNAPSHOT:hello");
6262
v1.addCliArgument("-Dname=World");
6363
v1.execute();
6464
v1.verifyErrorFreeLog();
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.it;
20+
21+
import java.io.File;
22+
23+
import org.junit.jupiter.api.Test;
24+
25+
/**
26+
* This is a test set for <a href="https://issues.apache.org/jira/browse/MNG-8572">MNG-8572</a>.
27+
*
28+
* It verifies that Maven plugins with extensions=true can provide custom artifact type handlers using the Maven API DI system.
29+
*/
30+
public class MavenITmng8572DITypeHandlerTest extends AbstractMavenIntegrationTestCase {
31+
32+
public MavenITmng8572DITypeHandlerTest() {
33+
super("[4.0.0-rc-4,)");
34+
}
35+
36+
@Test
37+
public void testCustomTypeHandler() throws Exception {
38+
// Build the extension first
39+
File testDir = extractResources("/mng-8572-di-type-handler");
40+
Verifier verifier = newVerifier(new File(testDir, "extension").getAbsolutePath());
41+
verifier.setAutoclean(false);
42+
verifier.deleteDirectory("target");
43+
verifier.deleteArtifacts("org.apache.maven.its.mng8572");
44+
verifier.addCliArgument("install");
45+
verifier.execute();
46+
verifier.verifyErrorFreeLog();
47+
48+
// Now use the extension in a test project
49+
verifier = newVerifier(new File(testDir, "test").getAbsolutePath());
50+
verifier.setAutoclean(false);
51+
verifier.deleteDirectory("target");
52+
verifier.addCliArguments(
53+
"install:install-file",
54+
"-Dfile=src/main/java/org/apache/maven/its/mng8572/test/DummyClass.java",
55+
"-DpomFile=dummy-artifact-pom.xml",
56+
"-Dpackaging=custom",
57+
"-DcreateChecksum=true");
58+
verifier.execute();
59+
verifier.verifyErrorFreeLog();
60+
61+
verifier = newVerifier(new File(testDir, "test").getAbsolutePath());
62+
verifier.setAutoclean(false);
63+
verifier.addCliArgument("validate");
64+
verifier.execute();
65+
verifier.verifyErrorFreeLog();
66+
67+
// Verify that our custom type handler was used
68+
verifier.verifyTextInLog("[INFO] [MNG-8572] Registering custom type handler for type: custom-type");
69+
}
70+
}

its/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ public TestSuiteOrdering() {
101101
* the tests are to finishing. Newer tests are also more likely to fail, so this is
102102
* a fail fast technique as well.
103103
*/
104+
suite.addTestSuite(MavenITmng8572DITypeHandlerTest.class);
104105
suite.addTestSuite(MavenITmng3558PropertyEscapingTest.class);
105106
suite.addTestSuite(MavenITmng4559MultipleJvmArgsTest.class);
106107
suite.addTestSuite(MavenITmng4559SpacesInJvmOptsTest.class);

its/core-it-suite/src/test/resources/mng-8525-maven-di-plugin/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ under the License.
2020
<project xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd">
2121
<modelVersion>4.1.0</modelVersion>
2222

23-
<groupId>org.apache.maven.its</groupId>
23+
<groupId>org.apache.maven.its.mng8525</groupId>
2424
<artifactId>mavendi-maven-plugin</artifactId>
2525
<version>0.0.1-SNAPSHOT</version>
2626
<packaging>maven-plugin</packaging>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# MNG-8572 Custom Type Handler with Maven API DI
2+
3+
This integration test demonstrates how to create a Maven plugin with `extensions=true` that provides a custom artifact type handler using the Maven API DI system.
4+
5+
## Structure
6+
7+
- `extension/`: The Maven plugin with extensions=true that provides a custom artifact type handler
8+
- Uses `org.apache.maven.api.di.Named` annotation
9+
- Implements `org.apache.maven.api.spi.TypeProvider` interface
10+
- Provides a custom type with ID "custom-type"
11+
- Uses the Maven API DI system for component discovery
12+
- The annotation processor automatically generates the DI index
13+
14+
- `test/`: A test project that uses the custom type handler
15+
- References the plugin in the build section with `<extensions>true</extensions>`
16+
- Has a dependency with `type="custom-type"`
17+
- Verifies that the custom type handler is used
18+
19+
## Key Points
20+
21+
1. **Maven Plugin with extensions=true**:
22+
- The plugin is configured with `<extensions>true</extensions>`
23+
- This allows it to participate in the build process and provide custom components
24+
25+
2. **TypeProvider Implementation**:
26+
- Implements the `org.apache.maven.api.spi.TypeProvider` interface
27+
- Annotated with `@Named` from `org.apache.maven.api.di`
28+
- Returns a custom implementation of the `Type` interface
29+
30+
3. **Annotation Processing**:
31+
- The Maven API DI annotation processor automatically generates the index file
32+
- No need to manually create the `META-INF/maven/org.apache.maven.api.di.Inject` file
33+
34+
## Running the Test
35+
36+
1. Build and install the plugin:
37+
```
38+
cd extension
39+
mvn install
40+
```
41+
42+
2. Install the dummy artifact:
43+
```
44+
cd test
45+
./install-dummy.sh
46+
```
47+
48+
3. Run the test project:
49+
```
50+
cd test
51+
mvn validate
52+
```
53+
54+
4. Verify that the custom type handler is used:
55+
```
56+
[INFO] [MNG-8572] Registering custom type handler for type: custom-type
57+
```
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing,
14+
software distributed under the License is distributed on an
15+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
KIND, either express or implied. See the License for the
17+
specific language governing permissions and limitations
18+
under the License.
19+
-->
20+
<project xmlns="http://maven.apache.org/POM/4.1.0" root="true">
21+
<modelVersion>4.1.0</modelVersion>
22+
23+
<groupId>org.apache.maven.its.mng8572</groupId>
24+
<artifactId>custom-type-maven-plugin</artifactId>
25+
<version>1.0-SNAPSHOT</version>
26+
<packaging>maven-plugin</packaging>
27+
28+
<name>MNG-8572 Custom Type Handler Maven Plugin</name>
29+
<description>Maven plugin with extensions=true that provides a custom artifact type handler using Maven API DI</description>
30+
31+
<properties>
32+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
33+
<maven.compiler.source>17</maven.compiler.source>
34+
<maven.compiler.target>17</maven.compiler.target>
35+
</properties>
36+
37+
<dependencies>
38+
<!-- Maven API for DI and SPI -->
39+
<dependency>
40+
<groupId>org.apache.maven</groupId>
41+
<artifactId>maven-api-core</artifactId>
42+
<version>4.0.0-rc-3</version>
43+
<scope>provided</scope>
44+
</dependency>
45+
<dependency>
46+
<groupId>org.apache.maven</groupId>
47+
<artifactId>maven-api-di</artifactId>
48+
<version>4.0.0-rc-3</version>
49+
<scope>provided</scope>
50+
</dependency>
51+
<dependency>
52+
<groupId>org.apache.maven</groupId>
53+
<artifactId>maven-api-spi</artifactId>
54+
<version>4.0.0-rc-3</version>
55+
<scope>provided</scope>
56+
</dependency>
57+
<dependency>
58+
<groupId>org.slf4j</groupId>
59+
<artifactId>slf4j-api</artifactId>
60+
<version>2.0.16</version>
61+
<scope>provided</scope>
62+
</dependency>
63+
</dependencies>
64+
65+
<build>
66+
<plugins>
67+
<!-- Configure as extension -->
68+
<plugin>
69+
<groupId>org.apache.maven.plugins</groupId>
70+
<artifactId>maven-plugin-plugin</artifactId>
71+
<version>4.0.0-beta-1</version>
72+
<extensions>true</extensions>
73+
<configuration>
74+
<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
75+
<extractors>
76+
<extractor>java-annotations</extractor>
77+
</extractors>
78+
</configuration>
79+
</plugin>
80+
81+
<!-- Compiler with annotation processing for DI index generation -->
82+
<plugin>
83+
<groupId>org.apache.maven.plugins</groupId>
84+
<artifactId>maven-compiler-plugin</artifactId>
85+
<version>3.14.0</version>
86+
<configuration>
87+
<release>17</release>
88+
<proc>full</proc>
89+
</configuration>
90+
</plugin>
91+
</plugins>
92+
</build>
93+
</project>

0 commit comments

Comments
 (0)