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;