Skip to content
Merged
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 @@ -16,6 +16,7 @@
import org.codehaus.plexus.PlexusContainer;

import org.eclipse.m2e.core.embedder.IComponentLookup;
import org.eclipse.m2e.internal.maven.compat.PlexusContainerFacade;


/**
Expand All @@ -24,9 +25,9 @@
*/
public interface IMavenPlexusContainer {

static final String MVN_FOLDER = ".mvn";
static final String MVN_FOLDER = PlexusContainerFacade.MVN_FOLDER;

static final String EXTENSIONS_FILENAME = MVN_FOLDER + "/extensions.xml";
static final String EXTENSIONS_FILENAME = PlexusContainerFacade.EXTENSIONS_FILENAME;

/**
* Maven allows to use a magic {@value #MVN_FOLDER} folder where one can configure several aspects of the maven run
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,8 @@
package org.eclipse.m2e.core.internal.embedder;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -56,21 +53,17 @@
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.LoggerManager;

import org.apache.maven.cli.internal.BootstrapCoreExtensionManager;
import org.apache.maven.cli.internal.ExtensionResolutionException;
import org.apache.maven.cli.internal.extension.model.CoreExtension;
import org.apache.maven.cli.internal.extension.model.io.xpp3.CoreExtensionsXpp3Reader;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionRequestPopulator;
import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
import org.apache.maven.extension.internal.CoreExports;
import org.apache.maven.extension.internal.CoreExtensionEntry;
import org.apache.maven.session.scope.internal.SessionScopeModule;

import org.eclipse.m2e.core.embedder.IComponentLookup;
import org.eclipse.m2e.core.embedder.IMavenConfiguration;
import org.eclipse.m2e.core.internal.Messages;
import org.eclipse.m2e.internal.maven.compat.ExtensionResolutionExceptionFacade;
import org.eclipse.m2e.internal.maven.compat.PlexusContainerFacade;


/**
Expand All @@ -82,8 +75,6 @@
public class PlexusContainerManager {
private static final ILog LOG = Platform.getLog(PlexusContainerManager.class);

private static final String CONTAINER_CONFIGURATION_NAME = "maven";

private static final String MAVEN_EXTENSION_REALM_PREFIX = "maven.ext.";

private static final String PLEXUS_CORE_REALM = "plexus.core";
Expand Down Expand Up @@ -180,8 +171,7 @@ public IMavenPlexusContainer aquire(File basedir) throws Exception {
containerMap.put(canonicalDirectory, plexusContainer);
} catch(ExtensionResolutionException e) {
//TODO how can we create an error marker on the extension file?
File file = new File(directory, IMavenPlexusContainer.EXTENSIONS_FILENAME);
new ExtensionResolutionExceptionFacade(e).throwForFile(file);
ExtensionResolutionExceptionFacade.throwForFile(e, new File(directory, IMavenPlexusContainer.EXTENSIONS_FILENAME));
}
}
return plexusContainer;
Expand Down Expand Up @@ -215,15 +205,27 @@ private static IMavenPlexusContainer newPlexusContainer(File multiModuleProjectD
multiModuleProjectDirectory);
ClassRealm coreRealm = classWorld.getRealm(PLEXUS_CORE_REALM);
CoreExtensionEntry coreEntry = CoreExtensionEntry.discoverFrom(coreRealm);

List<CoreExtensionEntry> extensions = loadCoreExtensions(coreRealm, coreEntry, multiModuleProjectDirectory,
loggerManager, mavenConfiguration);
List<CoreExtensionEntry> extensions = PlexusContainerFacade.loadCoreExtensions(coreRealm, coreEntry,
multiModuleProjectDirectory, loggerManager, container -> {
Optional<MavenProperties> mavenProperties = MavenProperties.getMavenArgs(multiModuleProjectDirectory);
IMavenConfiguration workspaceConfiguration = IMavenConfiguration.getWorkspaceConfiguration();
MavenExecutionRequest request = MavenExecutionContext.createExecutionRequest(mavenConfiguration,
wrap(container), mavenProperties.map(mavenCfg -> mavenCfg.getSettingsLocations(workspaceConfiguration))
.orElseGet(workspaceConfiguration::getSettingsLocations),
multiModuleProjectDirectory);
container.lookup(MavenExecutionRequestPopulator.class).populateDefaults(request);
request.setBaseDirectory(multiModuleProjectDirectory);
request.setMultiModuleProjectDirectory(multiModuleProjectDirectory);
Properties userProperties = request.getUserProperties();
mavenProperties.ifPresent(prop -> prop.getCliProperties(userProperties::setProperty));
return request;
});
List<File> extClassPath = List.of(); //TODO should we allow to set an ext-class path for m2e?
ClassRealm containerRealm = setupContainerRealm(coreRealm, extClassPath, extensions);

ContainerConfiguration cc = new DefaultContainerConfiguration().setClassWorld(classWorld).setRealm(containerRealm)
.setClassPathScanning(PlexusConstants.SCANNING_INDEX).setAutoWiring(true).setJSR250Lifecycle(true)
.setName(CONTAINER_CONFIGURATION_NAME);
.setName(PlexusContainerFacade.CONTAINER_CONFIGURATION_NAME);

Set<String> exportedArtifacts = new HashSet<>(coreEntry.getExportedArtifacts());
Set<String> exportedPackages = new HashSet<>(coreEntry.getExportedPackages());
Expand All @@ -241,6 +243,7 @@ protected void configure() {
bind(CoreExports.class).toInstance(exports);
}
}, new ExtensionModule());
PlexusContainerFacade facade = new PlexusContainerFacade(container);
classWorld.addListener(new LifecycleManagerDisposer(container));
container.setLookupRealm(null);
Thread thread = Thread.currentThread();
Expand All @@ -249,8 +252,7 @@ protected void configure() {
thread.setContextClassLoader(container.getContainerRealm());
container.setLoggerManager(loggerManager);
for(CoreExtensionEntry extension : extensions) {
container.discoverComponents(extension.getClassRealm(), new SessionScopeModule(container),
new MojoExecutionScopeModule(container));
facade.loadExtension(extension);
}
} finally {
thread.setContextClassLoader(ccl);
Expand Down Expand Up @@ -304,60 +306,6 @@ private static ClassRealm setupContainerRealm(ClassRealm coreRealm, List<File> e
return extRealm;
}

private static List<CoreExtensionEntry> loadCoreExtensions(ClassRealm coreRealm, CoreExtensionEntry coreEntry,
File multiModuleProjectDirectory, LoggerManager loggerManager, IMavenConfiguration mavenConfiguration)
throws Exception {
if(multiModuleProjectDirectory == null) {
return Collections.emptyList();
}
File extensionsXml = new File(multiModuleProjectDirectory, IMavenPlexusContainer.EXTENSIONS_FILENAME);
if(!extensionsXml.isFile()) {
return Collections.emptyList();
}
List<CoreExtension> extensions;
try (InputStream is = new FileInputStream(extensionsXml)) {
extensions = new CoreExtensionsXpp3Reader().read(is).getExtensions();
}
if(extensions.isEmpty()) {
return Collections.emptyList();
}

ContainerConfiguration cc = new DefaultContainerConfiguration().setClassWorld(coreRealm.getWorld())
.setRealm(coreRealm).setClassPathScanning(PlexusConstants.SCANNING_INDEX).setAutoWiring(true)
.setJSR250Lifecycle(true).setName(CONTAINER_CONFIGURATION_NAME);

DefaultPlexusContainer container = new DefaultPlexusContainer(cc, new AbstractModule() {
@Override
protected void configure() {
bind(ILoggerFactory.class).toInstance(LoggerFactory.getILoggerFactory());
}
});

Thread thread = Thread.currentThread();
ClassLoader ccl = thread.getContextClassLoader();
try {
container.setLookupRealm(null);
container.setLoggerManager(loggerManager);
thread.setContextClassLoader(container.getContainerRealm());
Optional<MavenProperties> mavenProperties = MavenProperties.getMavenArgs(multiModuleProjectDirectory);
IMavenConfiguration workspaceConfiguration = IMavenConfiguration.getWorkspaceConfiguration();
MavenExecutionRequest request = MavenExecutionContext.createExecutionRequest(mavenConfiguration,
wrap(container), mavenProperties.map(mavenCfg -> mavenCfg.getSettingsLocations(workspaceConfiguration))
.orElseGet(workspaceConfiguration::getSettingsLocations),
multiModuleProjectDirectory);
container.lookup(MavenExecutionRequestPopulator.class).populateDefaults(request);
request.setBaseDirectory(multiModuleProjectDirectory);
request.setMultiModuleProjectDirectory(multiModuleProjectDirectory);
Properties userProperties = request.getUserProperties();
mavenProperties.ifPresent(prop -> prop.getCliProperties(userProperties::setProperty));
BootstrapCoreExtensionManager resolver = container.lookup(BootstrapCoreExtensionManager.class);
return resolver.loadCoreExtensions(request, coreEntry.getExportedArtifacts(), extensions);
} finally {
thread.setContextClassLoader(ccl);
container.dispose();
}
}

private static final class M2EClassWorld extends ClassWorld {

private File multiModuleProjectDirectory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,25 @@
import org.codehaus.plexus.PlexusContainerException;

/**
* Facade for {@link ExtensionResolutionException} to avoid direct usage that might change in Maven 4.
* This facade wraps the exception and provides a method to throw it as a PlexusContainerException
* with appropriate context information.
* Facade for {@link ExtensionResolutionException} to avoid direct usage that
* might change in Maven 4. This facade wraps the exception and provides a
* method to throw it as a PlexusContainerException with appropriate context
* information.
*/
public class ExtensionResolutionExceptionFacade {

private final ExtensionResolutionException exception;

public ExtensionResolutionExceptionFacade(ExtensionResolutionException exception) {
if(exception == null) {
throw new IllegalArgumentException("exception cannot be null");
}
this.exception = exception;
}

/**
* Throws a PlexusContainerException with information about the failed extension and the file where it was defined.
*
* @param file the extensions file where the failed extension was defined
* @throws PlexusContainerException always thrown with details about the failed extension
*/
public void throwForFile(File file) throws PlexusContainerException {
CoreExtension extension = exception.getExtension();
throw new PlexusContainerException(
"can't create plexus container because the extension " + extension.getGroupId() + ":"
+ extension.getArtifactId() + ":" + extension.getVersion() + " can't be loaded (defined in "
+ file.getAbsolutePath() + ").",
exception);
}
/**
* Throws a PlexusContainerException with information about the failed extension
* and the file where it was defined.
*
* @param file the extensions file where the failed extension was defined
* @throws PlexusContainerException always thrown with details about the failed
* extension
*/
public static void throwForFile(ExtensionResolutionException exception, File file) throws PlexusContainerException {
CoreExtension extension = exception.getExtension();
throw new PlexusContainerException("can't create plexus container because the extension "
+ extension.getGroupId() + ":" + extension.getArtifactId() + ":" + extension.getVersion()
+ " can't be loaded (defined in " + file.getAbsolutePath() + ").", exception);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/********************************************************************************
* Copyright (c) 2025 Christoph Läubrich and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christoph Läubrich - initial API and implementation
********************************************************************************/
package org.eclipse.m2e.internal.maven.compat;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;

import org.apache.maven.cli.internal.BootstrapCoreExtensionManager;
import org.apache.maven.cli.internal.extension.model.CoreExtension;
import org.apache.maven.cli.internal.extension.model.io.xpp3.CoreExtensionsXpp3Reader;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
import org.apache.maven.extension.internal.CoreExtensionEntry;
import org.apache.maven.session.scope.internal.SessionScopeModule;
import org.codehaus.plexus.ContainerConfiguration;
import org.codehaus.plexus.DefaultContainerConfiguration;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.LoggerManager;
import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;

import com.google.inject.AbstractModule;

public class PlexusContainerFacade {

public static final String CONTAINER_CONFIGURATION_NAME = "maven";

public static final String MVN_FOLDER = ".mvn";

public static final String EXTENSIONS_FILENAME = MVN_FOLDER + "/extensions.xml";

private PlexusContainer container;

public PlexusContainerFacade(PlexusContainer container) {
this.container = container;
}

public static List<CoreExtensionEntry> loadCoreExtensions(ClassRealm coreRealm, CoreExtensionEntry coreEntry,
File multiModuleProjectDirectory, LoggerManager loggerManager, MavenExecutionRequesFactory requestFactory)
throws Exception {
if (multiModuleProjectDirectory == null) {
return Collections.emptyList();
}
File extensionsXml = new File(multiModuleProjectDirectory, EXTENSIONS_FILENAME);
if (!extensionsXml.isFile()) {
return Collections.emptyList();
}
List<CoreExtension> extensions;
try (InputStream is = new FileInputStream(extensionsXml)) {
extensions = new CoreExtensionsXpp3Reader().read(is).getExtensions();
}
if (extensions.isEmpty()) {
return Collections.emptyList();
}

ContainerConfiguration cc = new DefaultContainerConfiguration().setClassWorld(coreRealm.getWorld())
.setRealm(coreRealm).setClassPathScanning(PlexusConstants.SCANNING_INDEX).setAutoWiring(true)
.setJSR250Lifecycle(true).setName(CONTAINER_CONFIGURATION_NAME);

DefaultPlexusContainer container = new DefaultPlexusContainer(cc, new AbstractModule() {
@Override
protected void configure() {
bind(ILoggerFactory.class).toInstance(LoggerFactory.getILoggerFactory());
}
});

Thread thread = Thread.currentThread();
ClassLoader ccl = thread.getContextClassLoader();
try {
container.setLookupRealm(null);
container.setLoggerManager(loggerManager);
thread.setContextClassLoader(container.getContainerRealm());

BootstrapCoreExtensionManager resolver = container.lookup(BootstrapCoreExtensionManager.class);
return resolver.loadCoreExtensions(requestFactory.createFor(container), coreEntry.getExportedArtifacts(),
extensions);
} finally {
thread.setContextClassLoader(ccl);
container.dispose();
}
}

public static interface MavenExecutionRequesFactory {
public MavenExecutionRequest createFor(PlexusContainer container) throws Exception;
}

public void loadExtension(CoreExtensionEntry extension) throws ComponentLookupException {
if (container instanceof DefaultPlexusContainer) {
((DefaultPlexusContainer) container).discoverComponents(extension.getClassRealm(),
new SessionScopeModule(container),
new MojoExecutionScopeModule(container));
}
}

}
Loading