diff --git a/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManager.java b/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManager.java
new file mode 100644
index 0000000000..53ab36b789
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManager.java
@@ -0,0 +1,52 @@
+package org.apache.maven.classrealm;
+
+/*
+ * 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.apache.maven.model.Model;
+import org.apache.maven.model.Plugin;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+
+/**
+ * Manages the class realms used by Maven. Warning: This is an internal utility interface that is only
+ * public for technical reasons, it is not part of the public API. In particular, this interface can be changed or
+ * deleted without prior notice.
+ *
+ * @author Benjamin Bentmann
+ */
+public interface ClassRealmManager
+{
+
+ /**
+ * Creates a new class realm for the specified project and its build extensions.
+ *
+ * @param model The model of the project for which to create a realm, must not be {@code null}.
+ * @return The new project realm, never {@code null}.
+ */
+ ClassRealm createProjectRealm( Model model );
+
+ /**
+ * Creates a new class realm for the specified plugin.
+ *
+ * @param plugin The plugin for which to create a realm, must not be {@code null}.
+ * @return The new plugin realm, never {@code null}.
+ */
+ ClassRealm createPluginRealm( Plugin plugin );
+
+}
diff --git a/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java b/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java
new file mode 100644
index 0000000000..81dde36194
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java
@@ -0,0 +1,238 @@
+package org.apache.maven.classrealm;
+
+/*
+ * 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.Random;
+
+import org.apache.maven.artifact.ArtifactUtils;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.Plugin;
+import org.codehaus.plexus.MutablePlexusContainer;
+import org.codehaus.plexus.PlexusContainer;
+import org.codehaus.plexus.classworlds.ClassWorld;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
+import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.component.annotations.Requirement;
+import org.codehaus.plexus.logging.Logger;
+
+/**
+ * Manages the class realms used by Maven. Warning: This is an internal utility class that is only
+ * public for technical reasons, it is not part of the public API. In particular, this interface can be changed or
+ * deleted without prior notice.
+ *
+ * @author Benjamin Bentmann
+ */
+@Component( role = ClassRealmManager.class )
+public class DefaultClassRealmManager
+ implements ClassRealmManager
+{
+
+ @Requirement
+ private Logger logger;
+
+ @Requirement
+ protected PlexusContainer container;
+
+ private ClassWorld getClassWorld()
+ {
+ return ( (MutablePlexusContainer) container ).getClassWorld();
+ }
+
+ private ClassRealm createRealm( String baseRealmId )
+ {
+ ClassWorld world = getClassWorld();
+
+ String realmId = baseRealmId;
+
+ Random random = new Random();
+
+ synchronized ( world )
+ {
+ ClassRealm classRealm;
+
+ while ( true )
+ {
+ try
+ {
+ classRealm = world.newRealm( realmId );
+
+ if ( logger.isDebugEnabled() )
+ {
+ logger.debug( "Created new class realm " + realmId );
+ }
+
+ break;
+ }
+ catch ( DuplicateRealmException e )
+ {
+ realmId = baseRealmId + '-' + random.nextInt();
+ }
+ }
+
+ classRealm.setParentRealm( getApiRealm() );
+
+ importXpp3Dom( classRealm );
+
+ return classRealm;
+ }
+ }
+
+ /**
+ * Gets the class realm that holds the Maven API classes that we intend to share with plugins and extensions. The
+ * API realm is basically a subset of the core realm and hides internal utility/implementation classes from
+ * plugins/extensions.
+ *
+ * @return The class realm for the Maven API, never {@code null}.
+ */
+ private ClassRealm getApiRealm()
+ {
+ return container.getContainerRealm();
+
+// TODO: MNG-4273, currently non-functional because the core artifact filter wipes out transitive plugin dependencies
+// like plexus-utils, too. We need to filter the result set of the plugin artifacts, not the graph.
+//
+// ClassWorld world = getClassWorld();
+//
+// String realmId = "maven.api";
+//
+// ClassRealm apiRealm;
+//
+// synchronized ( world )
+// {
+// apiRealm = world.getClassRealm( realmId );
+//
+// if ( apiRealm == null )
+// {
+// try
+// {
+// apiRealm = world.newRealm( realmId );
+// }
+// catch ( DuplicateRealmException e )
+// {
+// throw new IllegalStateException( "Failed to create API realm " + realmId, e );
+// }
+//
+// String coreRealmId = container.getContainerRealm().getId();
+// try
+// {
+// // components.xml
+// apiRealm.importFrom( coreRealmId, "META-INF/plexus" );
+//
+// // maven-*
+// apiRealm.importFrom( coreRealmId, "org.apache.maven." );
+//
+// // plexus-classworlds
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.classworlds" );
+//
+// // plexus-container, plexus-component-annotations
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.component" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.configuration" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.container" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.context" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.lifecycle" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.logging" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.personality" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.ComponentRegistry" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.ContainerConfiguration" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.DefaultComponentRegistry" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.DefaultContainerConfiguration" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.DefaultPlexusContainer" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.DuplicateChildContainerException" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.MutablePlexusContainer" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.PlexusConstants" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.PlexusContainer" );
+// apiRealm.importFrom( coreRealmId, "org.codehaus.plexus.PlexusContainerException" );
+// }
+// catch ( NoSuchRealmException e )
+// {
+// throw new IllegalStateException( e );
+// }
+//
+// try
+// {
+// container.discoverComponents( apiRealm );
+// }
+// catch ( Exception e )
+// {
+// throw new IllegalStateException( "Failed to discover components in API realm " + realmId, e );
+// }
+// }
+// }
+//
+// return apiRealm;
+ }
+
+ /**
+ * Imports Xpp3Dom and associated types into the specified realm. Unlike the other archives that constitute the API
+ * realm, plexus-utils is not excluded from the plugin/project realm, yet we must ensure this class is loaded from
+ * the API realm and not from the plugin/project realm.
+ *
+ * @param importingRealm The realm into which to import Xpp3Dom, must not be {@code null}.
+ */
+ private void importXpp3Dom( ClassRealm importingRealm )
+ {
+ String coreRealmId = container.getContainerRealm().getId();
+ try
+ {
+ importingRealm.importFrom( coreRealmId, "org.codehaus.plexus.util.xml.Xpp3Dom" );
+ importingRealm.importFrom( coreRealmId, "org.codehaus.plexus.util.xml.pull.XmlPullParser" );
+ importingRealm.importFrom( coreRealmId, "org.codehaus.plexus.util.xml.pull.XmlPullParserException" );
+ importingRealm.importFrom( coreRealmId, "org.codehaus.plexus.util.xml.pull.XmlSerializer" );
+ }
+ catch ( NoSuchRealmException e )
+ {
+ throw new IllegalStateException( e );
+ }
+ }
+
+ public ClassRealm createProjectRealm( Model model )
+ {
+ if ( model == null )
+ {
+ throw new IllegalArgumentException( "model missing" );
+ }
+
+ return createRealm( getKey( model ) );
+ }
+
+ private String getKey( Model model )
+ {
+ return "project>" + model.getGroupId() + ":" + model.getArtifactId() + ":" + model.getVersion();
+ }
+
+ public ClassRealm createPluginRealm( Plugin plugin )
+ {
+ if ( plugin == null )
+ {
+ throw new IllegalArgumentException( "plugin missing" );
+ }
+
+ return createRealm( getKey( plugin ) );
+ }
+
+ private String getKey( Plugin plugin )
+ {
+ String version = ArtifactUtils.toSnapshotVersion( plugin.getVersion() );
+ return "plugin>" + plugin.getGroupId() + ":" + plugin.getArtifactId() + ":" + version;
+ }
+
+}
diff --git a/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginManager.java b/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginManager.java
index f73fc0c7da..876d0f6009 100644
--- a/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginManager.java
+++ b/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginManager.java
@@ -41,6 +41,7 @@ import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ResolutionErrorHandler;
import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
+import org.apache.maven.classrealm.ClassRealmManager;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Plugin;
@@ -51,12 +52,8 @@ import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
import org.apache.maven.project.DuplicateArtifactAttachmentException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.RepositorySystem;
-import org.codehaus.plexus.MutablePlexusContainer;
import org.codehaus.plexus.PlexusContainer;
-import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
-import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
-import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException;
@@ -91,6 +88,9 @@ public class DefaultPluginManager
@Requirement
protected PlexusContainer container;
+ @Requirement
+ private ClassRealmManager classRealmManager;
+
@Requirement
protected ArtifactFilterManager coreArtifactFilterManager;
@@ -450,45 +450,8 @@ public class DefaultPluginManager
* Creates ClassRealm with unique id for the given plugin
*/
private ClassRealm createPluginRealm( Plugin plugin )
- throws PluginManagerException
{
- ClassWorld world = ((MutablePlexusContainer) container).getClassWorld();
-
- String baseRealmId = constructPluginKey( plugin );
- String realmId = baseRealmId;
-
- synchronized ( world )
- {
- for ( int i = 0; i < 100; i++ )
- {
- try
- {
- ClassRealm pluginRealm = world.newRealm( realmId );
- pluginRealm.setParentRealm( container.getContainerRealm() );
-
- String coreRealmId = container.getContainerRealm().getId();
- try
- {
- pluginRealm.importFrom( coreRealmId, "org.codehaus.plexus.util.xml.Xpp3Dom" );
- pluginRealm.importFrom( coreRealmId, "org.codehaus.plexus.util.xml.pull.XmlPullParser" );
- pluginRealm.importFrom( coreRealmId, "org.codehaus.plexus.util.xml.pull.XmlPullParserException" );
- pluginRealm.importFrom( coreRealmId, "org.codehaus.plexus.util.xml.pull.XmlSerializer" );
- }
- catch ( NoSuchRealmException e )
- {
- throw new IllegalStateException( e );
- }
-
- return pluginRealm;
- }
- catch ( DuplicateRealmException e )
- {
- realmId = baseRealmId + "-" + i;
- }
- }
- }
-
- throw new PluginManagerException( plugin, "Could not create ClassRealm for plugin " + baseRealmId, (Throwable) null );
+ return classRealmManager.createPluginRealm( plugin );
}
private Mojo getConfiguredMojo( MavenSession session, MavenProject project, MojoExecution mojoExecution, ClassRealm pluginRealm )
diff --git a/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java b/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java
index bc8e917388..e3ee689d22 100644
--- a/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java
+++ b/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java
@@ -33,6 +33,7 @@ import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ResolutionErrorHandler;
+import org.apache.maven.classrealm.ClassRealmManager;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Extension;
@@ -40,17 +41,16 @@ import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.Repository;
import org.apache.maven.repository.RepositorySystem;
-import org.codehaus.plexus.MutablePlexusContainer;
import org.codehaus.plexus.PlexusContainer;
-import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
-import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
/**
- * Assists the project builder.
+ * Assists the project builder. Warning: This is an internal utility class that is only public for
+ * technical reasons, it is not part of the public API. In particular, this interface can be changed or deleted without
+ * prior notice.
*
* @author Benjamin Bentmann
*/
@@ -65,6 +65,9 @@ public class DefaultProjectBuildingHelper
@Requirement
private PlexusContainer container;
+ @Requirement
+ private ClassRealmManager classRealmManager;
+
@Requirement
private RepositorySystem repositorySystem;
@@ -125,32 +128,7 @@ public class DefaultProjectBuildingHelper
return projectRealm;
}
- String realmId = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion();
-
- if ( logger.isDebugEnabled() )
- {
- logger.debug( "Creating project realm " + realmId );
- }
-
- ClassWorld world = ( (MutablePlexusContainer) container ).getClassWorld();
-
- synchronized ( world )
- {
- projectRealm = world.getClassRealm( realmId );
-
- if ( projectRealm == null )
- {
- try
- {
- projectRealm = world.newRealm( realmId );
- projectRealm.setParentRealm( container.getContainerRealm() );
- }
- catch ( DuplicateRealmException e )
- {
- throw new IllegalStateException( "Failed to create project realm " + realmId, e );
- }
- }
- }
+ projectRealm = classRealmManager.createProjectRealm( model );
for ( Extension extension : build.getExtensions() )
{
@@ -180,7 +158,8 @@ public class DefaultProjectBuildingHelper
}
catch ( Exception e )
{
- throw new IllegalStateException( "Failed to discover components in project realm " + realmId, e );
+ throw new IllegalStateException( "Failed to discover components in project realm " + projectRealm.getId(),
+ e );
}
return projectRealm;