Skip to content

Commit e4f688f

Browse files
committed
[MNG-8572] Support DI beans in build extensions
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 08177a3 commit e4f688f

File tree

14 files changed

+546
-12
lines changed

14 files changed

+546
-12
lines changed

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

+10-11
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 = lookup;
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

+4-1
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;
@@ -440,12 +441,14 @@ private void discoverPluginComponents(
440441
}
441442
}
442443

444+
Thread.currentThread().setContextClassLoader(pluginRealm);
443445
((DefaultPlexusContainer) container)
444446
.discoverComponents(
445447
pluginRealm,
446448
new SessionScopeModule(container.lookup(SessionScope.class)),
447449
new MojoExecutionScopeModule(container.lookup(MojoExecutionScope.class)),
448-
new PluginConfigurationModule(plugin.getDelegate()));
450+
new PluginConfigurationModule(plugin.getDelegate()),
451+
new SisuDiBridgeModule(true));
449452
} catch (ComponentLookupException | CycleDetectedInComponentGraphException e) {
450453
throw new PluginContainerException(
451454
plugin,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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.addCliArgument("validate");
53+
verifier.execute();
54+
verifier.verifyErrorFreeLog();
55+
56+
// Verify that our custom type handler was used
57+
verifier.verifyTextInLog("[INFO] Using custom type handler for type: custom-type");
58+
}
59+
}

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

+1
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);
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] Using custom type handler for type: custom-type
57+
```
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">
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>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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.its.mng8572.extension;
20+
21+
import java.util.Arrays;
22+
import java.util.Collections;
23+
import java.util.HashSet;
24+
import java.util.Objects;
25+
import java.util.Set;
26+
27+
import org.apache.maven.api.Language;
28+
import org.apache.maven.api.PathType;
29+
import org.apache.maven.api.Type;
30+
31+
/**
32+
* Implementation of a custom artifact type.
33+
*/
34+
public class CustomType implements Type {
35+
36+
private final String id;
37+
private final Language language;
38+
private final String extension;
39+
private final String classifier;
40+
private final boolean includesDependencies;
41+
private final Set<PathType> pathTypes;
42+
43+
public CustomType(
44+
String id,
45+
Language language,
46+
String extension,
47+
String classifier,
48+
boolean includesDependencies,
49+
PathType... pathTypes) {
50+
this.id = Objects.requireNonNull(id, "id cannot be null");
51+
this.language = Objects.requireNonNull(language, "language cannot be null");
52+
this.extension = Objects.requireNonNull(extension, "extension cannot be null");
53+
this.classifier = classifier;
54+
this.includesDependencies = includesDependencies;
55+
this.pathTypes = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(pathTypes)));
56+
}
57+
58+
@Override
59+
public String id() {
60+
return id;
61+
}
62+
63+
@Override
64+
public Language getLanguage() {
65+
return language;
66+
}
67+
68+
@Override
69+
public String getExtension() {
70+
return extension;
71+
}
72+
73+
@Override
74+
public String getClassifier() {
75+
return classifier;
76+
}
77+
78+
@Override
79+
public boolean isIncludesDependencies() {
80+
return includesDependencies;
81+
}
82+
83+
@Override
84+
public Set<PathType> getPathTypes() {
85+
return pathTypes;
86+
}
87+
}

0 commit comments

Comments
 (0)