Skip to content

[MNG-8572] Support DI beans in build extensions #2274

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,15 @@
import javax.inject.Named;
import javax.inject.Singleton;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.apache.maven.api.JavaPathType;
import org.apache.maven.api.Type;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.services.LanguageRegistry;
import org.apache.maven.api.services.Lookup;
import org.apache.maven.api.services.TypeRegistry;
import org.apache.maven.api.spi.TypeProvider;
import org.apache.maven.artifact.handler.ArtifactHandler;
Expand All @@ -41,12 +40,11 @@
import org.apache.maven.impl.resolver.type.DefaultType;

import static java.util.Objects.requireNonNull;
import static java.util.function.Function.identity;

@Named
@Singleton
public class DefaultTypeRegistry extends AbstractEventSpy implements TypeRegistry {
private final Map<String, Type> types;
private final Lookup lookup;

private final LanguageRegistry languageRegistry;

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

@Inject
public DefaultTypeRegistry(
List<TypeProvider> providers, LanguageRegistry languageRegistry, LegacyArtifactHandlerManager manager) {
this.types = requireNonNull(providers, "providers cannot be null").stream()
.flatMap(p -> p.provides().stream())
.collect(Collectors.toMap(Type::id, identity()));
public DefaultTypeRegistry(Lookup lookup, LanguageRegistry languageRegistry, LegacyArtifactHandlerManager manager) {
this.lookup = lookup;
this.languageRegistry = requireNonNull(languageRegistry, "languageRegistry cannot be null");
this.usedTypes = new ConcurrentHashMap<>();
this.manager = requireNonNull(manager, "artifactHandlerManager cannot be null");
Expand All @@ -84,7 +79,11 @@ public Optional<Type> lookup(String id) {
public Type require(String id) {
requireNonNull(id, "id cannot be null");
return usedTypes.computeIfAbsent(id, i -> {
Type type = types.get(id);
Type type = lookup.lookupList(TypeProvider.class).stream()
.flatMap(p -> p.provides().stream())
.filter(t -> Objects.equals(id, t.id()))
.findFirst()
.orElse(null);
if (type == null) {
// Copy data as the ArtifactHandler is not immutable, but Type should be.
ArtifactHandler handler = manager.getArtifactHandler(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import org.apache.maven.internal.impl.DefaultLog;
import org.apache.maven.internal.impl.DefaultMojoExecution;
import org.apache.maven.internal.impl.InternalMavenSession;
import org.apache.maven.internal.impl.SisuDiBridgeModule;
import org.apache.maven.internal.xml.XmlPlexusConfiguration;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.ContextEnabled;
Expand Down Expand Up @@ -440,12 +441,14 @@ private void discoverPluginComponents(
}
}

Thread.currentThread().setContextClassLoader(pluginRealm);
((DefaultPlexusContainer) container)
.discoverComponents(
pluginRealm,
new SessionScopeModule(container.lookup(SessionScope.class)),
new MojoExecutionScopeModule(container.lookup(MojoExecutionScope.class)),
new PluginConfigurationModule(plugin.getDelegate()));
new PluginConfigurationModule(plugin.getDelegate()),
new SisuDiBridgeModule(true));
} catch (ComponentLookupException | CycleDetectedInComponentGraphException e) {
throw new PluginContainerException(
plugin,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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.it;

import java.io.File;

import org.junit.jupiter.api.Test;

/**
* This is a test set for <a href="https://issues.apache.org/jira/browse/MNG-8572">MNG-8572</a>.
*
* It verifies that Maven plugins with extensions=true can provide custom artifact type handlers using the Maven API DI system.
*/
public class MavenITmng8572DITypeHandlerTest extends AbstractMavenIntegrationTestCase {

public MavenITmng8572DITypeHandlerTest() {
super("[4.0.0-rc-4,)");
}

@Test
public void testCustomTypeHandler() throws Exception {
// Build the extension first
File testDir = extractResources("/mng-8572-di-type-handler");
Verifier verifier = newVerifier(new File(testDir, "extension").getAbsolutePath());
verifier.setAutoclean(false);
verifier.deleteDirectory("target");
verifier.deleteArtifacts("org.apache.maven.its.mng8572");
verifier.addCliArgument("install");
verifier.execute();
verifier.verifyErrorFreeLog();

// Now use the extension in a test project
verifier = newVerifier(new File(testDir, "test").getAbsolutePath());
verifier.setAutoclean(false);
verifier.deleteDirectory("target");
verifier.addCliArgument("validate");
verifier.execute();
verifier.verifyErrorFreeLog();

// Verify that our custom type handler was used
verifier.verifyTextInLog("[INFO] Using custom type handler for type: custom-type");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public TestSuiteOrdering() {
* the tests are to finishing. Newer tests are also more likely to fail, so this is
* a fail fast technique as well.
*/
suite.addTestSuite(MavenITmng8572DITypeHandlerTest.class);
suite.addTestSuite(MavenITmng3558PropertyEscapingTest.class);
suite.addTestSuite(MavenITmng4559MultipleJvmArgsTest.class);
suite.addTestSuite(MavenITmng4559SpacesInJvmOptsTest.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# MNG-8572 Custom Type Handler with Maven API DI

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.

## Structure

- `extension/`: The Maven plugin with extensions=true that provides a custom artifact type handler
- Uses `org.apache.maven.api.di.Named` annotation
- Implements `org.apache.maven.api.spi.TypeProvider` interface
- Provides a custom type with ID "custom-type"
- Uses the Maven API DI system for component discovery
- The annotation processor automatically generates the DI index

- `test/`: A test project that uses the custom type handler
- References the plugin in the build section with `<extensions>true</extensions>`
- Has a dependency with `type="custom-type"`
- Verifies that the custom type handler is used

## Key Points

1. **Maven Plugin with extensions=true**:
- The plugin is configured with `<extensions>true</extensions>`
- This allows it to participate in the build process and provide custom components

2. **TypeProvider Implementation**:
- Implements the `org.apache.maven.api.spi.TypeProvider` interface
- Annotated with `@Named` from `org.apache.maven.api.di`
- Returns a custom implementation of the `Type` interface

3. **Annotation Processing**:
- The Maven API DI annotation processor automatically generates the index file
- No need to manually create the `META-INF/maven/org.apache.maven.api.di.Inject` file

## Running the Test

1. Build and install the plugin:
```
cd extension
mvn install
```

2. Install the dummy artifact:
```
cd test
./install-dummy.sh
```

3. Run the test project:
```
cd test
mvn validate
```

4. Verify that the custom type handler is used:
```
[INFO] Using custom type handler for type: custom-type
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?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.
-->
<project xmlns="http://maven.apache.org/POM/4.1.0">
<modelVersion>4.1.0</modelVersion>

<groupId>org.apache.maven.its.mng8572</groupId>
<artifactId>custom-type-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>maven-plugin</packaging>

<name>MNG-8572 Custom Type Handler Maven Plugin</name>
<description>Maven plugin with extensions=true that provides a custom artifact type handler using Maven API DI</description>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>

<dependencies>
<!-- Maven API for DI and SPI -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-api-core</artifactId>
<version>4.0.0-rc-3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-api-di</artifactId>
<version>4.0.0-rc-3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-api-spi</artifactId>
<version>4.0.0-rc-3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.16</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
<plugins>
<!-- Configure as extension -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>4.0.0-beta-1</version>
<extensions>true</extensions>
<configuration>
<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
<extractors>
<extractor>java-annotations</extractor>
</extractors>
</configuration>
</plugin>

<!-- Compiler with annotation processing for DI index generation -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
<configuration>
<release>17</release>
<proc>full</proc>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* 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.its.mng8572.extension;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

import org.apache.maven.api.Language;
import org.apache.maven.api.PathType;
import org.apache.maven.api.Type;

/**
* Implementation of a custom artifact type.
*/
public class CustomType implements Type {

private final String id;
private final Language language;
private final String extension;
private final String classifier;
private final boolean includesDependencies;
private final Set<PathType> pathTypes;

public CustomType(
String id,
Language language,
String extension,
String classifier,
boolean includesDependencies,
PathType... pathTypes) {
this.id = Objects.requireNonNull(id, "id cannot be null");
this.language = Objects.requireNonNull(language, "language cannot be null");
this.extension = Objects.requireNonNull(extension, "extension cannot be null");
this.classifier = classifier;
this.includesDependencies = includesDependencies;
this.pathTypes = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(pathTypes)));
}

@Override
public String id() {
return id;
}

@Override
public Language getLanguage() {
return language;
}

@Override
public String getExtension() {
return extension;
}

@Override
public String getClassifier() {
return classifier;
}

@Override
public boolean isIncludesDependencies() {
return includesDependencies;
}

@Override
public Set<PathType> getPathTypes() {
return pathTypes;
}
}
Loading
Loading