Coverage Report - org.apache.maven.extension.DefaultExtensionManager
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultExtensionManager
0 %
0/98
0 %
0/34
0
DefaultExtensionManager$ProjectArtifactExceptionFilter
0 %
0/6
0 %
0/4
0
 
 1  
 package org.apache.maven.extension;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *  http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import static org.apache.maven.container.ContainerUtils.findChildComponentHints;
 23  
 
 24  
 import org.apache.maven.MavenArtifactFilterManager; 
 25  
 import org.apache.maven.artifact.Artifact;
 26  
 import org.apache.maven.artifact.ArtifactUtils;
 27  
 import org.apache.maven.artifact.factory.ArtifactFactory;
 28  
 import org.apache.maven.artifact.handler.ArtifactHandler;
 29  
 import org.apache.maven.artifact.manager.WagonManager;
 30  
 import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
 31  
 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
 32  
 import org.apache.maven.artifact.metadata.ResolutionGroup;
 33  
 import org.apache.maven.artifact.repository.ArtifactRepository;
 34  
 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
 35  
 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
 36  
 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
 37  
 import org.apache.maven.artifact.resolver.ArtifactResolver;
 38  
 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
 39  
 import org.apache.maven.model.Extension;
 40  
 import org.apache.maven.plugin.DefaultPluginManager;
 41  
 import org.apache.maven.project.MavenProject;
 42  
 import org.apache.maven.wagon.Wagon;
 43  
 import org.codehaus.classworlds.ClassRealm;
 44  
 import org.codehaus.classworlds.ClassWorld;
 45  
 import org.codehaus.classworlds.DuplicateRealmException;
 46  
 import org.codehaus.classworlds.NoSuchRealmException;
 47  
 import org.codehaus.plexus.DefaultPlexusContainer;
 48  
 import org.codehaus.plexus.PlexusConstants;
 49  
 import org.codehaus.plexus.PlexusContainer;
 50  
 import org.codehaus.plexus.PlexusContainerException;
 51  
 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
 52  
 import org.codehaus.plexus.context.Context;
 53  
 import org.codehaus.plexus.context.ContextException;
 54  
 import org.codehaus.plexus.logging.AbstractLogEnabled;
 55  
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
 56  
 import org.codehaus.plexus.util.xml.Xpp3Dom;
 57  
 import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
 58  
 
 59  
 import java.io.File;
 60  
 import java.io.InputStream;
 61  
 import java.io.InputStreamReader;
 62  
 import java.util.Collections;
 63  
 import java.util.HashMap;
 64  
 import java.util.Iterator;
 65  
 import java.util.LinkedHashSet;
 66  
 import java.util.Map;
 67  
 import java.util.Set;
 68  
 import java.util.jar.JarFile;
 69  
 
 70  
 /**
 71  
  * Used to locate extensions.
 72  
  *
 73  
  * @author <a href="mailto:brett@apache.org">Brett Porter</a>
 74  
  * @author Jason van Zyl
 75  
  * @version $Id: DefaultExtensionManager.java 796891 2009-07-22 22:03:26Z jdcasey $
 76  
  */
 77  0
 public class DefaultExtensionManager
 78  
     extends AbstractLogEnabled
 79  
     implements ExtensionManager, Contextualizable
 80  
 {
 81  
     private ArtifactResolver artifactResolver;
 82  
 
 83  
     private ArtifactFactory artifactFactory;
 84  
 
 85  
     private ArtifactMetadataSource artifactMetadataSource;
 86  
 
 87  
     private DefaultPlexusContainer container;
 88  
 
 89  0
     private ArtifactFilter artifactFilter = MavenArtifactFilterManager.createExtensionFilter();
 90  
 
 91  
     private WagonManager wagonManager;
 92  
 
 93  
     private PlexusContainer extensionContainer;
 94  
 
 95  
     private static final String CONTAINER_NAME = "extensions";
 96  
 
 97  
     public void addExtension( Extension extension,
 98  
                               MavenProject project,
 99  
                               ArtifactRepository localRepository )
 100  
         throws ArtifactResolutionException, PlexusContainerException, ArtifactNotFoundException
 101  
     {
 102  0
         String extensionId = ArtifactUtils.versionlessKey( extension.getGroupId(), extension.getArtifactId() );
 103  
 
 104  0
         getLogger().debug( "Initialising extension: " + extensionId );
 105  
 
 106  0
         Artifact artifact = (Artifact) project.getExtensionArtifactMap().get( extensionId );
 107  
 
 108  0
         if ( artifact != null )
 109  
         {
 110  0
             ArtifactFilter filter = new ProjectArtifactExceptionFilter( artifactFilter, project.getArtifact() );
 111  
 
 112  
             ResolutionGroup resolutionGroup;
 113  
             try
 114  
             {
 115  0
                 resolutionGroup = artifactMetadataSource.retrieve( artifact, localRepository,
 116  
                                                                    project.getRemoteArtifactRepositories() );
 117  
             }
 118  0
             catch ( ArtifactMetadataRetrievalException e )
 119  
             {
 120  0
                 throw new ArtifactResolutionException( "Unable to download metadata from repository for plugin '" +
 121  
                     artifact.getId() + "': " + e.getMessage(), artifact, e );
 122  0
             }
 123  
 
 124  
             // We use the same hack here to make sure that plexus 1.1 is available for extensions that do
 125  
             // not declare plexus-utils but need it. MNG-2900
 126  0
             Set rgArtifacts = resolutionGroup.getArtifacts();
 127  0
             rgArtifacts = DefaultPluginManager.checkPlexusUtils( rgArtifacts, artifactFactory );
 128  
 
 129  0
             Set dependencies = new LinkedHashSet();
 130  0
             dependencies.add( artifact );
 131  0
             dependencies.addAll( rgArtifacts );
 132  
 
 133  
             // Make sure that we do not influence the dependenecy resolution of extensions with the project's
 134  
             // dependencyManagement
 135  
 
 136  0
             ArtifactResolutionResult result = artifactResolver.resolveTransitively( dependencies, project.getArtifact(),
 137  
                                                                                     Collections.EMPTY_MAP,
 138  
                                                                                     //project.getManagedVersionMap(),
 139  
                                                                                     localRepository,
 140  
                                                                                     project.getRemoteArtifactRepositories(),
 141  
                                                                                     artifactMetadataSource, filter );
 142  
 
 143  
             // gross hack for some backwards compat (MNG-2749)
 144  
             // if it is a lone artifact, then we assume it to be a resource package, and put it in the main container
 145  
             // as before. If it has dependencies, that's when we risk conflict and exile to the child container
 146  
             // jvz: we have to make this 2 because plexus is always added now.
 147  
 
 148  0
             Set artifacts = result.getArtifacts();
 149  
 
 150  
             // Lifecycles are loaded by the Lifecycle executor by looking up lifecycle definitions from the
 151  
             // core container. So we need to look if an extension has a lifecycle mapping and use the container
 152  
             // and not an extension container. (MNG-2831)
 153  
 
 154  0
             if ( extensionContainsLifeycle( artifact.getFile() ) )
 155  
             {
 156  0
                 for ( Iterator i = artifacts.iterator(); i.hasNext(); )
 157  
                 {
 158  0
                     Artifact a = (Artifact) i.next();
 159  
 
 160  0
                     if ( artifactFilter.include( a ) )
 161  
                     {
 162  0
                         getLogger().debug( "Adding extension to core container: " + a.getFile() );
 163  
 
 164  0
                         container.addJarResource( a.getFile() );
 165  
                     }
 166  0
                 }
 167  
             }
 168  0
             else if ( artifacts.size() == 2 )
 169  
             {
 170  0
                 for ( Iterator i = artifacts.iterator(); i.hasNext(); )
 171  
                 {
 172  0
                     Artifact a = (Artifact) i.next();
 173  
 
 174  0
                     if ( !a.getArtifactId().equals( "plexus-utils" ) )
 175  
                     {
 176  0
                         a = project.replaceWithActiveArtifact( a );
 177  
 
 178  0
                         getLogger().debug( "Adding extension to core container: " + a.getFile() );
 179  
 
 180  0
                         container.addJarResource( a.getFile() );
 181  
                     }
 182  0
                 }
 183  
             }
 184  
             else
 185  
             {
 186  
                 // create a child container for the extension
 187  
                 // TODO: this could surely be simpler/different on trunk with the new classworlds
 188  
 
 189  0
                 if ( extensionContainer == null )
 190  
                 {
 191  0
                     extensionContainer = createContainer();
 192  
                 }
 193  
 
 194  0
                 for ( Iterator i = result.getArtifacts().iterator(); i.hasNext(); )
 195  
                 {
 196  0
                     Artifact a = (Artifact) i.next();
 197  
 
 198  0
                     a = project.replaceWithActiveArtifact( a );
 199  
 
 200  0
                     getLogger().debug( "Adding to extension classpath: " + a.getFile() );
 201  
 
 202  0
                     extensionContainer.addJarResource( a.getFile() );
 203  0
                 }
 204  
 
 205  0
                 if ( getLogger().isDebugEnabled() )
 206  
                 {
 207  0
                     getLogger().debug( "Extension container contents:" );
 208  0
                     extensionContainer.getContainerRealm().display();
 209  
                 }
 210  
             }
 211  
         }
 212  0
     }
 213  
 
 214  
     private PlexusContainer createContainer()
 215  
         throws PlexusContainerException
 216  
     {
 217  0
         DefaultPlexusContainer child = new DefaultPlexusContainer();
 218  
 
 219  0
         ClassWorld classWorld = container.getClassWorld();
 220  0
         child.setClassWorld( classWorld );
 221  
 
 222  0
         ClassRealm childRealm = null;
 223  
 
 224  
         // note: ideally extensions would live in their own realm, but this would mean that things like wagon-scm would
 225  
         // have no way to obtain SCM extensions
 226  0
         String childRealmId = "plexus.core.child-container[" + CONTAINER_NAME + "]";
 227  
         try
 228  
         {
 229  0
             childRealm = classWorld.getRealm( childRealmId );
 230  
         }
 231  0
         catch ( NoSuchRealmException e )
 232  
         {
 233  
             try
 234  
             {
 235  0
                 childRealm = classWorld.newRealm( childRealmId );
 236  
             }
 237  0
             catch ( DuplicateRealmException impossibleError )
 238  
             {
 239  0
                 getLogger().error( "An impossible error has occurred. After getRealm() failed, newRealm() " +
 240  
                     "produced duplication error on same id!", impossibleError );
 241  0
             }
 242  0
         }
 243  
 
 244  0
         childRealm.setParent( container.getContainerRealm() );
 245  
 
 246  0
         child.setCoreRealm( childRealm );
 247  
 
 248  0
         child.setName( CONTAINER_NAME );
 249  
 
 250  
         // This is what we are skipping - we use the parent realm, but not the parent container since otherwise
 251  
         // we won't reload component descriptors that already exist in there
 252  
 //        child.setParentPlexusContainer( this );
 253  
 
 254  
         // ----------------------------------------------------------------------
 255  
         // Set all the child elements from the parent that were set
 256  
         // programmatically.
 257  
         // ----------------------------------------------------------------------
 258  
 
 259  0
         child.setLoggerManager( container.getLoggerManager() );
 260  
 
 261  0
         child.initialize();
 262  
 
 263  0
         child.start();
 264  
 
 265  0
         return child;
 266  
     }
 267  
 
 268  
     public void registerWagons()
 269  
     {
 270  0
         if ( extensionContainer != null )
 271  
         {
 272  0
             Set<String> wagons = findChildComponentHints( Wagon.ROLE, container, extensionContainer );
 273  0
             if ( wagons != null && !wagons.isEmpty() )
 274  
             {
 275  0
                 getLogger().debug( "Wagons to register: " + wagons );
 276  0
                 wagonManager.registerWagons( wagons, extensionContainer );
 277  
             }
 278  0
         }
 279  
         else
 280  
         {
 281  0
             getLogger().debug( "Wagons could not be registered as the extension container was never created" );
 282  
         }
 283  0
     }
 284  
     
 285  
     @SuppressWarnings( "unchecked" )
 286  
     public Map<String, ArtifactHandler> getArtifactTypeHandlers()
 287  
     {
 288  0
         Map<String, ArtifactHandler> result = new HashMap<String, ArtifactHandler>();
 289  
         
 290  0
         if ( extensionContainer != null )
 291  
         {
 292  
             try
 293  
             {
 294  0
                 result.putAll( extensionContainer.lookupMap( ArtifactHandler.ROLE ) );
 295  
             }
 296  0
             catch ( ComponentLookupException e )
 297  
             {
 298  0
                 getLogger().debug( "ArtifactHandler extensions could not be loaded: " + e.getMessage(), e );
 299  0
             }
 300  
         }
 301  
         else
 302  
         {
 303  
             try
 304  
             {
 305  0
                 result.putAll( container.lookupMap( ArtifactHandler.ROLE ) );
 306  
             }
 307  0
             catch ( ComponentLookupException e )
 308  
             {
 309  0
                 getLogger().debug( "ArtifactHandler extensions could not be loaded: " + e.getMessage(), e );
 310  0
             }
 311  
         }
 312  
         
 313  0
         return result;
 314  
     }
 315  
 
 316  
     public void contextualize( Context context )
 317  
         throws ContextException
 318  
     {
 319  0
         container = (DefaultPlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
 320  0
     }
 321  
 
 322  0
     private static final class ProjectArtifactExceptionFilter
 323  
         implements ArtifactFilter
 324  
     {
 325  
         private ArtifactFilter passThroughFilter;
 326  
 
 327  
         private String projectDependencyConflictId;
 328  
 
 329  
         ProjectArtifactExceptionFilter( ArtifactFilter passThroughFilter,
 330  
                                         Artifact projectArtifact )
 331  0
         {
 332  0
             this.passThroughFilter = passThroughFilter;
 333  0
             projectDependencyConflictId = projectArtifact.getDependencyConflictId();
 334  0
         }
 335  
 
 336  
         public boolean include( Artifact artifact )
 337  
         {
 338  0
             String depConflictId = artifact.getDependencyConflictId();
 339  
 
 340  0
             return projectDependencyConflictId.equals( depConflictId ) || passThroughFilter.include( artifact );
 341  
         }
 342  
     }
 343  
 
 344  
     private boolean extensionContainsLifeycle( File extension )
 345  
     {
 346  
         JarFile f;
 347  
 
 348  
         try
 349  
         {
 350  0
             f = new JarFile( extension );
 351  
 
 352  0
             InputStream is = f.getInputStream( f.getEntry( "META-INF/plexus/components.xml" ) );
 353  
 
 354  0
             if ( is == null )
 355  
             {
 356  0
                 return false;
 357  
             }
 358  
 
 359  0
             Xpp3Dom dom = Xpp3DomBuilder.build( new InputStreamReader( is ) );
 360  
 
 361  0
             Xpp3Dom[] components = dom.getChild( "components" ).getChildren( "component" );
 362  
 
 363  0
             for ( int i = 0; i < components.length; i++ )
 364  
             {
 365  0
                 if ( components[i].getChild( "role" ).getValue().equals( "org.apache.maven.lifecycle.mapping.LifecycleMapping" ) )
 366  
                 {
 367  0
                     return true;
 368  
                 }
 369  
             }
 370  
         }
 371  0
         catch( Exception e )
 372  
         {
 373  
             // do nothing
 374  0
         }
 375  
 
 376  0
         return false;
 377  
     }
 378  
 }