[MNG-7055] Fix G level metadata handling (#555)
Maven Artifact Transfer silently prevents group level metadata to reach Resolver and causes metadata loss on install/deploy. Fix is to "bridge" this from maven-resolver-provider (and core) by reusing the actual metadata that m-plugin-p:addPluginArtifactMetadata mojo adds, but m-a-t filters out.
diff --git a/maven-compat/pom.xml b/maven-compat/pom.xml
index ed90947..7f8d4a3 100644
--- a/maven-compat/pom.xml
+++ b/maven-compat/pom.xml
@@ -109,6 +109,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-file</artifactId>
<scope>test</scope>
diff --git a/maven-compat/src/test/java/org/apache/maven/artifact/deployer/ArtifactDeployerTest.java b/maven-compat/src/test/java/org/apache/maven/artifact/deployer/ArtifactDeployerTest.java
index 53cc388..3f33fa1 100644
--- a/maven-compat/src/test/java/org/apache/maven/artifact/deployer/ArtifactDeployerTest.java
+++ b/maven-compat/src/test/java/org/apache/maven/artifact/deployer/ArtifactDeployerTest.java
@@ -24,12 +24,15 @@
import org.apache.maven.artifact.AbstractArtifactComponentTestCase;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.session.scope.internal.SessionScope;
import org.codehaus.plexus.util.FileUtils;
import org.junit.jupiter.api.Test;
import static org.codehaus.plexus.testing.PlexusExtension.getBasedir;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
import javax.inject.Inject;
@@ -42,6 +45,9 @@
@Inject
private ArtifactDeployer artifactDeployer;
+ @Inject
+ private SessionScope sessionScope;
+
protected String component()
{
return "deployer";
@@ -51,18 +57,28 @@
public void testArtifactInstallation()
throws Exception
{
- String artifactBasedir = new File( getBasedir(), "src/test/resources/artifact-install" ).getAbsolutePath();
+ sessionScope.enter();
+ try
+ {
+ sessionScope.seed(MavenSession.class, mock(MavenSession.class));
- Artifact artifact = createArtifact( "artifact", "1.0" );
+ String artifactBasedir = new File( getBasedir(), "src/test/resources/artifact-install" ).getAbsolutePath();
- File file = new File( artifactBasedir, "artifact-1.0.jar" );
- assertEquals( "dummy", FileUtils.fileRead( file, "UTF-8" ).trim() );
+ Artifact artifact = createArtifact( "artifact", "1.0" );
- artifactDeployer.deploy( file, artifact, remoteRepository(), localRepository() );
+ File file = new File( artifactBasedir, "artifact-1.0.jar" );
+ assertEquals( "dummy", FileUtils.fileRead( file, "UTF-8" ).trim() );
- ArtifactRepository remoteRepository = remoteRepository();
- File deployedFile = new File( remoteRepository.getBasedir(), remoteRepository.pathOf( artifact ) );
- assertTrue( deployedFile.exists() );
- assertEquals( "dummy", FileUtils.fileRead( deployedFile, "UTF-8" ).trim() );
+ artifactDeployer.deploy( file, artifact, remoteRepository(), localRepository() );
+
+ ArtifactRepository remoteRepository = remoteRepository();
+ File deployedFile = new File( remoteRepository.getBasedir(), remoteRepository.pathOf( artifact ) );
+ assertTrue( deployedFile.exists() );
+ assertEquals( "dummy", FileUtils.fileRead( deployedFile, "UTF-8" ).trim() );
+ }
+ finally
+ {
+ sessionScope.exit();
+ }
}
}
\ No newline at end of file
diff --git a/maven-compat/src/test/java/org/apache/maven/artifact/installer/ArtifactInstallerTest.java b/maven-compat/src/test/java/org/apache/maven/artifact/installer/ArtifactInstallerTest.java
index ca99ab2..afa2761 100644
--- a/maven-compat/src/test/java/org/apache/maven/artifact/installer/ArtifactInstallerTest.java
+++ b/maven-compat/src/test/java/org/apache/maven/artifact/installer/ArtifactInstallerTest.java
@@ -23,11 +23,14 @@
import org.apache.maven.artifact.AbstractArtifactComponentTestCase;
import org.apache.maven.artifact.Artifact;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.session.scope.internal.SessionScope;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;
import static org.codehaus.plexus.testing.PlexusExtension.getBasedir;
+import static org.mockito.Mockito.mock;
/**
* @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
@@ -38,6 +41,9 @@
@Inject
private ArtifactInstaller artifactInstaller;
+ @Inject
+ private SessionScope sessionScope;
+
protected String component()
{
return "installer";
@@ -47,14 +53,24 @@
public void testArtifactInstallation()
throws Exception
{
- String artifactBasedir = new File( getBasedir(), "src/test/resources/artifact-install" ).getAbsolutePath();
+ sessionScope.enter();
+ try
+ {
+ sessionScope.seed(MavenSession.class, mock(MavenSession.class));
- Artifact artifact = createArtifact( "artifact", "1.0" );
+ String artifactBasedir = new File( getBasedir(), "src/test/resources/artifact-install" ).getAbsolutePath();
- File source = new File( artifactBasedir, "artifact-1.0.jar" );
+ Artifact artifact = createArtifact( "artifact", "1.0" );
- artifactInstaller.install( source, artifact, localRepository() );
+ File source = new File( artifactBasedir, "artifact-1.0.jar" );
- assertLocalArtifactPresent( artifact );
+ artifactInstaller.install( source, artifact, localRepository() );
+
+ assertLocalArtifactPresent( artifact );
+ }
+ finally
+ {
+ sessionScope.exit();
+ }
}
}
\ No newline at end of file
diff --git a/maven-core/src/main/java/org/apache/maven/execution/infoproviders/DefaultPluginsMetadataInfoProvider.java b/maven-core/src/main/java/org/apache/maven/execution/infoproviders/DefaultPluginsMetadataInfoProvider.java
new file mode 100644
index 0000000..616a3a8
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/execution/infoproviders/DefaultPluginsMetadataInfoProvider.java
@@ -0,0 +1,137 @@
+package org.apache.maven.execution.infoproviders;
+
+/*
+ * 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.
+ */
+
+import java.util.Objects;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.apache.maven.artifact.repository.metadata.Metadata;
+import org.apache.maven.artifact.repository.metadata.Plugin;
+import org.apache.maven.artifact.repository.metadata.RepositoryMetadata;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.repository.internal.PluginsMetadataInfoProvider;
+import org.apache.maven.repository.legacy.metadata.ArtifactMetadata;
+import org.eclipse.aether.artifact.Artifact;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Default implementation of {@link PluginsMetadataInfoProvider}.
+ */
+@Named
+@Singleton
+public class DefaultPluginsMetadataInfoProvider
+ implements PluginsMetadataInfoProvider
+{
+ private final Provider<MavenSession> mavenSessionProvider;
+
+ @Inject
+ public DefaultPluginsMetadataInfoProvider( final Provider<MavenSession> mavenSessionProvider )
+ {
+ this.mavenSessionProvider = requireNonNull( mavenSessionProvider );
+ }
+
+ @Override
+ public PluginInfo getPluginInfo( final Artifact artifact )
+ {
+ MavenSession mavenSession = mavenSessionProvider.get();
+ if ( mavenSession != null )
+ {
+ MavenProject mavenProject = searchForProject( mavenSession, artifact );
+ if ( mavenProject != null && "maven-plugin".equals( mavenProject.getPackaging() ) )
+ {
+ Plugin plugin = searchForPluginGroupLevelRepositoryMetadata( mavenProject );
+
+ if ( plugin != null )
+ {
+ return new PluginInfo()
+ {
+ @Override
+ public String getPluginGroupId()
+ {
+ return artifact.getGroupId();
+ }
+
+ @Override
+ public String getPluginArtifactId()
+ {
+ return artifact.getArtifactId();
+ }
+
+ @Override
+ public String getPluginPrefix()
+ {
+ return plugin.getPrefix();
+ }
+
+ @Override
+ public String getPluginName()
+ {
+ return plugin.getName();
+ }
+ };
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private MavenProject searchForProject( MavenSession mavenSession, Artifact artifact )
+ {
+ for ( MavenProject mavenProject : mavenSession.getProjects() )
+ {
+ if ( mavenProject.getArtifact() != null
+ && Objects.equals( mavenProject.getGroupId(), artifact.getGroupId() )
+ && Objects.equals( mavenProject.getArtifactId(), artifact.getArtifactId() ) )
+ {
+ return mavenProject;
+ }
+ }
+ return null;
+ }
+
+ private Plugin searchForPluginGroupLevelRepositoryMetadata( MavenProject mavenProject )
+ {
+ org.apache.maven.artifact.Artifact projectArtifact = mavenProject.getArtifact();
+ for ( ArtifactMetadata artifactMetadata : projectArtifact.getMetadataList() )
+ {
+ if ( artifactMetadata instanceof RepositoryMetadata )
+ {
+ RepositoryMetadata repositoryMetadata = (RepositoryMetadata) artifactMetadata;
+ Metadata metadata = repositoryMetadata.getMetadata();
+
+ for ( Plugin plugin : metadata.getPlugins() )
+ {
+ if ( Objects.equals( plugin.getArtifactId(), mavenProject.getArtifactId() ) )
+ {
+ return plugin;
+ }
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/providers/MavenPluginLifecycleMappingProvider.java b/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/providers/MavenPluginLifecycleMappingProvider.java
index c01afc7..8ab0883 100644
--- a/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/providers/MavenPluginLifecycleMappingProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/providers/MavenPluginLifecycleMappingProvider.java
@@ -74,13 +74,11 @@
);
lifecyclePhases.put(
"install",
- // TODO: MNG-6556: Do not upgrade to 3.0.0-M1 is does not install the plugin prefix metadata
- new LifecyclePhase( "org.apache.maven.plugins:maven-install-plugin:2.5.2:install" )
+ new LifecyclePhase( "org.apache.maven.plugins:maven-install-plugin:3.0.0-M1:install" )
);
lifecyclePhases.put(
"deploy",
- // TODO: MNG-6556: Do not upgrade to 3.0.0-M1 is does not install the plugin prefix metadata
- new LifecyclePhase( "org.apache.maven.plugins:maven-deploy-plugin:2.8.2:deploy" )
+ new LifecyclePhase( "org.apache.maven.plugins:maven-deploy-plugin:3.0.0-M1:deploy" )
);
Lifecycle lifecycle = new Lifecycle();
diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/PluginsMetadata.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/PluginsMetadata.java
new file mode 100644
index 0000000..7a6c45a
--- /dev/null
+++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/PluginsMetadata.java
@@ -0,0 +1,117 @@
+package org.apache.maven.repository.internal;
+
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import org.apache.maven.artifact.repository.metadata.Metadata;
+import org.apache.maven.artifact.repository.metadata.Plugin;
+import org.apache.maven.repository.internal.PluginsMetadataInfoProvider.PluginInfo;
+import org.eclipse.aether.artifact.Artifact;
+
+/**
+ * Plugin G level metadata.
+ */
+final class PluginsMetadata
+ extends MavenMetadata
+{
+ private final PluginInfo pluginInfo;
+
+ PluginsMetadata( PluginInfo pluginInfo, Date timestamp )
+ {
+ super( createRepositoryMetadata( pluginInfo ), null, timestamp );
+ this.pluginInfo = pluginInfo;
+ }
+
+ PluginsMetadata( PluginInfo pluginInfo, File file, Date timestamp )
+ {
+ super( createRepositoryMetadata( pluginInfo ), file, timestamp );
+ this.pluginInfo = pluginInfo;
+ }
+
+ private static Metadata createRepositoryMetadata( PluginInfo pluginInfo )
+ {
+ Metadata result = new Metadata();
+ Plugin plugin = new Plugin();
+ plugin.setPrefix( pluginInfo.getPluginPrefix() );
+ plugin.setArtifactId( pluginInfo.getPluginArtifactId() );
+ plugin.setName( pluginInfo.getPluginName() );
+ result.getPlugins().add( plugin );
+ return result;
+ }
+
+ @Override
+ protected void merge( Metadata recessive )
+ {
+ List<Plugin> recessivePlugins = recessive.getPlugins();
+ List<Plugin> plugins = metadata.getPlugins();
+ if ( !plugins.isEmpty() )
+ {
+ LinkedHashMap<String, Plugin> mergedPlugins = new LinkedHashMap<>();
+ recessivePlugins.forEach( p -> mergedPlugins.put( p.getPrefix(), p ) );
+ plugins.forEach( p -> mergedPlugins.put( p.getPrefix(), p ) );
+ metadata.setPlugins( new ArrayList<>( mergedPlugins.values() ) );
+ }
+ }
+
+ public Object getKey()
+ {
+ return getGroupId();
+ }
+
+ public static Object getKey( Artifact artifact )
+ {
+ return artifact.getGroupId();
+ }
+
+ @Override
+ public MavenMetadata setFile( File file )
+ {
+ return new PluginsMetadata( pluginInfo, file, timestamp );
+ }
+
+ @Override
+ public String getGroupId()
+ {
+ return pluginInfo.getPluginGroupId();
+ }
+
+ @Override
+ public String getArtifactId()
+ {
+ return "";
+ }
+
+ @Override
+ public String getVersion()
+ {
+ return "";
+ }
+
+ @Override
+ public Nature getNature()
+ {
+ return Nature.RELEASE_OR_SNAPSHOT;
+ }
+}
diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/PluginsMetadataGenerator.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/PluginsMetadataGenerator.java
new file mode 100644
index 0000000..4f35568
--- /dev/null
+++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/PluginsMetadataGenerator.java
@@ -0,0 +1,130 @@
+package org.apache.maven.repository.internal;
+
+/*
+ * 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.
+ */
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.maven.repository.internal.PluginsMetadataInfoProvider.PluginInfo;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.deployment.DeployRequest;
+import org.eclipse.aether.impl.MetadataGenerator;
+import org.eclipse.aether.installation.InstallRequest;
+import org.eclipse.aether.metadata.Metadata;
+import org.eclipse.aether.util.ConfigUtils;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Plugin G level metadata.
+ */
+class PluginsMetadataGenerator
+ implements MetadataGenerator
+{
+ private final PluginsMetadataInfoProvider pluginsMetadataInfoProvider;
+
+ private final Map<Object, PluginsMetadata> plugins;
+
+ private final Map<Object, PluginsMetadata> processedPlugins;
+
+ private final Date timestamp;
+
+ PluginsMetadataGenerator( PluginsMetadataInfoProvider pluginsMetadataInfoProvider,
+ RepositorySystemSession session,
+ InstallRequest request )
+ {
+ this( pluginsMetadataInfoProvider, session, request.getMetadata() );
+ }
+
+ PluginsMetadataGenerator( PluginsMetadataInfoProvider pluginsMetadataInfoProvider,
+ RepositorySystemSession session,
+ DeployRequest request )
+ {
+ this( pluginsMetadataInfoProvider, session, request.getMetadata() );
+ }
+
+ private PluginsMetadataGenerator( PluginsMetadataInfoProvider pluginsMetadataInfoProvider,
+ RepositorySystemSession session,
+ Collection<? extends Metadata> metadatas )
+ {
+ this.pluginsMetadataInfoProvider = requireNonNull( pluginsMetadataInfoProvider );
+ this.plugins = new LinkedHashMap<>();
+ this.processedPlugins = new LinkedHashMap<>();
+ this.timestamp = (Date) ConfigUtils.getObject( session, new Date(), "maven.startTime" );
+
+ /*
+ * NOTE: This should be considered a quirk to support interop with Maven's legacy ArtifactDeployer which
+ * processes one artifact at a time and hence cannot associate the artifacts from the same project to use the
+ * same version index. Allowing the caller to pass in metadata from a previous deployment allows to re-establish
+ * the association between the artifacts of the same project.
+ */
+ for ( Iterator<? extends Metadata> it = metadatas.iterator(); it.hasNext(); )
+ {
+ Metadata metadata = it.next();
+ if ( metadata instanceof PluginsMetadata )
+ {
+ it.remove();
+ PluginsMetadata pluginMetadata = ( PluginsMetadata ) metadata;
+ processedPlugins.put( pluginMetadata.getKey(), pluginMetadata );
+ }
+ }
+ }
+
+ @Override
+ public Collection<? extends Metadata> prepare( Collection<? extends Artifact> artifacts )
+ {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Artifact transformArtifact( Artifact artifact )
+ {
+ return artifact;
+ }
+
+ @Override
+ public Collection<? extends Metadata> finish( Collection<? extends Artifact> artifacts )
+ {
+ for ( Artifact artifact : artifacts )
+ {
+ PluginInfo pluginInfo = pluginsMetadataInfoProvider.getPluginInfo( artifact );
+ if ( pluginInfo != null )
+ {
+ Object key = PluginsMetadata.getKey( artifact );
+ if ( processedPlugins.get( key ) == null )
+ {
+ PluginsMetadata pluginMetadata = plugins.get( key );
+ if ( pluginMetadata == null )
+ {
+ pluginMetadata = new PluginsMetadata( pluginInfo, timestamp );
+ plugins.put( key, pluginMetadata );
+ }
+ }
+ }
+ }
+
+ return plugins.values();
+ }
+}
diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/PluginsMetadataGeneratorFactory.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/PluginsMetadataGeneratorFactory.java
new file mode 100644
index 0000000..19d499b
--- /dev/null
+++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/PluginsMetadataGeneratorFactory.java
@@ -0,0 +1,67 @@
+package org.apache.maven.repository.internal;
+
+/*
+ * 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.
+ */
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.deployment.DeployRequest;
+import org.eclipse.aether.impl.MetadataGenerator;
+import org.eclipse.aether.impl.MetadataGeneratorFactory;
+import org.eclipse.aether.installation.InstallRequest;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Plugin G level metadata.
+ */
+@Named( "plugins" )
+@Singleton
+public class PluginsMetadataGeneratorFactory
+ implements MetadataGeneratorFactory
+{
+ private final PluginsMetadataInfoProvider pluginsMetadataInfoProvider;
+
+ @Inject
+ public PluginsMetadataGeneratorFactory( PluginsMetadataInfoProvider pluginsMetadataInfoProvider )
+ {
+ this.pluginsMetadataInfoProvider = requireNonNull( pluginsMetadataInfoProvider );
+ }
+
+ @Override
+ public MetadataGenerator newInstance( RepositorySystemSession session, InstallRequest request )
+ {
+ return new PluginsMetadataGenerator( pluginsMetadataInfoProvider, session, request );
+ }
+
+ @Override
+ public MetadataGenerator newInstance( RepositorySystemSession session, DeployRequest request )
+ {
+ return new PluginsMetadataGenerator( pluginsMetadataInfoProvider, session, request );
+ }
+
+ @Override
+ public float getPriority()
+ {
+ return 5;
+ }
+}
diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/PluginsMetadataInfoProvider.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/PluginsMetadataInfoProvider.java
new file mode 100644
index 0000000..629a23f
--- /dev/null
+++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/PluginsMetadataInfoProvider.java
@@ -0,0 +1,44 @@
+package org.apache.maven.repository.internal;
+
+/*
+ * 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.
+ */
+
+import org.eclipse.aether.artifact.Artifact;
+
+/**
+ * Plugin G level metadata provider.
+ */
+public interface PluginsMetadataInfoProvider
+{
+ interface PluginInfo
+ {
+ String getPluginGroupId();
+
+ String getPluginArtifactId();
+
+ String getPluginPrefix();
+
+ String getPluginName();
+ }
+
+ /**
+ * Returns {@link PluginInfo} corresponding for passed in {@link Artifact}, or {@code null}.
+ */
+ PluginInfo getPluginInfo( Artifact artifact );
+}