You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by be...@apache.org on 2009/09/28 15:56:26 UTC

svn commit: r819540 - in /maven/maven-3/trunk: ./ maven-core/src/main/java/org/apache/maven/classrealm/ maven-core/src/main/java/org/apache/maven/plugin/ maven-core/src/main/java/org/apache/maven/plugin/internal/ maven-core/src/main/java/org/apache/mav...

Author: bentmann
Date: Mon Sep 28 13:56:25 2009
New Revision: 819540

URL: http://svn.apache.org/viewvc?rev=819540&view=rev
Log:
[MNG-4381] Allow extension plugins to contribute non-core components to be reused by other plugins

Added:
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java   (with props)
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java   (with props)
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java   (with props)
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptor.java   (with props)
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptorBuilder.java   (with props)
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java   (with props)
    maven/maven-3/trunk/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java   (with props)
Modified:
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManager.java
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultModelBuildingListener.java
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
    maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingHelper.java
    maven/maven-3/trunk/maven-core/src/test/java/org/apache/maven/project/EmptyProjectBuildingHelper.java
    maven/maven-3/trunk/pom.xml

Modified: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManager.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManager.java?rev=819540&r1=819539&r2=819540&view=diff
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManager.java (original)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManager.java Mon Sep 28 13:56:25 2009
@@ -44,6 +44,14 @@
     ClassRealm createProjectRealm( Model model );
 
     /**
+     * Creates a new class realm for the specified build extension.
+     * 
+     * @param plugin The extension plugin for which to create a realm, must not be {@code null}.
+     * @return The new extension realm, never {@code null}.
+     */
+    ClassRealm createExtensionRealm( Plugin extension );
+
+    /**
      * Creates a new class realm for the specified plugin.
      * 
      * @param plugin The plugin for which to create a realm, must not be {@code null}.

Modified: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java?rev=819540&r1=819539&r2=819540&view=diff
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java (original)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java Mon Sep 28 13:56:25 2009
@@ -197,6 +197,16 @@
         return "project>" + model.getGroupId() + ":" + model.getArtifactId() + ":" + model.getVersion();
     }
 
+    public ClassRealm createExtensionRealm( Plugin plugin )
+    {
+        if ( plugin == null )
+        {
+            throw new IllegalArgumentException( "extension plugin missing" );
+        }
+
+        return createRealm( getKey( plugin, true ), null, null );
+    }
+
     public ClassRealm createPluginRealm( Plugin plugin, ClassLoader parent, List<String> imports )
     {
         if ( plugin == null )
@@ -204,13 +214,14 @@
             throw new IllegalArgumentException( "plugin missing" );
         }
 
-        return createRealm( getKey( plugin ), parent, imports );
+        return createRealm( getKey( plugin, false ), parent, imports );
     }
 
-    private String getKey( Plugin plugin )
+    private String getKey( Plugin plugin, boolean extension )
     {
         String version = ArtifactUtils.toSnapshotVersion( plugin.getVersion() );
-        return "plugin>" + plugin.getGroupId() + ":" + plugin.getArtifactId() + ":" + version;
+        return ( extension ? "extension>" : "plugin>" ) + plugin.getGroupId() + ":" + plugin.getArtifactId() + ":"
+            + version;
     }
 
     private List<ClassRealmManagerDelegate> getDelegates()

Added: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java?rev=819540&view=auto
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java (added)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java Mon Sep 28 13:56:25 2009
@@ -0,0 +1,231 @@
+package org.apache.maven.plugin;
+
+/*
+ * 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.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.repository.RepositoryRequest;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Exclusion;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.project.ExtensionDescriptor;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.codehaus.plexus.component.annotations.Component;
+
+/**
+ * Default extension realm cache implementation. Assumes cached data does not change.
+ */
+@Component( role = ExtensionRealmCache.class )
+public class DefaultExtensionRealmCache
+    implements ExtensionRealmCache
+{
+
+    private static class CacheKey
+    {
+
+        private final Plugin extension;
+
+        private final List<ArtifactRepository> repositories = new ArrayList<ArtifactRepository>();
+
+        private final int hashCode;
+
+        public CacheKey( Plugin extension, RepositoryRequest repositoryRequest )
+        {
+            this.extension = extension.clone();
+            this.repositories.add( repositoryRequest.getLocalRepository() );
+            this.repositories.addAll( repositoryRequest.getRemoteRepositories() );
+
+            int hash = 17;
+            hash = hash * 31 + extensionHashCode( extension );
+            hash = hash * 31 + repositories.hashCode();
+            this.hashCode = hash;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return hashCode;
+        }
+
+        @Override
+        public boolean equals( Object o )
+        {
+            if ( o == this )
+            {
+                return true;
+            }
+
+            if ( !( o instanceof CacheKey ) )
+            {
+                return false;
+            }
+
+            CacheKey other = (CacheKey) o;
+
+            return extensionEquals( extension, other.extension ) && eq( repositories, other.repositories );
+        }
+
+    }
+
+    private final Map<CacheKey, CacheRecord> cache = new HashMap<CacheKey, CacheRecord>();
+
+    public CacheRecord get( Plugin extension, RepositoryRequest repositoryRequest )
+    {
+        return cache.get( new CacheKey( extension, repositoryRequest ) );
+    }
+
+    public void put( Plugin extension, RepositoryRequest repositoryRequest, ClassRealm extensionRealm,
+                     List<Artifact> extensionArtifacts, ExtensionDescriptor extensionDescriptor )
+    {
+        if ( extensionRealm == null || extensionArtifacts == null )
+        {
+            throw new NullPointerException();
+        }
+
+        CacheKey key = new CacheKey( extension, repositoryRequest );
+
+        if ( cache.containsKey( key ) )
+        {
+            throw new IllegalStateException( "Duplicate extension realm for extension " + extension.getId() );
+        }
+
+        CacheRecord record = new CacheRecord( extensionRealm, extensionArtifacts, extensionDescriptor );
+        cache.put( key, record );
+    }
+
+    public void flush()
+    {
+        cache.clear();
+    }
+
+    protected static int extensionHashCode( Plugin extension )
+    {
+        int hash = 17;
+
+        hash = hash * 31 + extension.getGroupId().hashCode();
+        hash = hash * 31 + extension.getArtifactId().hashCode();
+        hash = hash * 31 + extension.getVersion().hashCode();
+
+        for ( Dependency dependency : extension.getDependencies() )
+        {
+            hash = hash * 31 + dependency.getGroupId().hashCode();
+            hash = hash * 31 + dependency.getArtifactId().hashCode();
+            hash = hash * 31 + dependency.getVersion().hashCode();
+            hash = hash * 31 + dependency.getType().hashCode();
+            hash = hash * 31 + ( dependency.getClassifier() != null ? dependency.getClassifier().hashCode() : 0 );
+            hash = hash * 31 + ( dependency.getScope() != null ? dependency.getScope().hashCode() : 0 );
+
+            for ( Exclusion exclusion : dependency.getExclusions() )
+            {
+                hash = hash * 31 + exclusion.getGroupId().hashCode();
+                hash = hash * 31 + exclusion.getArtifactId().hashCode();
+            }
+        }
+
+        return hash;
+    }
+
+    private static boolean extensionEquals( Plugin a, Plugin b )
+    {
+        return eq( a.getGroupId(), b.getGroupId() ) //
+            && eq( a.getArtifactId(), b.getArtifactId() ) //
+            && eq( a.getVersion(), b.getVersion() ) // 
+            && a.isExtensions() == b.isExtensions() //
+            && dependenciesEquals( a.getDependencies(), b.getDependencies() );
+    }
+
+    private static boolean dependenciesEquals( List<Dependency> a, List<Dependency> b )
+    {
+        if ( a.size() != b.size() )
+        {
+            return false;
+        }
+
+        Iterator<Dependency> aI = a.iterator();
+        Iterator<Dependency> bI = b.iterator();
+
+        while ( aI.hasNext() )
+        {
+            Dependency aD = aI.next();
+            Dependency bD = bI.next();
+
+            boolean r = eq( aD.getGroupId(), bD.getGroupId() ) //
+                && eq( aD.getArtifactId(), bD.getArtifactId() ) //
+                && eq( aD.getVersion(), bD.getVersion() ) // 
+                && eq( aD.getType(), bD.getType() ) //
+                && eq( aD.getClassifier(), bD.getClassifier() ) //
+                && eq( aD.getScope(), bD.getScope() );
+
+            r &= exclusionsEquals( aD.getExclusions(), bD.getExclusions() );
+
+            if ( !r )
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static boolean exclusionsEquals( List<Exclusion> a, List<Exclusion> b )
+    {
+        if ( a.size() != b.size() )
+        {
+            return false;
+        }
+
+        Iterator<Exclusion> aI = a.iterator();
+        Iterator<Exclusion> bI = b.iterator();
+
+        while ( aI.hasNext() )
+        {
+            Exclusion aD = aI.next();
+            Exclusion bD = bI.next();
+
+            boolean r = eq( aD.getGroupId(), bD.getGroupId() ) //
+                && eq( aD.getArtifactId(), bD.getArtifactId() );
+
+            if ( !r )
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static <T> boolean eq( T s1, T s2 )
+    {
+        return s1 != null ? s1.equals( s2 ) : s2 == null;
+    }
+
+    public void register( MavenProject project, ClassRealm extensionRealm )
+    {
+        // default cache does not track extension usage
+    }
+
+}

Propchange: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java?rev=819540&view=auto
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java (added)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java Mon Sep 28 13:56:25 2009
@@ -0,0 +1,77 @@
+package org.apache.maven.plugin;
+
+/*
+ * 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.List;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.repository.RepositoryRequest;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.project.ExtensionDescriptor;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+
+/**
+ * Caches extension class realms. <strong>Warning:</strong> 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 Igor Fedorenko
+ * @author Benjamin Bentmann
+ */
+public interface ExtensionRealmCache
+{
+
+    public static class CacheRecord
+    {
+
+        public final ClassRealm realm;
+
+        public final List<Artifact> artifacts;
+
+        public final ExtensionDescriptor desciptor;
+
+        public CacheRecord( ClassRealm realm, List<Artifact> artifacts, ExtensionDescriptor descriptor )
+        {
+            this.realm = realm;
+            this.artifacts = artifacts;
+            this.desciptor = descriptor;
+        }
+
+    }
+
+    CacheRecord get( Plugin extension, RepositoryRequest repositoryRequest );
+
+    void put( Plugin extension, RepositoryRequest repositoryRequest, ClassRealm extensionRealm,
+              List<Artifact> extensionArtifacts, ExtensionDescriptor extensionDescriptor );
+
+    void flush();
+
+    /**
+     * Registers the specified extension realm for usage with the given project. Integrators can use the information
+     * collected from this method in combination with a custom cache implementation to dispose unused extension realms
+     * from the cache.
+     * 
+     * @param project The project that employs the extension realm, must not be {@code null}.
+     * @param extensionRealm The extension realm being used for the project, must not be {@code null}.
+     */
+    void register( MavenProject project, ClassRealm extensionRealm );
+
+}

Propchange: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Modified: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java?rev=819540&r1=819539&r2=819540&view=diff
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java (original)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java Mon Sep 28 13:56:25 2009
@@ -27,6 +27,7 @@
 import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -42,6 +43,7 @@
 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.artifact.resolver.filter.AndArtifactFilter;
 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
 import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
 import org.apache.maven.classrealm.ClassRealmManager;
@@ -339,13 +341,16 @@
             throw new IllegalArgumentException( "incomplete plugin descriptor, plugin artifact missing" );
         }
 
+        MavenProject project = session.getCurrentProject();
+
         RepositoryRequest request = new DefaultRepositoryRequest();
         request.setLocalRepository( session.getLocalRepository() );
-        request.setRemoteRepositories( session.getCurrentProject().getPluginArtifactRepositories() );
+        request.setRemoteRepositories( project.getPluginArtifactRepositories() );
         request.setCache( session.getRepositoryCache() );
         request.setOffline( session.isOffline() );
 
-        List<Artifact> pluginArtifacts = resolvePluginArtifacts( plugin, pluginArtifact, request );
+        List<Artifact> pluginArtifacts =
+            resolvePluginArtifacts( plugin, pluginArtifact, request, project.getExtensionArtifactFilter() );
 
         ClassRealm pluginRealm = classRealmManager.createPluginRealm( plugin, parent, imports );
 
@@ -417,7 +422,8 @@
      */
     // FIXME: only exposed to allow workaround for MNG-4194
     protected List<Artifact> resolvePluginArtifacts( Plugin plugin, Artifact pluginArtifact,
-                                                   RepositoryRequest repositoryRequest )
+                                                     RepositoryRequest repositoryRequest,
+                                                     ArtifactFilter extensionArtifactFilter )
         throws PluginResolutionException
     {
         Set<Artifact> overrideArtifacts = new LinkedHashSet<Artifact>();
@@ -430,6 +436,11 @@
 
         ArtifactFilter resolutionFilter = artifactFilterManager.getCoreArtifactFilter();
 
+        if ( extensionArtifactFilter != null )
+        {
+            resolutionFilter = new AndArtifactFilter( Arrays.asList( resolutionFilter, extensionArtifactFilter ) );
+        }
+
         ArtifactResolutionRequest request = new ArtifactResolutionRequest( repositoryRequest );
         request.setArtifact( pluginArtifact );
         request.setArtifactDependencies( overrideArtifacts );

Modified: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultModelBuildingListener.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultModelBuildingListener.java?rev=819540&r1=819539&r2=819540&view=diff
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultModelBuildingListener.java (original)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultModelBuildingListener.java Mon Sep 28 13:56:25 2009
@@ -25,6 +25,7 @@
 import org.apache.maven.artifact.repository.DefaultRepositoryRequest;
 import org.apache.maven.artifact.repository.RepositoryRequest;
 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
 import org.apache.maven.model.Model;
 import org.apache.maven.model.building.AbstractModelBuildingListener;
 import org.apache.maven.model.building.ModelBuildingEvent;
@@ -46,6 +47,8 @@
 
     private ClassRealm projectRealm;
 
+    private ArtifactFilter extensionArtifactFilter;
+
     private List<ArtifactRepository> remoteRepositories;
 
     private List<ArtifactRepository> pluginRepositories;
@@ -79,6 +82,16 @@
     }
 
     /**
+     * Gets the artifact filter to exclude extension artifacts from plugin realms.
+     * 
+     * @return The extension artifact filter or {@code null} if none.
+     */
+    public ArtifactFilter getExtentionArtifactFilter()
+    {
+        return extensionArtifactFilter;
+    }
+
+    /**
      * Gets the effective remote artifact repositories for the project. The repository list is created from the
      * repositories given by {@link ProjectBuildingRequest#getRemoteRepositories()} and the repositories given in the
      * POM, i.e. {@link Model#getRepositories()}. The POM repositories themselves also contain any repositories
@@ -113,17 +126,6 @@
 
         try
         {
-            remoteRepositories =
-                projectBuildingHelper.createArtifactRepositories( model.getRepositories(), remoteRepositories,
-                                                                  projectBuildingRequest );
-        }
-        catch ( Exception e )
-        {
-            event.getProblems().addError( "Invalid artifact repository: " + e.getMessage(), e );
-        }
-
-        try
-        {
             pluginRepositories =
                 projectBuildingHelper.createArtifactRepositories( model.getPluginRepositories(), pluginRepositories,
                                                                   projectBuildingRequest );
@@ -143,7 +145,11 @@
                 repositoryRequest.setRemoteRepositories( pluginRepositories );
                 repositoryRequest.setOffline( projectBuildingRequest.isOffline() );
 
-                projectRealm = projectBuildingHelper.createProjectRealm( model, repositoryRequest );
+                ProjectRealmCache.CacheRecord record =
+                    projectBuildingHelper.createProjectRealm( model, repositoryRequest );
+
+                projectRealm = record.realm;
+                extensionArtifactFilter = record.extensionArtifactFilter;
             }
             catch ( ArtifactResolutionException e )
             {
@@ -164,6 +170,18 @@
                 Thread.currentThread().setContextClassLoader( projectRealm );
             }
         }
+
+        // build the regular repos after extensions are loaded to allow for custom layouts
+        try
+        {
+            remoteRepositories =
+                projectBuildingHelper.createArtifactRepositories( model.getRepositories(), remoteRepositories,
+                                                                  projectBuildingRequest );
+        }
+        catch ( Exception e )
+        {
+            event.getProblems().addError( "Invalid artifact repository: " + e.getMessage(), e );
+        }
     }
 
 }

Modified: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java?rev=819540&r1=819539&r2=819540&view=diff
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java (original)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java Mon Sep 28 13:56:25 2009
@@ -486,6 +486,7 @@
         project.setPluginArtifactRepositories( listener.getPluginRepositories() );
 
         project.setClassRealm( listener.getProjectRealm() );
+        project.setExtensionArtifactFilter( listener.getExtentionArtifactFilter() );
 
         Build build = project.getBuild();
         project.addScriptSourceRoot( build.getScriptSourceDirectory() );

Modified: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java?rev=819540&r1=819539&r2=819540&view=diff
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java (original)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java Mon Sep 28 13:56:25 2009
@@ -19,10 +19,14 @@
  * under the License.
  */
 
+import java.io.IOException;
 import java.net.MalformedURLException;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.maven.ArtifactFilterManager;
@@ -35,6 +39,8 @@
 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
 import org.apache.maven.artifact.resolver.ResolutionErrorHandler;
 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.apache.maven.artifact.resolver.filter.ExclusionSetFilter;
+import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
 import org.apache.maven.classrealm.ClassRealmManager;
 import org.apache.maven.model.Build;
 import org.apache.maven.model.Dependency;
@@ -42,6 +48,7 @@
 import org.apache.maven.model.Model;
 import org.apache.maven.model.Plugin;
 import org.apache.maven.model.Repository;
+import org.apache.maven.plugin.ExtensionRealmCache;
 import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
 import org.apache.maven.plugin.version.PluginVersionRequest;
 import org.apache.maven.plugin.version.PluginVersionResolutionException;
@@ -75,6 +82,12 @@
     private ClassRealmManager classRealmManager;
 
     @Requirement
+    private ExtensionRealmCache extensionRealmCache;
+
+    @Requirement
+    private ProjectRealmCache projectRealmCache;
+
+    @Requirement
     private RepositorySystem repositorySystem;
 
     @Requirement
@@ -86,6 +99,8 @@
     @Requirement
     private PluginVersionResolver pluginVersionResolver;
 
+    private ExtensionDescriptorBuilder extensionDescriptorBuilder = new ExtensionDescriptorBuilder();
+
     public List<ArtifactRepository> createArtifactRepositories( List<Repository> pomRepositories,
                                                                 List<ArtifactRepository> externalRepositories,
                                                                 ProjectBuildingRequest request )
@@ -114,51 +129,48 @@
         return artifactRepositories;
     }
 
-    public ClassRealm createProjectRealm( Model model, RepositoryRequest repositoryRequest )
+    public synchronized ProjectRealmCache.CacheRecord createProjectRealm( Model model,
+                                                                          RepositoryRequest repositoryRequest )
         throws ArtifactResolutionException, PluginVersionResolutionException
     {
         ClassRealm projectRealm = null;
 
-        Build build = model.getBuild();
-
-        if ( build == null )
-        {
-            return projectRealm;
-        }
-
         List<Plugin> extensionPlugins = new ArrayList<Plugin>();
 
-        for ( Plugin plugin : build.getPlugins() )
+        Build build = model.getBuild();
+
+        if ( build != null )
         {
-            if ( plugin.isExtensions() )
+            for ( Extension extension : build.getExtensions() )
             {
+                Plugin plugin = new Plugin();
+                plugin.setGroupId( extension.getGroupId() );
+                plugin.setArtifactId( extension.getArtifactId() );
+                plugin.setVersion( extension.getVersion() );
                 extensionPlugins.add( plugin );
             }
+
+            for ( Plugin plugin : build.getPlugins() )
+            {
+                if ( plugin.isExtensions() )
+                {
+                    extensionPlugins.add( plugin );
+                }
+            }
         }
 
-        if ( build.getExtensions().isEmpty() && extensionPlugins.isEmpty() )
+        if ( extensionPlugins.isEmpty() )
         {
-            return projectRealm;
+            return new ProjectRealmCache.CacheRecord( null, null );
         }
 
-        projectRealm = classRealmManager.createProjectRealm( model );
+        List<ClassRealm> extensionRealms = new ArrayList<ClassRealm>();
 
-        for ( Extension extension : build.getExtensions() )
-        {
-            if ( extension.getVersion() == null )
-            {
-                PluginVersionRequest versionRequest = new DefaultPluginVersionRequest( repositoryRequest );
-                versionRequest.setGroupId( extension.getGroupId() );
-                versionRequest.setArtifactId( extension.getArtifactId() );
-                extension.setVersion( pluginVersionResolver.resolve( versionRequest ).getVersion() );
-            }
+        Map<ClassRealm, List<String>> exportedPackages = new HashMap<ClassRealm, List<String>>();
 
-            Artifact artifact =
-                repositorySystem.createArtifact( extension.getGroupId(), extension.getArtifactId(),
-                                                 extension.getVersion(), "jar" );
+        Map<ClassRealm, List<String>> exportedArtifacts = new HashMap<ClassRealm, List<String>>();
 
-            populateRealm( projectRealm, artifact, null, repositoryRequest );
-        }
+        List<Artifact> publicArtifacts = new ArrayList<Artifact>();
 
         for ( Plugin plugin : extensionPlugins )
         {
@@ -168,73 +180,206 @@
                 plugin.setVersion( pluginVersionResolver.resolve( versionRequest ).getVersion() );
             }
 
-            Artifact artifact = repositorySystem.createPluginArtifact( plugin );
+            ClassRealm extensionRealm;
+            List<Artifact> artifacts;
+            ExtensionDescriptor extensionDescriptor = null;
+
+            ExtensionRealmCache.CacheRecord record = extensionRealmCache.get( plugin, repositoryRequest );
 
-            Set<Artifact> dependencies = new LinkedHashSet<Artifact>();
-            for ( Dependency dependency : plugin.getDependencies() )
+            if ( record != null )
             {
-                dependencies.add( repositorySystem.createDependencyArtifact( dependency ) );
+                extensionRealm = record.realm;
+                artifacts = record.artifacts;
+                extensionDescriptor = record.desciptor;
             }
+            else
+            {
+                artifacts = resolveExtensionArtifacts( plugin, repositoryRequest );
 
-            populateRealm( projectRealm, artifact, dependencies, repositoryRequest );
-        }
+                extensionRealm = classRealmManager.createExtensionRealm( plugin );
 
-        try
-        {
-            container.discoverComponents( projectRealm );
-        }
-        catch ( Exception e )
-        {
-            throw new IllegalStateException( "Failed to discover components in project realm " + projectRealm.getId(),
-                                             e );
-        }
+                if ( logger.isDebugEnabled() )
+                {
+                    logger.debug( "Populating extension realm for " + plugin.getId() );
+                }
 
-        return projectRealm;
-    }
+                for ( Artifact artifact : artifacts )
+                {
+                    if ( artifact.getFile() != null )
+                    {
+                        if ( logger.isDebugEnabled() )
+                        {
+                            logger.debug( "  Included: " + artifact.getId() );
+                        }
+
+                        try
+                        {
+                            extensionRealm.addURL( artifact.getFile().toURI().toURL() );
+                        }
+                        catch ( MalformedURLException e )
+                        {
+                            // Not going to happen
+                        }
+                    }
+                    else
+                    {
+                        if ( logger.isDebugEnabled() )
+                        {
+                            logger.debug( "  Excluded: " + artifact.getId() );
+                        }
+                    }
+                }
 
-    private void populateRealm( ClassRealm realm, Artifact artifact, Set<Artifact> dependencies,
-                                RepositoryRequest repositoryRequest )
-        throws ArtifactResolutionException
-    {
-        ArtifactResolutionRequest request = new ArtifactResolutionRequest( repositoryRequest );
-        request.setArtifact( artifact );
-        request.setArtifactDependencies( dependencies );
-        request.setResolveTransitively( true );
-        // FIXME setTransferListener
+                try
+                {
+                    container.discoverComponents( extensionRealm );
+                }
+                catch ( Exception e )
+                {
+                    throw new IllegalStateException( "Failed to discover components in extension realm "
+                        + extensionRealm.getId(), e );
+                }
 
-        ArtifactResolutionResult result = repositorySystem.resolve( request );
+                Artifact extensionArtifact = artifacts.get( 0 );
+                try
+                {
+                    extensionDescriptor = extensionDescriptorBuilder.build( extensionArtifact.getFile() );
+                }
+                catch ( IOException e )
+                {
+                    String message = "Invalid extension descriptor for " + plugin.getId() + ": " + e.getMessage();
+                    if ( logger.isDebugEnabled() )
+                    {
+                        logger.error( message, e );
+                    }
+                    else
+                    {
+                        logger.error( message );
+                    }
+                }
 
-        resolutionErrorHandler.throwErrors( request, result );
+                extensionRealmCache.put( plugin, repositoryRequest, extensionRealm, artifacts, extensionDescriptor );
+            }
 
-        ArtifactFilter filter = artifactFilterManager.getCoreArtifactFilter();
+            extensionRealms.add( extensionRealm );
+            if ( extensionDescriptor != null )
+            {
+                exportedPackages.put( extensionRealm, extensionDescriptor.getExportedPackages() );
+                exportedArtifacts.put( extensionRealm, extensionDescriptor.getExportedArtifacts() );
+            }
 
-        for ( Artifact resultArtifact : result.getArtifacts() )
+            if ( !plugin.isExtensions() && artifacts.size() == 1 && artifacts.get( 0 ).getFile() != null )
+            {
+                /*
+                 * This is purely for backward-compat with 2.x where <extensions> consisting of a single artifact where
+                 * loaded into the core and hence available to plugins, in contrast to bigger extensions that were
+                 * loaded into a dedicated realm which is invisible to plugins.
+                 */
+                publicArtifacts.addAll( artifacts );
+            }
+        }
+
+        ProjectRealmCache.CacheRecord record = projectRealmCache.get( extensionRealms );
+
+        if ( record == null )
         {
-            if ( filter.include( resultArtifact ) )
+            projectRealm = classRealmManager.createProjectRealm( model );
+
+            if ( logger.isDebugEnabled() )
+            {
+                logger.debug( "Populating project realm for " + model.getId() );
+            }
+
+            for ( Artifact publicArtifact : publicArtifacts )
             {
                 if ( logger.isDebugEnabled() )
                 {
-                    logger.debug( "  Included: " + resultArtifact.getId() );
+                    logger.debug( "  Included: " + publicArtifact.getId() );
                 }
 
                 try
                 {
-                    realm.addURL( resultArtifact.getFile().toURI().toURL() );
+                    projectRealm.addURL( publicArtifact.getFile().toURI().toURL() );
                 }
                 catch ( MalformedURLException e )
                 {
-                    throw new IllegalStateException( "Failed to populate project realm " + realm.getId() + " with "
-                        + artifact.getFile(), e );
+                    // can't happen
                 }
             }
-            else
+
+            Set<String> exclusions = new LinkedHashSet<String>();
+
+            for ( ClassRealm extensionRealm : extensionRealms )
             {
-                if ( logger.isDebugEnabled() )
+                List<String> excludes = exportedArtifacts.get( extensionRealm );
+
+                if ( excludes != null )
+                {
+                    exclusions.addAll( excludes );
+                }
+
+                List<String> exports = exportedPackages.get( extensionRealm );
+
+                if ( exports == null || exports.isEmpty() )
+                {
+                    /*
+                     * Most existing extensions don't define exported packages, i.e. no classes are to be exposed to
+                     * plugins, yet the components provided by the extension (e.g. artifact handlers) must be
+                     * accessible, i.e. we still must import the extension realm into the project realm.
+                     */
+                    exports = Arrays.asList( extensionRealm.getId() );
+                }
+
+                for ( String export : exports )
                 {
-                    logger.debug( "  Excluded: " + resultArtifact.getId() );
+                    projectRealm.importFrom( extensionRealm, export );
                 }
             }
+
+            ArtifactFilter extensionArtifactFilter = null;
+            if ( !exclusions.isEmpty() )
+            {
+                extensionArtifactFilter = new ExclusionSetFilter( exclusions );
+            }
+
+            projectRealmCache.put( extensionRealms, projectRealm, extensionArtifactFilter );
+
+            record = new ProjectRealmCache.CacheRecord( projectRealm, extensionArtifactFilter );
+        }
+
+        return record;
+    }
+
+    private List<Artifact> resolveExtensionArtifacts( Plugin extensionPlugin, RepositoryRequest repositoryRequest )
+        throws ArtifactResolutionException
+    {
+        Artifact extensionArtifact = repositorySystem.createPluginArtifact( extensionPlugin );
+
+        Set<Artifact> overrideArtifacts = new LinkedHashSet<Artifact>();
+        for ( Dependency dependency : extensionPlugin.getDependencies() )
+        {
+            overrideArtifacts.add( repositorySystem.createDependencyArtifact( dependency ) );
         }
+
+        ArtifactFilter collectionFilter = new ScopeArtifactFilter( Artifact.SCOPE_RUNTIME_PLUS_SYSTEM );
+
+        ArtifactFilter resolutionFilter = artifactFilterManager.getCoreArtifactFilter();
+
+        ArtifactResolutionRequest request = new ArtifactResolutionRequest( repositoryRequest );
+        request.setArtifact( extensionArtifact );
+        request.setArtifactDependencies( overrideArtifacts );
+        request.setCollectionFilter( collectionFilter );
+        request.setResolutionFilter( resolutionFilter );
+        request.setResolveRoot( true );
+        request.setResolveTransitively( true );
+
+        ArtifactResolutionResult result = repositorySystem.resolve( request );
+
+        resolutionErrorHandler.throwErrors( request, result );
+
+        List<Artifact> extensionArtifacts = new ArrayList<Artifact>( result.getArtifacts() );
+
+        return extensionArtifacts;
     }
 
 }

Added: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java?rev=819540&view=auto
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java (added)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java Mon Sep 28 13:56:25 2009
@@ -0,0 +1,110 @@
+package org.apache.maven.project;
+
+/*
+ * 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.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.codehaus.plexus.component.annotations.Component;
+
+/**
+ * Default project realm cache implementation. Assumes cached data does not change.
+ */
+@Component( role = ProjectRealmCache.class )
+public class DefaultProjectRealmCache
+    implements ProjectRealmCache
+{
+
+    private static class CacheKey
+    {
+
+        private final List<? extends ClassRealm> extensionRealms;
+
+        private final int hashCode;
+
+        public CacheKey( List<? extends ClassRealm> extensionRealms )
+        {
+            this.extensionRealms = ( extensionRealms != null ) ? extensionRealms : Collections.<ClassRealm> emptyList();
+
+            this.hashCode = this.extensionRealms.hashCode();
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return hashCode;
+        }
+
+        @Override
+        public boolean equals( Object o )
+        {
+            if ( o == this )
+            {
+                return true;
+            }
+
+            if ( !( o instanceof CacheKey ) )
+            {
+                return false;
+            }
+
+            CacheKey other = (CacheKey) o;
+
+            return extensionRealms.equals( other.extensionRealms );
+        }
+
+    }
+
+    private final Map<CacheKey, CacheRecord> cache = new HashMap<CacheKey, CacheRecord>();
+
+    public CacheRecord get( List<? extends ClassRealm> extensionRealms )
+    {
+        return cache.get( new CacheKey( extensionRealms ) );
+    }
+
+    public void put( List<? extends ClassRealm> extensionRealms, ClassRealm projectRealm,
+                     ArtifactFilter extensionArtifactFilter )
+    {
+        if ( projectRealm == null )
+        {
+            throw new NullPointerException();
+        }
+
+        CacheKey key = new CacheKey( extensionRealms );
+
+        if ( cache.containsKey( key ) )
+        {
+            throw new IllegalStateException( "Duplicate project realm for extensions " + extensionRealms );
+        }
+
+        CacheRecord record = new CacheRecord( projectRealm, extensionArtifactFilter );
+        cache.put( key, record );
+    }
+
+    public void flush()
+    {
+        cache.clear();
+    }
+
+}

Propchange: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptor.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptor.java?rev=819540&view=auto
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptor.java (added)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptor.java Mon Sep 28 13:56:25 2009
@@ -0,0 +1,88 @@
+package org.apache.maven.project;
+
+/*
+ * 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.ArrayList;
+import java.util.List;
+
+/**
+ * Provides metadata about a build extension. <strong>Warning:</strong> This is an internal utility class that is only
+ * public for technical reasons, it is not part of the public API. In particular, this class can be changed or deleted
+ * without prior notice.
+ * 
+ * @author Benjamin Bentmann
+ */
+public class ExtensionDescriptor
+{
+
+    private List<String> exportedPackages;
+
+    private List<String> exportedArtifacts;
+
+    ExtensionDescriptor()
+    {
+        // hide constructor
+    }
+
+    public List<String> getExportedPackages()
+    {
+        if ( exportedPackages == null )
+        {
+            exportedPackages = new ArrayList<String>();
+        }
+
+        return exportedPackages;
+    }
+
+    public void setExportedPackages( List<String> exportedPackages )
+    {
+        if ( exportedPackages == null )
+        {
+            this.exportedPackages = null;
+        }
+        else
+        {
+            this.exportedPackages = new ArrayList<String>( exportedPackages );
+        }
+    }
+
+    public List<String> getExportedArtifacts()
+    {
+        if ( exportedArtifacts == null )
+        {
+            exportedArtifacts = new ArrayList<String>();
+        }
+
+        return exportedArtifacts;
+    }
+
+    public void setExportedArtifacts( List<String> exportedArtifacts )
+    {
+        if ( exportedArtifacts == null )
+        {
+            this.exportedArtifacts = null;
+        }
+        else
+        {
+            this.exportedArtifacts = new ArrayList<String>( exportedArtifacts );
+        }
+    }
+
+}

Propchange: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptor.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptorBuilder.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptorBuilder.java?rev=819540&view=auto
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptorBuilder.java (added)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptorBuilder.java Mon Sep 28 13:56:25 2009
@@ -0,0 +1,159 @@
+package org.apache.maven.project;
+
+/*
+ * 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.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.ReaderFactory;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Creates an extension descriptor from some XML stream.
+ * 
+ * @author Benjamin Bentmann
+ */
+class ExtensionDescriptorBuilder
+{
+
+    private String getExtensionDescriptorLocation()
+    {
+        return "META-INF/maven/extension.xml";
+    }
+
+    /**
+     * Extracts the extension descriptor (if any) from the specified JAR file.
+     * 
+     * @param extensionJar The JAR file or directory to extract the descriptor from, must not be {@code null}.
+     * @return The extracted descriptor or {@code null} if no descriptor was found.
+     * @throws IOException If the descriptor is present but could not be parsed.
+     */
+    public ExtensionDescriptor build( File extensionJar )
+        throws IOException
+    {
+        ExtensionDescriptor extensionDescriptor = null;
+
+        if ( extensionJar.isFile() )
+        {
+            JarFile pluginJar = new JarFile( extensionJar, false );
+            try
+            {
+                ZipEntry pluginDescriptorEntry = pluginJar.getEntry( getExtensionDescriptorLocation() );
+
+                if ( pluginDescriptorEntry != null )
+                {
+                    InputStream is = pluginJar.getInputStream( pluginDescriptorEntry );
+
+                    extensionDescriptor = build( is );
+                }
+            }
+            finally
+            {
+                pluginJar.close();
+            }
+        }
+        else
+        {
+            File pluginXml = new File( extensionJar, getExtensionDescriptorLocation() );
+
+            if ( pluginXml.canRead() )
+            {
+                InputStream is = new BufferedInputStream( new FileInputStream( pluginXml ) );
+                try
+                {
+                    extensionDescriptor = build( is );
+                }
+                finally
+                {
+                    IOUtil.close( is );
+                }
+            }
+        }
+
+        return extensionDescriptor;
+    }
+
+    ExtensionDescriptor build( InputStream is )
+        throws IOException
+    {
+        ExtensionDescriptor extensionDescriptor = new ExtensionDescriptor();
+
+        Xpp3Dom dom;
+        try
+        {
+            dom = Xpp3DomBuilder.build( ReaderFactory.newXmlReader( is ) );
+        }
+        catch ( XmlPullParserException e )
+        {
+            throw (IOException) new IOException( e.getMessage() ).initCause( e );
+        }
+        finally
+        {
+            IOUtil.close( is );
+        }
+
+        if ( !"extension".equals( dom.getName() ) )
+        {
+            throw new IOException( "Unexpected root element \"" + dom.getName() + "\", expected \"extension\"" );
+        }
+
+        extensionDescriptor.setExportedPackages( parseStrings( dom.getChild( "exportedPackages" ) ) );
+
+        extensionDescriptor.setExportedArtifacts( parseStrings( dom.getChild( "exportedArtifacts" ) ) );
+
+        return extensionDescriptor;
+    }
+
+    private List<String> parseStrings( Xpp3Dom dom )
+    {
+        List<String> strings = null;
+
+        if ( dom != null )
+        {
+            strings = new ArrayList<String>();
+
+            for ( Xpp3Dom child : dom.getChildren() )
+            {
+                String string = child.getValue();
+                if ( string != null )
+                {
+                    string = string.trim();
+                    if ( string.length() > 0 )
+                    {
+                        strings.add( string );
+                    }
+                }
+            }
+        }
+
+        return strings;
+    }
+
+}

Propchange: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptorBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptorBuilder.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Modified: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/MavenProject.java?rev=819540&r1=819539&r2=819540&view=diff
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/MavenProject.java (original)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/MavenProject.java Mon Sep 28 13:56:25 2009
@@ -167,6 +167,8 @@
 
     private ClassRealm classRealm;
 
+    private ArtifactFilter extensionArtifactFilter;
+
     //
 
     public MavenProject()
@@ -1948,7 +1950,9 @@
     }
 
     /**
-     * Sets the project's class realm.
+     * Sets the project's class realm. <strong>Warning:</strong> This is an internal utility method that is only public
+     * for technical reasons, it is not part of the public API. In particular, this method can be changed or deleted
+     * without prior notice and must not be used by plugins.
      * 
      * @param classRealm The class realm hosting the build extensions of this project, may be {@code null}.
      */
@@ -1959,6 +1963,9 @@
 
     /**
      * Gets the project's class realm. This class realm hosts the build extensions of the project.
+     * <strong>Warning:</strong> This is an internal utility method that is only public for technical reasons, it is not
+     * part of the public API. In particular, this method can be changed or deleted without prior notice and must not be
+     * used by plugins.
      * 
      * @return The project's class realm or {@code null}.
      */
@@ -1967,4 +1974,28 @@
         return classRealm;
     }
 
+    /**
+     * Sets the artifact filter used to exclude shared extension artifacts from plugin realms. <strong>Warning:</strong>
+     * This is an internal utility method that is only public for technical reasons, it is not part of the public API.
+     * In particular, this method can be changed or deleted without prior notice and must not be used by plugins.
+     * 
+     * @param extensionArtifactFilter The artifact filter to apply to plugins, may be {@code null}.
+     */
+    public void setExtensionArtifactFilter( ArtifactFilter extensionArtifactFilter )
+    {
+        this.extensionArtifactFilter = extensionArtifactFilter;
+    }
+
+    /**
+     * Gets the artifact filter used to exclude shared extension artifacts from plugin realms. <strong>Warning:</strong>
+     * This is an internal utility method that is only public for technical reasons, it is not part of the public API.
+     * In particular, this method can be changed or deleted without prior notice and must not be used by plugins.
+     * 
+     * @return The artifact filter or {@code null}.
+     */
+    public ArtifactFilter getExtensionArtifactFilter()
+    {
+        return extensionArtifactFilter;
+    }
+
 }

Modified: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingHelper.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingHelper.java?rev=819540&r1=819539&r2=819540&view=diff
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingHelper.java (original)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingHelper.java Mon Sep 28 13:56:25 2009
@@ -28,7 +28,6 @@
 import org.apache.maven.model.Model;
 import org.apache.maven.model.Repository;
 import org.apache.maven.plugin.version.PluginVersionResolutionException;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
 
 /**
  * Assists the project builder. <strong>Warning:</strong> This is an internal utility interface that is only public for
@@ -61,10 +60,10 @@
      * 
      * @param model The model to create the project realm for, must not be {@code null}
      * @param repositoryRequest The repository request to use for artifact resolution, must not be {@code null}.
-     * @return The project realm or {@code null} if the project uses no extensions.
+     * @return The record with the project realm and extension artifact filter, never {@code null}.
      * @throws ArtifactResolutionException If any build extension could not be resolved.
      */
-    ClassRealm createProjectRealm( Model model, RepositoryRequest repositoryRequest )
+    ProjectRealmCache.CacheRecord createProjectRealm( Model model, RepositoryRequest repositoryRequest )
         throws ArtifactResolutionException, PluginVersionResolutionException;
 
 }

Added: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java?rev=819540&view=auto
==============================================================================
--- maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java (added)
+++ maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java Mon Sep 28 13:56:25 2009
@@ -0,0 +1,60 @@
+package org.apache.maven.project;
+
+/*
+ * 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.List;
+
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+
+/**
+ * Caches project class realms. <strong>Warning:</strong> 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 Igor Fedorenko
+ * @author Benjamin Bentmann
+ */
+public interface ProjectRealmCache
+{
+
+    public static class CacheRecord
+    {
+
+        public final ClassRealm realm;
+
+        public final ArtifactFilter extensionArtifactFilter;
+
+        public CacheRecord( ClassRealm realm, ArtifactFilter extensionArtifactFilter )
+        {
+            this.realm = realm;
+            this.extensionArtifactFilter = extensionArtifactFilter;
+        }
+
+    }
+
+    CacheRecord get( List<? extends ClassRealm> extensionRealms );
+
+    void put( List<? extends ClassRealm> extensionRealms, ClassRealm projectRealm,
+              ArtifactFilter extensionArtifactFilter );
+
+    void flush();
+
+}

Propchange: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/maven-3/trunk/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Modified: maven/maven-3/trunk/maven-core/src/test/java/org/apache/maven/project/EmptyProjectBuildingHelper.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/test/java/org/apache/maven/project/EmptyProjectBuildingHelper.java?rev=819540&r1=819539&r2=819540&view=diff
==============================================================================
--- maven/maven-3/trunk/maven-core/src/test/java/org/apache/maven/project/EmptyProjectBuildingHelper.java (original)
+++ maven/maven-3/trunk/maven-core/src/test/java/org/apache/maven/project/EmptyProjectBuildingHelper.java Mon Sep 28 13:56:25 2009
@@ -28,7 +28,6 @@
 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
 import org.apache.maven.model.Model;
 import org.apache.maven.model.Repository;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
 import org.codehaus.plexus.component.annotations.Component;
 
 /**
@@ -56,10 +55,10 @@
         }
     }
 
-    public ClassRealm createProjectRealm( Model model, RepositoryRequest repositoryRequest )
+    public ProjectRealmCache.CacheRecord createProjectRealm( Model model, RepositoryRequest repositoryRequest )
         throws ArtifactResolutionException
     {
-        return null;
+        return new ProjectRealmCache.CacheRecord( null, null );
     }
 
 }

Added: maven/maven-3/trunk/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java?rev=819540&view=auto
==============================================================================
--- maven/maven-3/trunk/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java (added)
+++ maven/maven-3/trunk/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java Mon Sep 28 13:56:25 2009
@@ -0,0 +1,101 @@
+package org.apache.maven.project;
+
+/*
+ * 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.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests {@link ExtensionDescriptorBuilder}.
+ * 
+ * @author Benjamin Bentmann
+ */
+public class ExtensionDescriptorBuilderTest
+    extends TestCase
+{
+
+    private ExtensionDescriptorBuilder builder;
+
+    @Override
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+
+        builder = new ExtensionDescriptorBuilder();
+    }
+
+    @Override
+    protected void tearDown()
+        throws Exception
+    {
+        builder = null;
+
+        super.tearDown();
+    }
+
+    private InputStream toStream( String xml )
+    {
+        try
+        {
+            return new ByteArrayInputStream( xml.getBytes( "UTF-8" ) );
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            throw new IllegalStateException( e );
+        }
+    }
+
+    public void testEmptyDescriptor()
+        throws Exception
+    {
+        String xml = "<extension></extension>";
+
+        ExtensionDescriptor ed = builder.build( toStream( xml ) );
+
+        assertNotNull( ed );
+        assertNotNull( ed.getExportedPackages() );
+        assertTrue( ed.getExportedPackages().isEmpty() );
+        assertNotNull( ed.getExportedArtifacts() );
+        assertTrue( ed.getExportedArtifacts().isEmpty() );
+    }
+
+    public void testCompleteDescriptor()
+        throws Exception
+    {
+        String xml =
+            "<?xml version='1.0' encoding='UTF-8'?>" + "<extension>" + "<exportedPackages>"
+                + "<exportedPackage>a</exportedPackage>" + "<exportedPackage>b</exportedPackage>"
+                + "<exportedPackage>c</exportedPackage>" + "</exportedPackages>" + "<exportedArtifacts>"
+                + "<exportedArtifact>x</exportedArtifact>" + "<exportedArtifact>y</exportedArtifact>"
+                + "<exportedArtifact> z </exportedArtifact>" + "</exportedArtifacts>" + "</extension>";
+
+        ExtensionDescriptor ed = builder.build( toStream( xml ) );
+
+        assertNotNull( ed );
+        assertEquals( Arrays.asList( "a", "b", "c" ), ed.getExportedPackages() );
+        assertEquals( Arrays.asList( "x", "y", "z" ), ed.getExportedArtifacts() );
+    }
+
+}

Propchange: maven/maven-3/trunk/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/maven-3/trunk/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Modified: maven/maven-3/trunk/pom.xml
URL: http://svn.apache.org/viewvc/maven/maven-3/trunk/pom.xml?rev=819540&r1=819539&r2=819540&view=diff
==============================================================================
--- maven/maven-3/trunk/pom.xml (original)
+++ maven/maven-3/trunk/pom.xml Mon Sep 28 13:56:25 2009
@@ -38,11 +38,11 @@
   <inceptionYear>2001</inceptionYear>
 
   <properties>
-    <classWorldsVersion>2.1.0</classWorldsVersion>
+    <classWorldsVersion>2.2.0-SNAPSHOT</classWorldsVersion>
     <commonsCliVersion>1.2</commonsCliVersion>
     <easyMockVersion>1.2_Java1.3</easyMockVersion>
     <junitVersion>3.8.2</junitVersion>
-    <plexusVersion>1.2.1</plexusVersion>
+    <plexusVersion>1.3.0-SNAPSHOT</plexusVersion>
     <plexusInterpolationVersion>1.11</plexusInterpolationVersion>
     <plexusPluginManagerVersion>1.0-alpha-1</plexusPluginManagerVersion>
     <plexusUtilsVersion>2.0.0</plexusUtilsVersion>
@@ -158,6 +158,14 @@
     </site>
   </distributionManagement>
 
+  <!-- FIXME: Remove once snapshots have been released -->
+  <repositories>
+    <repository>
+      <id>sonatype.public</id>
+      <url>http://repository.sonatype.org/content/groups/public/</url>
+    </repository>
+  </repositories>
+
   <!--bootstrap-start-comment-->
   <dependencyManagement>
     <!--bootstrap-end-comment-->