diff --git a/maven-compat/src/test/java/org/apache/maven/project/EmptyLifecycleExecutor.java b/maven-compat/src/test/java/org/apache/maven/project/EmptyLifecycleExecutor.java index 135b3e868e..d1e7643e23 100644 --- a/maven-compat/src/test/java/org/apache/maven/project/EmptyLifecycleExecutor.java +++ b/maven-compat/src/test/java/org/apache/maven/project/EmptyLifecycleExecutor.java @@ -25,11 +25,8 @@ import java.util.List; import java.util.Set; import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.*; import org.apache.maven.lifecycle.LifecycleExecutionException; -import org.apache.maven.lifecycle.LifecycleExecutor; -import org.apache.maven.lifecycle.LifecycleNotFoundException; -import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException; -import org.apache.maven.lifecycle.MavenExecutionPlan; import org.apache.maven.model.Plugin; import org.apache.maven.model.PluginExecution; import org.apache.maven.plugin.InvalidPluginDescriptorException; @@ -55,7 +52,7 @@ public class EmptyLifecycleExecutor throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, MojoNotFoundException { - return new MavenExecutionPlan( Collections. emptyList(), null, null ); + return new MavenExecutionPlan(null, null, null ); } public void execute( MavenSession session ) diff --git a/maven-core/src/main/java/org/apache/maven/DefaultMaven.java b/maven-core/src/main/java/org/apache/maven/DefaultMaven.java index 5507be2c5b..e8eee221e3 100644 --- a/maven-core/src/main/java/org/apache/maven/DefaultMaven.java +++ b/maven-core/src/main/java/org/apache/maven/DefaultMaven.java @@ -40,6 +40,7 @@ import org.apache.maven.execution.MavenExecutionResult; import org.apache.maven.execution.MavenSession; import org.apache.maven.execution.ProjectDependencyGraph; import org.apache.maven.lifecycle.LifecycleExecutor; +import org.apache.maven.lifecycle.internal.LifecycleWeaveBuilder; import org.apache.maven.model.building.ModelProblem; import org.apache.maven.model.building.ModelSource; import org.apache.maven.model.building.UrlModelSource; @@ -191,7 +192,8 @@ public class DefaultMaven // Reactor // Workspace // User Local Repository - delegatingLocalArtifactRepository.setBuildReactor( new ReactorArtifactRepository( projectMap, session ) ); + final boolean isWeaveMode = LifecycleWeaveBuilder.isWeaveMode( request); + delegatingLocalArtifactRepository.setBuildReactor( new ReactorArtifactRepository( projectMap, isWeaveMode ) ); } catch ( org.apache.maven.DuplicateProjectException e ) { @@ -245,7 +247,7 @@ public class DefaultMaven } result.setTopologicallySortedProjects( session.getProjects() ); - + if ( result.hasExceptions() ) { return result; diff --git a/maven-core/src/main/java/org/apache/maven/ReactorArtifactRepository.java b/maven-core/src/main/java/org/apache/maven/ReactorArtifactRepository.java index c32590f361..e882e0e6b6 100644 --- a/maven-core/src/main/java/org/apache/maven/ReactorArtifactRepository.java +++ b/maven-core/src/main/java/org/apache/maven/ReactorArtifactRepository.java @@ -1,19 +1,13 @@ package org.apache.maven; -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.ArtifactUtils; -import org.apache.maven.execution.MavenSession; import org.apache.maven.project.MavenProject; import org.apache.maven.repository.LocalArtifactRepository; +import java.io.File; +import java.util.*; + /** * An implementation of a repository that knows how to search the Maven reactor for artifacts. * @@ -27,11 +21,15 @@ public class ReactorArtifactRepository private Map> availableVersions; + private final boolean isWeaveMode; + private final int hashCode; - public ReactorArtifactRepository( Map reactorProjects, MavenSession session ) + @SuppressWarnings({"ConstantConditions"}) + public ReactorArtifactRepository( Map reactorProjects, boolean isWeaveMode ) { this.reactorProjects = reactorProjects; + this.isWeaveMode = isWeaveMode; hashCode = ( reactorProjects != null ) ? reactorProjects.keySet().hashCode() : 0; availableVersions = new HashMap>( reactorProjects.size() * 2 ); @@ -70,7 +68,7 @@ public class ReactorArtifactRepository Artifact projectArtifact = findMatchingArtifact( project, artifact ); - if ( projectArtifact != null && projectArtifact.getFile() != null && projectArtifact.getFile().exists() ) + if ( !isWeaveMode && (projectArtifact != null && projectArtifact.getFile() != null && projectArtifact.getFile().exists()) ) { //TODO: This is really completely wrong and should probably be based on the phase that is currently being executed. // If we are running before the packaging phase there is going to be no archive anyway, but if we are running prior to package diff --git a/maven-core/src/main/java/org/apache/maven/artifact/repository/DefaultRepositoryRequest.java b/maven-core/src/main/java/org/apache/maven/artifact/repository/DefaultRepositoryRequest.java index 51d663e4ff..189c6ebd4f 100644 --- a/maven-core/src/main/java/org/apache/maven/artifact/repository/DefaultRepositoryRequest.java +++ b/maven-core/src/main/java/org/apache/maven/artifact/repository/DefaultRepositoryRequest.java @@ -22,6 +22,8 @@ package org.apache.maven.artifact.repository; import java.util.ArrayList; import java.util.List; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; import org.apache.maven.repository.ArtifactTransferListener; /** @@ -68,6 +70,21 @@ public class DefaultRepositoryRequest setTransferListener( repositoryRequest.getTransferListener() ); } + public static RepositoryRequest getRepositoryRequest(MavenSession session, MavenProject project) { + RepositoryRequest request = new DefaultRepositoryRequest(); + + request.setCache(session.getRepositoryCache()); + request.setLocalRepository(session.getLocalRepository()); + if (project != null) { + request.setRemoteRepositories(project.getPluginArtifactRepositories()); + } + request.setOffline(session.isOffline()); + request.setForceUpdate(session.getRequest().isUpdateSnapshots()); + request.setTransferListener(session.getRequest().getTransferListener()); + + return request; + } + public boolean isOffline() { return offline; diff --git a/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java b/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java index 3d9b634b77..f7138fc213 100644 --- a/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java +++ b/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java @@ -201,7 +201,7 @@ public class DefaultExceptionHandler reference = exception.getClass().getSimpleName(); } } - else if ( exception instanceof LifecycleExecutionException ) + else if ( exception instanceof LifecycleExecutionException) { reference = getReference( exception.getCause() ); } diff --git a/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java b/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java index dd7fd8f46b..58f6f2a27e 100644 --- a/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java +++ b/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java @@ -126,6 +126,10 @@ public class DefaultMavenExecutionRequest private ExecutionListener executionListener; + private String threadCount; + private boolean perCoreThreadCount; + private boolean weaveMode; + /** * Suppress SNAPSHOT updates. * @@ -1024,4 +1028,28 @@ public class DefaultMavenExecutionRequest return this; } + public String getThreadCount() { + return threadCount; + } + + public void setThreadCount(String threadCount) { + this.threadCount = threadCount; + } + + public boolean isThreadConfigurationPresent() { + return getThreadCount() != null; + } + + public boolean isPerCoreThreadCount() { + return perCoreThreadCount; + } + + public void setPerCoreThreadCount(boolean perCoreThreadCount) { + this.perCoreThreadCount = perCoreThreadCount; + } + + public boolean isWeaveMode() { + return weaveMode; + } + } diff --git a/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java b/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java index 2ac483ea95..925e0599a4 100644 --- a/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java +++ b/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java @@ -152,6 +152,15 @@ public interface MavenExecutionRequest MavenExecutionRequest setMakeBehavior( String makeBehavior ); String getMakeBehavior(); + void setThreadCount( String threadCount ); + String getThreadCount(); + boolean isThreadConfigurationPresent(); + void setPerCoreThreadCount(boolean perCoreThreadCount); + boolean isPerCoreThreadCount(); + + boolean isWeaveMode(); + + // Recursive (really to just process the top-level POM) MavenExecutionRequest setRecursive( boolean recursive ); boolean isRecursive(); @@ -269,6 +278,6 @@ public interface MavenExecutionRequest ExecutionListener getExecutionListener(); MavenExecutionRequest setExecutionListener( ExecutionListener executionListener ); - ProjectBuildingRequest getProjectBuildingRequest(); + ProjectBuildingRequest getProjectBuildingRequest(); } diff --git a/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java b/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java index 115d6627e7..c8774846d7 100644 --- a/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java +++ b/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java @@ -22,6 +22,7 @@ package org.apache.maven.execution; import java.io.File; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; @@ -44,9 +45,10 @@ import org.codehaus.plexus.component.repository.exception.ComponentLookupExcepti * @version $Id$ */ public class MavenSession + implements Cloneable { private PlexusContainer container; - + private MavenExecutionRequest request; private MavenExecutionResult result; @@ -56,27 +58,28 @@ public class MavenSession private Properties executionProperties; private MavenProject currentProject; - + /** * These projects have already been topologically sorted in the {@link org.apache.maven.Maven} component before * being passed into the session. */ private List projects; - + private MavenProject topLevelProject; private ProjectDependencyGraph projectDependencyGraph; - private Collection blackListedProjects; + private boolean parallel; - private final Map>> pluginContextsByProjectAndPluginKey = - new ConcurrentHashMap>> (); + private final Map>> pluginContextsByProjectAndPluginKey = + new ConcurrentHashMap>>(); @Deprecated - public MavenSession( PlexusContainer container, MavenExecutionRequest request, MavenExecutionResult result, MavenProject project ) + public MavenSession( PlexusContainer container, MavenExecutionRequest request, MavenExecutionResult result, + MavenProject project ) { - this( container, request, result, Arrays.asList( new MavenProject[]{ project } ) ); - } + this( container, request, result, Arrays.asList( new MavenProject[]{project} ) ); + } @Deprecated public MavenSession( PlexusContainer container, Settings settings, ArtifactRepository localRepository, @@ -105,7 +108,8 @@ public class MavenSession } @Deprecated - public MavenSession( PlexusContainer container, MavenExecutionRequest request, MavenExecutionResult result, List projects ) + public MavenSession( PlexusContainer container, MavenExecutionRequest request, MavenExecutionResult result, + List projects ) { this.container = container; this.request = request; @@ -143,8 +147,8 @@ public class MavenSession this.topLevelProject = null; } this.projects = projects; - } - + } + @Deprecated public PlexusContainer getContainer() { @@ -198,7 +202,7 @@ public class MavenSession * Gets the user properties to use for interpolation and profile activation. The user properties have been * configured directly by the user on his discretion, e.g. via the {@code -Dkey=value} parameter on the command * line. - * + * * @return The user properties, never {@code null}. */ public Properties getUserProperties() @@ -209,7 +213,7 @@ public class MavenSession /** * Gets the system properties to use for interpolation and profile activation. The system properties are collected * from the runtime environment like {@link System#getProperties()} and environment variables. - * + * * @return The system properties, never {@code null}. */ public Properties getSystemProperties() @@ -237,7 +241,7 @@ public class MavenSession { return settings; } - + public List getProjects() { return projects; @@ -278,16 +282,16 @@ public class MavenSession { return request.getProjectBuildingRequest(); } - + public List getPluginGroups() { return request.getPluginGroups(); } - + public boolean isOffline() { return request.isOffline(); - } + } public MavenProject getTopLevelProject() { @@ -297,10 +301,10 @@ public class MavenSession public MavenExecutionResult getResult() { return result; - } - + } // Backward compat + public Map getPluginContext( PluginDescriptor plugin, MavenProject project ) { String projectKey = project.getId(); @@ -328,6 +332,11 @@ public class MavenSession return pluginContext; } + public ProjectDependencyGraph getProjectDependencyGraph() + { + return projectDependencyGraph; + } + public void setProjectDependencyGraph( ProjectDependencyGraph projectDependencyGraph ) { this.projectDependencyGraph = projectDependencyGraph; @@ -338,24 +347,16 @@ public class MavenSession return request.getReactorFailureBehavior(); } - public boolean isBlackListed( MavenProject project ) + @Override + public MavenSession clone() { - return blackListedProjects != null && blackListedProjects.contains( getId( project ) ); - } - - public void blackList( MavenProject project ) - { - if ( blackListedProjects == null ) + try { - blackListedProjects = new HashSet(); + return (MavenSession) super.clone(); } - - if ( blackListedProjects.add( getId( project ) ) && projectDependencyGraph != null ) + catch ( CloneNotSupportedException e ) { - for ( MavenProject downstreamProject : projectDependencyGraph.getDownstreamProjects( project, true ) ) - { - blackListedProjects.add( getId( downstreamProject ) ); - } + throw new RuntimeException( "Bug", e ); } } @@ -375,4 +376,11 @@ public class MavenSession return request.getStartTime(); } + public boolean isParallel() { + return parallel; + } + + public void setParallel(boolean parallel) { + this.parallel = parallel; + } } diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycleExecutor.java b/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycleExecutor.java index 908d633c03..f4740a2596 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycleExecutor.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycleExecutor.java @@ -1,239 +1,103 @@ -package org.apache.maven.lifecycle; - /* * 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. */ +package org.apache.maven.lifecycle; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.TreeMap; -import java.util.TreeSet; - -import org.apache.maven.ProjectDependenciesResolver; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.artifact.ArtifactUtils; -import org.apache.maven.artifact.repository.DefaultRepositoryRequest; -import org.apache.maven.artifact.repository.RepositoryRequest; -import org.apache.maven.artifact.resolver.ArtifactNotFoundException; -import org.apache.maven.artifact.resolver.ArtifactResolutionException; -import org.apache.maven.artifact.resolver.MultipleArtifactsNotFoundException; -import org.apache.maven.artifact.resolver.filter.ArtifactFilter; -import org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter; -import org.apache.maven.execution.BuildFailure; -import org.apache.maven.execution.BuildSuccess; import org.apache.maven.execution.DefaultLifecycleEvent; import org.apache.maven.execution.ExecutionEvent; import org.apache.maven.execution.ExecutionListener; import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenExecutionResult; import org.apache.maven.execution.MavenSession; -import org.apache.maven.lifecycle.mapping.LifecycleMapping; -import org.apache.maven.model.Dependency; +import org.apache.maven.lifecycle.internal.BuildListCalculator; +import org.apache.maven.lifecycle.internal.ConcurrencyDependencyGraph; +import org.apache.maven.lifecycle.internal.LifecycleDebugLogger; +import org.apache.maven.lifecycle.internal.LifecycleModuleBuilder; +import org.apache.maven.lifecycle.internal.LifecycleTaskSegmentCalculator; +import org.apache.maven.lifecycle.internal.LifecycleThreadedBuilder; +import org.apache.maven.lifecycle.internal.LifecycleWeaveBuilder; +import org.apache.maven.lifecycle.internal.MojoDescriptorCreator; +import org.apache.maven.lifecycle.internal.ProjectBuildList; +import org.apache.maven.lifecycle.internal.ProjectIndex; +import org.apache.maven.lifecycle.internal.ProjectSegment; +import org.apache.maven.lifecycle.internal.ReactorBuildStatus; +import org.apache.maven.lifecycle.internal.ReactorContext; +import org.apache.maven.lifecycle.internal.TaskSegment; +import org.apache.maven.lifecycle.internal.ThreadConfigurationService; import org.apache.maven.model.Plugin; -import org.apache.maven.model.PluginExecution; -import org.apache.maven.model.PluginManagement; import org.apache.maven.plugin.InvalidPluginDescriptorException; import org.apache.maven.plugin.MojoExecution; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.MojoNotFoundException; -import org.apache.maven.plugin.PluginConfigurationException; import org.apache.maven.plugin.PluginDescriptorParsingException; -import org.apache.maven.plugin.BuildPluginManager; -import org.apache.maven.plugin.PluginManagerException; import org.apache.maven.plugin.PluginNotFoundException; import org.apache.maven.plugin.PluginResolutionException; import org.apache.maven.plugin.descriptor.MojoDescriptor; -import org.apache.maven.plugin.descriptor.Parameter; -import org.apache.maven.plugin.descriptor.PluginDescriptor; -import org.apache.maven.plugin.lifecycle.Execution; -import org.apache.maven.plugin.lifecycle.Phase; -import org.apache.maven.plugin.prefix.DefaultPluginPrefixRequest; import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; -import org.apache.maven.plugin.prefix.PluginPrefixRequest; -import org.apache.maven.plugin.prefix.PluginPrefixResolver; -import org.apache.maven.plugin.prefix.PluginPrefixResult; -import org.apache.maven.plugin.version.DefaultPluginVersionRequest; -import org.apache.maven.plugin.version.PluginVersionRequest; import org.apache.maven.plugin.version.PluginVersionResolutionException; -import org.apache.maven.plugin.version.PluginVersionResolver; import org.apache.maven.project.MavenProject; -import org.apache.maven.repository.RepositorySystem; -import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; -import org.codehaus.plexus.configuration.PlexusConfiguration; import org.codehaus.plexus.logging.Logger; -import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable; -import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException; -import org.codehaus.plexus.util.StringUtils; -import org.codehaus.plexus.util.xml.Xpp3Dom; -import org.codehaus.plexus.util.xml.pull.XmlPullParserException; -//TODO: The configuration for the lifecycle needs to be externalized so that I can use the annotations properly for the wiring and reference and external source for the lifecycle configuration. -//TODO: check for online status in the build plan and die if necessary +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; /** * @author Jason van Zyl + * @author Benjamin Bentmann + * @author Kristian Rosenvold */ +@Component(role = LifecycleExecutor.class) public class DefaultLifecycleExecutor - implements LifecycleExecutor, Initializable + implements LifecycleExecutor { + @Requirement + private LifeCyclePluginAnalyzer lifeCyclePluginAnalyzer; + + @Requirement + private DefaultLifecycles defaultLifeCycles; + @Requirement private Logger logger; @Requirement - private BuildPluginManager pluginManager; + private LifecycleModuleBuilder lifecycleModuleBuilder; @Requirement - protected RepositorySystem repositorySystem; + private LifecycleWeaveBuilder lifeCycleWeaveBuilder; @Requirement - private ProjectDependenciesResolver projectDependenciesResolver; + private LifecycleThreadedBuilder lifecycleThreadedBuilder; @Requirement - private PluginVersionResolver pluginVersionResolver; + private BuildListCalculator buildListCalculator; @Requirement - private PluginPrefixResolver pluginPrefixResolver; - - // @Configuration(source="org/apache/maven/lifecycle/lifecycles.xml") - private List lifecycles; + private LifecycleDebugLogger lifecycleDebugLogger; - /** - * We use this to display all the lifecycles available and their phases to users. Currently this is primarily - * used in the IDE integrations where a UI is presented to the user and they can select the lifecycle phase - * they would like to execute. - */ - private Map lifecycleMap; - - /** - * We use this to map all phases to the lifecycle that contains it. This is used so that a user can specify the - * phase they want to execute and we can easily determine what lifecycle we need to run. - */ - private Map phaseToLifecycleMap; - - /** - * These mappings correspond to packaging types, like WAR packaging, which configure a particular mojos - * to run in a given phase. - */ @Requirement - private Map lifecycleMappings; + private LifecycleTaskSegmentCalculator lifecycleTaskSegmentCalculator; - private void fireEvent( MavenSession session, MojoExecution mojoExecution, LifecycleEventCatapult catapult ) + @Requirement + private ThreadConfigurationService threadConfigService; + + public DefaultLifecycleExecutor() { - ExecutionListener listener = session.getRequest().getExecutionListener(); - - if ( listener != null ) - { - ExecutionEvent event = new DefaultLifecycleEvent( session, mojoExecution ); - - catapult.fire( listener, event ); - } - } - - private static String getKey( MavenProject project ) - { - return project.getGroupId() + ':' + project.getArtifactId() + ':' + project.getVersion(); - } - - private void debugReactorPlan( List projectBuilds ) - { - logger.debug( "=== REACTOR BUILD PLAN ================================================" ); - - for ( Iterator it = projectBuilds.iterator(); it.hasNext(); ) - { - ProjectBuild projectBuild = it.next(); - - logger.debug( "Project: " + projectBuild.project.getId() ); - logger.debug( "Tasks: " + projectBuild.taskSegment.tasks ); - logger.debug( "Style: " + ( projectBuild.taskSegment.aggregating ? "Aggregating" : "Regular" ) ); - - if ( it.hasNext() ) - { - logger.debug( "-----------------------------------------------------------------------" ); - } - } - - logger.debug( "=======================================================================" ); - } - - private void debugProjectPlan( MavenProject currentProject, MavenExecutionPlan executionPlan ) - { - logger.debug( "=== PROJECT BUILD PLAN ================================================" ); - logger.debug( "Project: " + getKey( currentProject ) ); - logger.debug( "Dependencies (collect): " + executionPlan.getRequiredCollectionScopes() ); - logger.debug( "Dependencies (resolve): " + executionPlan.getRequiredResolutionScopes() ); - - for ( MojoExecution mojoExecution : executionPlan.getExecutions() ) - { - debugMojoExecution( mojoExecution ); - } - - logger.debug( "=======================================================================" ); - } - - private void debugMojoExecution( MojoExecution mojoExecution ) - { - String mojoExecId = - mojoExecution.getGroupId() + ':' + mojoExecution.getArtifactId() + ':' + mojoExecution.getVersion() + ':' - + mojoExecution.getGoal() + " (" + mojoExecution.getExecutionId() + ')'; - - Map> forkedExecutions = mojoExecution.getForkedExecutions(); - if ( !forkedExecutions.isEmpty() ) - { - for ( Map.Entry> fork : forkedExecutions.entrySet() ) - { - logger.debug( "--- init fork of " + fork.getKey() + " for " + mojoExecId + " ---" ); - - for ( MojoExecution forkedExecution : fork.getValue() ) - { - debugMojoExecution( forkedExecution ); - } - - logger.debug( "--- exit fork of " + fork.getKey() + " for " + mojoExecId + " ---" ); - } - } - - logger.debug( "-----------------------------------------------------------------------" ); - logger.debug( "Goal: " + mojoExecId ); - logger.debug( "Style: " - + ( mojoExecution.getMojoDescriptor().isAggregator() ? "Aggregating" : "Regular" ) ); - logger.debug( "Configuration: " + mojoExecution.getConfiguration() ); - } - - private List getProjects( MavenProject project, MavenSession session, boolean aggregator ) - { - if ( aggregator ) - { - return session.getProjects(); - } - else - { - return Collections.singletonList( project ); - } } public void execute( MavenSession session ) @@ -242,1878 +106,178 @@ public class DefaultLifecycleExecutor MavenExecutionResult result = session.getResult(); - List projectBuilds; - - ProjectIndex projectIndex; - try { - if ( !session.isUsingPOMsFromFilesystem() && requiresProject( session ) ) + if ( !session.isUsingPOMsFromFilesystem() && lifecycleTaskSegmentCalculator.requiresProject( session ) ) { - throw new MissingProjectException( "The goal you specified requires a project to execute" - + " but there is no POM in this directory (" + session.getExecutionRootDirectory() + ")." - + " Please verify you invoked Maven from the correct directory." ); + throw new MissingProjectException( "The goal you specified requires a project to execute" + + " but there is no POM in this directory (" + session.getExecutionRootDirectory() + ")." + + " Please verify you invoked Maven from the correct directory." ); } - projectBuilds = calculateProjectBuilds( session ); + final MavenExecutionRequest executionRequest = session.getRequest(); + boolean isThreaded = executionRequest.isThreadConfigurationPresent(); + session.setParallel( isThreaded ); + + List taskSegments = buildListCalculator.calculateTaskSegments( session ); + + ProjectBuildList projectBuilds = buildListCalculator.calculateProjectBuilds( session, taskSegments ); if ( projectBuilds.isEmpty() ) { - throw new NoGoalSpecifiedException( "No goals have been specified for this build." - + " You must specify a valid lifecycle phase or a goal in the format : or" - + " :[:]:." - + " Available lifecycle phases are: " + getLifecyclePhaseList() + "." ); + throw new NoGoalSpecifiedException( "No goals have been specified for this build." + + " You must specify a valid lifecycle phase or a goal in the format : or" + + " :[:]:." + + " Available lifecycle phases are: " + defaultLifeCycles.getLifecyclePhaseList() + "." ); + } + + ProjectIndex projectIndex = new ProjectIndex( session.getProjects() ); + + if ( logger.isDebugEnabled() ) + { + lifecycleDebugLogger.debugReactorPlan( projectBuilds ); + } + + ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); + + ReactorBuildStatus reactorBuildStatus = new ReactorBuildStatus( session.getProjectDependencyGraph() ); + ReactorContext callableContext = + new ReactorContext( result, projectIndex, oldContextClassLoader, reactorBuildStatus ); + + if ( isThreaded ) + { + ExecutorService executor = threadConfigService.getExecutorService( executionRequest.getThreadCount(), + executionRequest.isPerCoreThreadCount(), + session.getProjects().size()); + try + { + + final boolean isWeaveMode = LifecycleWeaveBuilder.isWeaveMode( executionRequest ); + if ( isWeaveMode ) + { + lifecycleDebugLogger.logWeavePlan( session ); + CompletionService service = + new ExecutorCompletionService( executor ); + lifeCycleWeaveBuilder.build( projectBuilds, callableContext, taskSegments, session, service, + reactorBuildStatus ); + } + else + { + ConcurrencyDependencyGraph analyzer = + new ConcurrencyDependencyGraph( projectBuilds, session.getProjectDependencyGraph() ); + + CompletionService service = + new ExecutorCompletionService( executor ); + + lifecycleThreadedBuilder.build( session, callableContext, projectBuilds, taskSegments, analyzer, service ); + } + } + finally + { + executor.shutdown(); + } + } + else + { + singleThreadedBuild( session, callableContext, projectBuilds, taskSegments, reactorBuildStatus ); } - projectIndex = new ProjectIndex( session.getProjects() ); } - catch ( Exception e ) + + catch ( + + Exception e + + ) + { result.addException( e ); - - fireEvent( session, null, LifecycleEventCatapult.SESSION_ENDED ); - - return; - } - - if ( logger.isDebugEnabled() ) - { - debugReactorPlan( projectBuilds ); - } - - ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); - - for ( ProjectBuild projectBuild : projectBuilds ) - { - MavenProject currentProject = projectBuild.project; - - long buildStartTime = System.currentTimeMillis(); - - try - { - session.setCurrentProject( currentProject ); - - if ( session.isBlackListed( currentProject ) ) - { - fireEvent( session, null, LifecycleEventCatapult.PROJECT_SKIPPED ); - - continue; - } - - fireEvent( session, null, LifecycleEventCatapult.PROJECT_STARTED ); - - ClassRealm projectRealm = currentProject.getClassRealm(); - if ( projectRealm != null ) - { - Thread.currentThread().setContextClassLoader( projectRealm ); - } - - MavenExecutionPlan executionPlan = - calculateExecutionPlan( session, currentProject, projectBuild.taskSegment ); - - if ( logger.isDebugEnabled() ) - { - debugProjectPlan( currentProject, executionPlan ); - } - - // TODO: once we have calculated the build plan then we should accurately be able to download - // the project dependencies. Having it happen in the plugin manager is a tangled mess. We can optimize - // this later by looking at the build plan. Would be better to just batch download everything required - // by the reactor. - - List projectsToResolve = - getProjects( currentProject, session, projectBuild.taskSegment.aggregating ); - - for ( MavenProject project : projectsToResolve ) - { - resolveProjectDependencies( project, executionPlan.getRequiredCollectionScopes(), - executionPlan.getRequiredResolutionScopes(), session, - projectBuild.taskSegment.aggregating ); - } - - DependencyContext dependencyContext = - new DependencyContext( executionPlan, projectBuild.taskSegment.aggregating ); - - execute( session, executionPlan.getExecutions(), projectIndex, dependencyContext ); - - long buildEndTime = System.currentTimeMillis(); - - result.addBuildSummary( new BuildSuccess( currentProject, buildEndTime - buildStartTime ) ); - - fireEvent( session, null, LifecycleEventCatapult.PROJECT_SUCCEEDED ); - } - catch ( Exception e ) - { - result.addException( e ); - - long buildEndTime = System.currentTimeMillis(); - - result.addBuildSummary( new BuildFailure( currentProject, buildEndTime - buildStartTime, e ) ); - - fireEvent( session, null, LifecycleEventCatapult.PROJECT_FAILED ); - - if ( MavenExecutionRequest.REACTOR_FAIL_NEVER.equals( session.getReactorFailureBehavior() ) ) - { - // continue the build - } - else if ( MavenExecutionRequest.REACTOR_FAIL_AT_END.equals( session.getReactorFailureBehavior() ) ) - { - // continue the build but ban all projects that depend on the failed one - session.blackList( currentProject ); - } - else if ( MavenExecutionRequest.REACTOR_FAIL_FAST.equals( session.getReactorFailureBehavior() ) ) - { - // abort the build - break; - } - else - { - throw new IllegalArgumentException( "invalid reactor failure behavior " - + session.getReactorFailureBehavior() ); - } - } - finally - { - session.setCurrentProject( null ); - - Thread.currentThread().setContextClassLoader( oldContextClassLoader ); - } } fireEvent( session, null, LifecycleEventCatapult.SESSION_ENDED ); - } - - private void resolveProjectDependencies( MavenProject project, Collection scopesToCollect, - Collection scopesToResolve, MavenSession session, - boolean aggregating ) - throws LifecycleExecutionException - { - Set artifacts; - - try - { - try - { - artifacts = projectDependenciesResolver.resolve( project, scopesToCollect, scopesToResolve, session ); - } - catch ( MultipleArtifactsNotFoundException e ) - { - /* - * MNG-2277, the check below compensates for our bad plugin support where we ended up with aggregator - * plugins that require dependency resolution although they usually run in phases of the build where project - * artifacts haven't been assembled yet. The prime example of this is "mvn release:prepare". - */ - if ( aggregating && areAllArtifactsInReactor( session.getProjects(), e.getMissingArtifacts() ) ) - { - logger.warn( "The following artifacts could not be resolved at this point of the build" - + " but seem to be part of the reactor:" ); - - for ( Artifact artifact : e.getMissingArtifacts() ) - { - logger.warn( "o " + artifact.getId() ); - } - - logger.warn( "Try running the build up to the lifecycle phase \"package\"" ); - - artifacts = new LinkedHashSet( e.getResolvedArtifacts() ); - } - else - { - throw e; - } - } - } - catch ( ArtifactResolutionException e ) - { - throw new LifecycleExecutionException( null, project, e ); - } - catch ( ArtifactNotFoundException e ) - { - throw new LifecycleExecutionException( null, project, e ); - } - - project.setResolvedArtifacts( artifacts ); - - if ( project.getDependencyArtifacts() == null ) - { - Set directDependencies = new HashSet( project.getDependencies().size() * 2 ); - for ( Dependency dependency : project.getDependencies() ) - { - directDependencies.add( dependency.getManagementKey() ); - } - - Set dependencyArtifacts = new LinkedHashSet( project.getDependencies().size() * 2 ); - for ( Artifact artifact : artifacts ) - { - if ( directDependencies.contains( artifact.getDependencyConflictId() ) ) - { - dependencyArtifacts.add( artifact ); - } - } - project.setDependencyArtifacts( dependencyArtifacts ); - } - } - - private boolean areAllArtifactsInReactor( Collection projects, Collection artifacts ) - { - Set projectKeys = new HashSet( projects.size() * 2 ); - for ( MavenProject project : projects ) - { - String key = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() ); - projectKeys.add( key ); - } - - for ( Artifact artifact : artifacts ) - { - String key = ArtifactUtils.key( artifact ); - if ( !projectKeys.contains( key ) ) - { - return false; - } - } - - return true; - } - - private class DependencyContext - { - - private final Collection scopesToCollect; - - private final Collection scopesToResolve; - - private final boolean aggregating; - - private MavenProject lastProject; - - private Collection lastDependencyArtifacts; - - private int lastDependencyArtifactCount; - - DependencyContext( Collection scopesToCollect, Collection scopesToResolve, boolean aggregating ) - { - this.scopesToCollect = scopesToCollect; - this.scopesToResolve = scopesToResolve; - this.aggregating = aggregating; - } - - DependencyContext( MavenExecutionPlan executionPlan, boolean aggregating ) - { - this.scopesToCollect = executionPlan.getRequiredCollectionScopes(); - this.scopesToResolve = executionPlan.getRequiredResolutionScopes(); - this.aggregating = aggregating; - } - - DependencyContext( MojoExecution mojoExecution ) - { - this.scopesToCollect = new TreeSet(); - this.scopesToResolve = new TreeSet(); - collectDependencyRequirements( scopesToResolve, scopesToCollect, mojoExecution ); - this.aggregating = mojoExecution.getMojoDescriptor().isAggregator(); - } - - public DependencyContext clone() - { - return new DependencyContext( scopesToCollect, scopesToResolve, aggregating ); - } - - void checkForUpdate( MavenSession session ) - throws LifecycleExecutionException - { - if ( lastProject == session.getCurrentProject() ) - { - if ( lastDependencyArtifacts != lastProject.getDependencyArtifacts() - || ( lastDependencyArtifacts != null && lastDependencyArtifactCount != lastDependencyArtifacts.size() ) ) - { - logger.debug( "Re-resolving dependencies for project " + lastProject.getId() - + " to account for updates by previous goal execution" ); - resolveProjectDependencies( lastProject, scopesToCollect, scopesToResolve, session, aggregating ); - } - } - - lastProject = session.getCurrentProject(); - lastDependencyArtifacts = lastProject.getDependencyArtifacts(); - lastDependencyArtifactCount = ( lastDependencyArtifacts != null ) ? lastDependencyArtifacts.size() : 0; - } - } - - private void execute( MavenSession session, List mojoExecutions, ProjectIndex projectIndex, - DependencyContext dependencyContext ) - throws LifecycleExecutionException - { - MavenProject project = session.getCurrentProject(); - - String lastLifecyclePhase = null; - - for ( MojoExecution mojoExecution : mojoExecutions ) - { - execute( session, mojoExecution, projectIndex, dependencyContext ); - - String lifecyclePhase = mojoExecution.getLifecyclePhase(); - if ( lifecyclePhase != null ) - { - if ( lastLifecyclePhase == null ) - { - lastLifecyclePhase = lifecyclePhase; - } - else if ( !lifecyclePhase.equals( lastLifecyclePhase ) ) - { - project.addLifecyclePhase( lastLifecyclePhase ); - lastLifecyclePhase = lifecyclePhase; - } - } - } - - if ( lastLifecyclePhase != null ) - { - project.addLifecyclePhase( lastLifecyclePhase ); - } - } - - private void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex, - DependencyContext dependencyContext ) - throws LifecycleExecutionException - { - MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); - - if ( mojoDescriptor.isProjectRequired() && !session.isUsingPOMsFromFilesystem() ) - { - Throwable cause = - new MissingProjectException( "Goal requires a project to execute" - + " but there is no POM in this directory (" + session.getExecutionRootDirectory() + ")." - + " Please verify you invoked Maven from the correct directory." ); - throw new LifecycleExecutionException( mojoExecution, null, cause ); - } - - if ( mojoDescriptor.isOnlineRequired() && session.isOffline() ) - { - if ( MojoExecution.Source.CLI.equals( mojoExecution.getSource() ) ) - { - Throwable cause = - new IllegalStateException( "Goal requires online mode for execution" - + " but Maven is currently offline." ); - throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), cause ); - } - else - { - fireEvent( session, mojoExecution, LifecycleEventCatapult.MOJO_SKIPPED ); - - return; - } - } - - dependencyContext.checkForUpdate( session ); - - List forkedProjects = - executeForkedExecutions( mojoExecution, session, projectIndex, dependencyContext ); - - fireEvent( session, mojoExecution, LifecycleEventCatapult.MOJO_STARTED ); - - ArtifactFilter artifactFilter = getArtifactFilter( mojoDescriptor ); - List resolvedProjects = - getProjects( session.getCurrentProject(), session, mojoDescriptor.isAggregator() ); - for ( MavenProject project : resolvedProjects ) - { - project.setArtifactFilter( artifactFilter ); - } - - try - { - try - { - pluginManager.executeMojo( session, mojoExecution ); - } - catch ( MojoFailureException e ) - { - throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); - } - catch ( MojoExecutionException e ) - { - throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); - } - catch ( PluginConfigurationException e ) - { - throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); - } - catch ( PluginManagerException e ) - { - throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); - } - - fireEvent( session, mojoExecution, LifecycleEventCatapult.MOJO_SUCCEEDED ); - } - catch ( LifecycleExecutionException e ) - { - fireEvent( session, mojoExecution, LifecycleEventCatapult.MOJO_FAILED ); - - throw e; - } - finally - { - for ( MavenProject forkedProject : forkedProjects ) - { - forkedProject.setExecutionProject( null ); - } - } - } - - private ArtifactFilter getArtifactFilter( MojoDescriptor mojoDescriptor ) - { - String scopeToResolve = mojoDescriptor.getDependencyResolutionRequired(); - String scopeToCollect = mojoDescriptor.getDependencyCollectionRequired(); - - List scopes = new ArrayList( 2 ); - if ( StringUtils.isNotEmpty( scopeToCollect ) ) - { - scopes.add( scopeToCollect ); - } - if ( StringUtils.isNotEmpty( scopeToResolve ) ) - { - scopes.add( scopeToResolve ); - } - - if ( scopes.isEmpty() ) - { - return null; - } - else - { - return new CumulativeScopeArtifactFilter( scopes ); - } - } - - public List executeForkedExecutions( MojoExecution mojoExecution, MavenSession session ) - throws LifecycleExecutionException - { - return executeForkedExecutions( mojoExecution, session, new ProjectIndex( session.getProjects() ), - new DependencyContext( mojoExecution ) ); - } - - private List executeForkedExecutions( MojoExecution mojoExecution, MavenSession session, - ProjectIndex projectIndex, DependencyContext dependencyContext ) - throws LifecycleExecutionException - { - List forkedProjects = Collections.emptyList(); - - Map> forkedExecutions = mojoExecution.getForkedExecutions(); - - if ( !forkedExecutions.isEmpty() ) - { - fireEvent( session, mojoExecution, LifecycleEventCatapult.FORK_STARTED ); - - MavenProject project = session.getCurrentProject(); - - forkedProjects = new ArrayList( forkedExecutions.size() ); - - dependencyContext = dependencyContext.clone(); - - try - { - for ( Map.Entry> fork : forkedExecutions.entrySet() ) - { - int index = projectIndex.indices.get( fork.getKey() ); - - MavenProject forkedProject = projectIndex.projects.get( fork.getKey() ); - - forkedProjects.add( forkedProject ); - - MavenProject executedProject = forkedProject.clone(); - - forkedProject.setExecutionProject( executedProject ); - - try - { - session.setCurrentProject( executedProject ); - session.getProjects().set( index, executedProject ); - projectIndex.projects.put( fork.getKey(), executedProject ); - - execute( session, fork.getValue(), projectIndex, dependencyContext ); - } - finally - { - projectIndex.projects.put( fork.getKey(), forkedProject ); - session.getProjects().set( index, forkedProject ); - session.setCurrentProject( project ); - } - } - - fireEvent( session, mojoExecution, LifecycleEventCatapult.FORK_SUCCEEDED ); - } - catch ( LifecycleExecutionException e ) - { - fireEvent( session, mojoExecution, LifecycleEventCatapult.FORK_FAILED ); - - throw e; - } - } - - return forkedProjects; - } - - private static final class ProjectIndex - { - - Map projects; - - Map indices; - - ProjectIndex( List projects ) - { - this.projects = new HashMap( projects.size() * 2 ); - this.indices = new HashMap( projects.size() * 2 ); - - for ( int i = 0; i < projects.size(); i++ ) - { - MavenProject project = projects.get( i ); - String key = getKey( project ); - - this.projects.put( key, project ); - this.indices.put( key, Integer.valueOf( i ) ); - } - } } - private List calculateProjectBuilds( MavenSession session ) - throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, - MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, - PluginVersionResolutionException + private void singleThreadedBuild( MavenSession session, ReactorContext callableContext, + ProjectBuildList projectBuilds, List taskSegments, + ReactorBuildStatus reactorBuildStatus ) { - List projectBuilds = new ArrayList(); - - MavenProject rootProject = session.getTopLevelProject(); - - List tasks = session.getGoals(); - - if ( tasks == null || tasks.isEmpty() ) - { - if ( !StringUtils.isEmpty( rootProject.getDefaultGoal() ) ) - { - tasks = Arrays.asList( StringUtils.split( rootProject.getDefaultGoal() ) ); - } - } - - List taskSegments = calculateTaskSegments( session, tasks ); - for ( TaskSegment taskSegment : taskSegments ) { - List projects; - - if ( taskSegment.aggregating ) + for ( ProjectSegment projectBuild : projectBuilds.getByTaskSegment( taskSegment ) ) { - projects = Collections.singletonList( rootProject ); - } - else - { - projects = session.getProjects(); - } - - for ( MavenProject project : projects ) - { - projectBuilds.add( new ProjectBuild( project, taskSegment ) ); - } - } - - return projectBuilds; - } - - private MavenExecutionPlan calculateExecutionPlan( MavenSession session, MavenProject project, - TaskSegment taskSegment ) - throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException, - PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException, - NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException - { - resolveMissingPluginVersions( project, session ); - - List mojoExecutions = new ArrayList(); - - Set requiredDependencyResolutionScopes = new TreeSet(); - - Set requiredDependencyCollectionScopes = new TreeSet(); - - for ( Object task : taskSegment.tasks ) - { - if ( task instanceof GoalTask ) - { - String pluginGoal = ( (GoalTask) task ).pluginGoal; - - MojoDescriptor mojoDescriptor = getMojoDescriptor( pluginGoal, session, project ); - - MojoExecution mojoExecution = - new MojoExecution( mojoDescriptor, "default-cli", MojoExecution.Source.CLI ); - - mojoExecutions.add( mojoExecution ); - } - else if ( task instanceof LifecycleTask ) - { - String lifecyclePhase = ( (LifecycleTask) task ).lifecyclePhase; - - Map> phaseToMojoMapping = - calculateLifecycleMappings( session, project, lifecyclePhase ); - - for ( List mojoExecutionsFromLifecycle : phaseToMojoMapping.values() ) + try { - mojoExecutions.addAll( mojoExecutionsFromLifecycle ); - } - } - else - { - throw new IllegalStateException( "unexpected task " + task ); - } - } - - for ( MojoExecution mojoExecution : mojoExecutions ) - { - MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); - - if ( mojoDescriptor == null ) - { - mojoDescriptor = - pluginManager.getMojoDescriptor( mojoExecution.getPlugin(), mojoExecution.getGoal(), - getRepositoryRequest( session, project ) ); - - mojoExecution.setMojoDescriptor( mojoDescriptor ); - } - - populateMojoExecutionConfiguration( project, mojoExecution, - MojoExecution.Source.CLI.equals( mojoExecution.getSource() ) ); - - finalizeMojoConfiguration( mojoExecution ); - - calculateForkedExecutions( mojoExecution, session, project, new HashSet() ); - - collectDependencyRequirements( requiredDependencyResolutionScopes, requiredDependencyCollectionScopes, - mojoExecution ); - } - - return new MavenExecutionPlan( mojoExecutions, requiredDependencyResolutionScopes, - requiredDependencyCollectionScopes ); - } - - private List calculateTaskSegments( MavenSession session, List tasks ) - throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, - MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, - PluginVersionResolutionException - { - List taskSegments = new ArrayList( tasks.size() ); - - TaskSegment currentSegment = null; - - for ( String task : tasks ) - { - if ( isGoalSpecification( task ) ) - { - // "pluginPrefix:goal" or "groupId:artifactId[:version]:goal" - - resolveMissingPluginVersions( session.getTopLevelProject(), session ); - - MojoDescriptor mojoDescriptor = getMojoDescriptor( task, session, session.getTopLevelProject() ); - - boolean aggregating = mojoDescriptor.isAggregator() || !mojoDescriptor.isProjectRequired(); - - if ( currentSegment == null || currentSegment.aggregating != aggregating ) - { - currentSegment = new TaskSegment( aggregating ); - taskSegments.add( currentSegment ); - } - - currentSegment.tasks.add( new GoalTask( task ) ); - } - else - { - // lifecycle phase - - if ( currentSegment == null || currentSegment.aggregating ) - { - currentSegment = new TaskSegment( false ); - taskSegments.add( currentSegment ); - } - - currentSegment.tasks.add( new LifecycleTask( task ) ); - } - } - - return taskSegments; - } - - private boolean isGoalSpecification( String task ) - { - return task.indexOf( ':' ) >= 0; - } - - private static final class ProjectBuild - { - - final MavenProject project; - - final TaskSegment taskSegment; - - ProjectBuild( MavenProject project, TaskSegment taskSegment ) - { - this.project = project; - this.taskSegment = taskSegment; - } - - @Override - public String toString() - { - return project.getId() + " -> " + taskSegment; - } - - } - - private static final class TaskSegment - { - - final List tasks; - - final boolean aggregating; - - TaskSegment( boolean aggregating ) - { - this.aggregating = aggregating; - tasks = new ArrayList(); - } - - @Override - public String toString() - { - return tasks.toString(); - } - - } - - private static final class GoalTask - { - - final String pluginGoal; - - GoalTask( String pluginGoal ) - { - this.pluginGoal = pluginGoal; - } - - @Override - public String toString() - { - return pluginGoal; - } - - } - - private static final class LifecycleTask - { - - final String lifecyclePhase; - - LifecycleTask( String lifecyclePhase ) - { - this.lifecyclePhase = lifecyclePhase; - } - - @Override - public String toString() - { - return lifecyclePhase; - } - - } - - public MavenExecutionPlan calculateExecutionPlan( MavenSession session, String... tasks ) - throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, - MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, - PluginManagerException, LifecyclePhaseNotFoundException, LifecycleNotFoundException, - PluginVersionResolutionException - { - List taskSegments = calculateTaskSegments( session, Arrays.asList( tasks ) ); - - TaskSegment mergedSegment = new TaskSegment( false ); - - for ( TaskSegment taskSegment : taskSegments ) - { - mergedSegment.tasks.addAll( taskSegment.tasks ); - } - - return calculateExecutionPlan( session, session.getCurrentProject(), mergedSegment ); - } - - private RepositoryRequest getRepositoryRequest( MavenSession session, MavenProject project ) - { - RepositoryRequest request = new DefaultRepositoryRequest(); - - request.setCache( session.getRepositoryCache() ); - request.setLocalRepository( session.getLocalRepository() ); - if ( project != null ) - { - request.setRemoteRepositories( project.getPluginArtifactRepositories() ); - } - request.setOffline( session.isOffline() ); - request.setForceUpdate( session.getRequest().isUpdateSnapshots() ); - request.setTransferListener( session.getRequest().getTransferListener() ); - - return request; - } - - private void collectDependencyRequirements( Collection requiredDependencyResolutionScopes, - Collection requiredDependencyCollectionScopes, - MojoExecution mojoExecution ) - { - MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); - - String requiredDependencyResolutionScope = mojoDescriptor.getDependencyResolutionRequired(); - - if ( StringUtils.isNotEmpty( requiredDependencyResolutionScope ) ) - { - requiredDependencyResolutionScopes.add( requiredDependencyResolutionScope ); - } - - String requiredDependencyCollectionScope = mojoDescriptor.getDependencyCollectionRequired(); - - if ( StringUtils.isNotEmpty( requiredDependencyCollectionScope ) ) - { - requiredDependencyCollectionScopes.add( requiredDependencyCollectionScope ); - } - - for ( List forkedExecutions : mojoExecution.getForkedExecutions().values() ) - { - for ( MojoExecution forkedExecution : forkedExecutions ) - { - collectDependencyRequirements( requiredDependencyResolutionScopes, - requiredDependencyCollectionScopes, forkedExecution ); - } - } - } - - private Map> calculateLifecycleMappings( MavenSession session, MavenProject project, - String lifecyclePhase ) - throws LifecyclePhaseNotFoundException, PluginNotFoundException, PluginResolutionException, - PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException - { - /* - * Determine the lifecycle that corresponds to the given phase. - */ - - Lifecycle lifecycle = phaseToLifecycleMap.get( lifecyclePhase ); - - if ( lifecycle == null ) - { - throw new LifecyclePhaseNotFoundException( "Unknown lifecycle phase \"" + lifecyclePhase - + "\". You must specify a valid lifecycle phase" + " or a goal in the format : or" - + " :[:]:. Available lifecycle phases are: " - + getLifecyclePhaseList() + ".", lifecyclePhase ); - } - - /* - * Initialize mapping from lifecycle phase to bound mojos. The key set of this map denotes the phases the caller - * is interested in, i.e. all phases up to and including the specified phase. - */ - - Map>> mappings = - new LinkedHashMap>>(); - - for ( String phase : lifecycle.getPhases() ) - { - Map> phaseBindings = new TreeMap>(); - - mappings.put( phase, phaseBindings ); - - if ( phase.equals( lifecyclePhase ) ) - { - break; - } - } - - /* - * Grab plugin executions that are bound to the selected lifecycle phases from project. The effective model of - * the project already contains the plugin executions induced by the project's packaging type. Remember, all - * phases of interest and only those are in the lifecyle mapping, if a phase has no value in the map, we are not - * interested in any of the executions bound to it. - */ - - for ( Plugin plugin : project.getBuild().getPlugins() ) - { - for ( PluginExecution execution : plugin.getExecutions() ) - { - // if the phase is specified then I don't have to go fetch the plugin yet and pull it down - // to examine the phase it is associated to. - if ( execution.getPhase() != null ) - { - Map> phaseBindings = mappings.get( execution.getPhase() ); - if ( phaseBindings != null ) + lifecycleModuleBuilder.buildProject( session, callableContext, projectBuild.getProject(), taskSegment ); + if ( reactorBuildStatus.isHalted() ) { - for ( String goal : execution.getGoals() ) - { - MojoExecution mojoExecution = new MojoExecution( plugin, goal, execution.getId() ); - mojoExecution.setLifecyclePhase( execution.getPhase() ); - addMojoExecution( phaseBindings, mojoExecution, execution.getPriority() ); - } + break; } } - // if not then i need to grab the mojo descriptor and look at the phase that is specified - else + catch ( Exception e ) { - for ( String goal : execution.getGoals() ) - { - MojoDescriptor mojoDescriptor = - pluginManager.getMojoDescriptor( plugin, goal, getRepositoryRequest( session, project ) ); - - Map> phaseBindings = mappings.get( mojoDescriptor.getPhase() ); - if ( phaseBindings != null ) - { - MojoExecution mojoExecution = new MojoExecution( mojoDescriptor, execution.getId() ); - mojoExecution.setLifecyclePhase( mojoDescriptor.getPhase() ); - addMojoExecution( phaseBindings, mojoExecution, execution.getPriority() ); - } - } - } - } - } - - Map> lifecycleMappings = new LinkedHashMap>(); - - for ( Map.Entry>> entry : mappings.entrySet() ) - { - List mojoExecutions = new ArrayList(); - - for ( List executions : entry.getValue().values() ) - { - mojoExecutions.addAll( executions ); - } - - lifecycleMappings.put( entry.getKey(), mojoExecutions ); - } - - return lifecycleMappings; - } - - private void addMojoExecution( Map> phaseBindings, MojoExecution mojoExecution, - int priority ) - { - List mojoExecutions = phaseBindings.get( priority ); - - if ( mojoExecutions == null ) - { - mojoExecutions = new ArrayList(); - phaseBindings.put( priority, mojoExecutions ); - } - - mojoExecutions.add( mojoExecution ); - } - - public void calculateForkedExecutions( MojoExecution mojoExecution, MavenSession session ) - throws MojoNotFoundException, PluginNotFoundException, PluginResolutionException, - PluginDescriptorParsingException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, - LifecyclePhaseNotFoundException, LifecycleNotFoundException, PluginVersionResolutionException - { - calculateForkedExecutions( mojoExecution, session, session.getCurrentProject(), new HashSet() ); - } - - private void calculateForkedExecutions( MojoExecution mojoExecution, MavenSession session, MavenProject project, - Collection alreadyForkedExecutions ) - throws MojoNotFoundException, PluginNotFoundException, PluginResolutionException, - PluginDescriptorParsingException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, - LifecyclePhaseNotFoundException, LifecycleNotFoundException, PluginVersionResolutionException - { - MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); - - if ( !mojoDescriptor.isForking() ) - { - return; - } - - if ( !alreadyForkedExecutions.add( mojoDescriptor ) ) - { - return; - } - - List forkedProjects = getProjects( project, session, mojoDescriptor.isAggregator() ); - - for ( MavenProject forkedProject : forkedProjects ) - { - List forkedExecutions; - - if ( StringUtils.isNotEmpty( mojoDescriptor.getExecutePhase() ) ) - { - forkedExecutions = - calculateForkedLifecycle( mojoExecution, session, forkedProject, alreadyForkedExecutions ); - } - else - { - forkedExecutions = calculateForkedGoal( mojoExecution, session, forkedProject, alreadyForkedExecutions ); - } - - mojoExecution.setForkedExecutions( getKey( forkedProject ), forkedExecutions ); - } - - alreadyForkedExecutions.remove( mojoDescriptor ); - } - - private List calculateForkedGoal( MojoExecution mojoExecution, MavenSession session, - MavenProject project, - Collection alreadyForkedExecutions ) - throws MojoNotFoundException, PluginNotFoundException, PluginResolutionException, - PluginDescriptorParsingException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, - LifecyclePhaseNotFoundException, LifecycleNotFoundException, PluginVersionResolutionException - { - MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); - - PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor(); - - String forkedGoal = mojoDescriptor.getExecuteGoal(); - - MojoDescriptor forkedMojoDescriptor = pluginDescriptor.getMojo( forkedGoal ); - if ( forkedMojoDescriptor == null ) - { - throw new MojoNotFoundException( forkedGoal, pluginDescriptor ); - } - - if ( alreadyForkedExecutions.contains( forkedMojoDescriptor ) ) - { - return Collections.emptyList(); - } - - MojoExecution forkedExecution = new MojoExecution( forkedMojoDescriptor, forkedGoal ); - - populateMojoExecutionConfiguration( project, forkedExecution, true ); - - finalizeMojoConfiguration( forkedExecution ); - - calculateForkedExecutions( forkedExecution, session, project, alreadyForkedExecutions ); - - return Collections.singletonList( forkedExecution ); - } - - private List calculateForkedLifecycle( MojoExecution mojoExecution, MavenSession session, - MavenProject project, - Collection alreadyForkedExecutions ) - throws MojoNotFoundException, PluginNotFoundException, PluginResolutionException, - PluginDescriptorParsingException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, - LifecyclePhaseNotFoundException, LifecycleNotFoundException, PluginVersionResolutionException - { - MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); - - String forkedPhase = mojoDescriptor.getExecutePhase(); - - Map> lifecycleMappings = calculateLifecycleMappings( session, project, forkedPhase ); - - for ( List forkedExecutions : lifecycleMappings.values() ) - { - for ( MojoExecution forkedExecution : forkedExecutions ) - { - if ( forkedExecution.getMojoDescriptor() == null ) - { - MojoDescriptor forkedMojoDescriptor = - pluginManager.getMojoDescriptor( forkedExecution.getPlugin(), forkedExecution.getGoal(), - getRepositoryRequest( session, project ) ); - - forkedExecution.setMojoDescriptor( forkedMojoDescriptor ); + break; // Why are we just ignoring this exception? Are exceptions are being used for flow control } - populateMojoExecutionConfiguration( project, forkedExecution, false ); } } - - injectLifecycleOverlay( lifecycleMappings, mojoExecution, session, project ); - - List mojoExecutions = new ArrayList(); - - for ( List forkedExecutions : lifecycleMappings.values() ) - { - for ( MojoExecution forkedExecution : forkedExecutions ) - { - if ( !alreadyForkedExecutions.contains( forkedExecution.getMojoDescriptor() ) ) - { - finalizeMojoConfiguration( forkedExecution ); - - calculateForkedExecutions( forkedExecution, session, project, alreadyForkedExecutions ); - - mojoExecutions.add( forkedExecution ); - } - } - } - - return mojoExecutions; } - private void injectLifecycleOverlay( Map> lifecycleMappings, - MojoExecution mojoExecution, MavenSession session, MavenProject project ) - throws PluginDescriptorParsingException, LifecycleNotFoundException, MojoNotFoundException, - PluginNotFoundException, PluginResolutionException, NoPluginFoundForPrefixException, - InvalidPluginDescriptorException, PluginVersionResolutionException + public static void fireEvent( MavenSession session, MojoExecution mojoExecution, LifecycleEventCatapult catapult ) { - MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); - - PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor(); - - String forkedLifecycle = mojoDescriptor.getExecuteLifecycle(); - - if ( StringUtils.isEmpty( forkedLifecycle ) ) + ExecutionListener listener = session.getRequest().getExecutionListener(); + if ( listener != null ) { - return; - } + ExecutionEvent event = new DefaultLifecycleEvent( session, mojoExecution ); - org.apache.maven.plugin.lifecycle.Lifecycle lifecycleOverlay; - - try - { - lifecycleOverlay = pluginDescriptor.getLifecycleMapping( forkedLifecycle ); - } - catch ( IOException e ) - { - throw new PluginDescriptorParsingException( pluginDescriptor.getPlugin(), pluginDescriptor.getSource(), e ); - } - catch ( XmlPullParserException e ) - { - throw new PluginDescriptorParsingException( pluginDescriptor.getPlugin(), pluginDescriptor.getSource(), e ); - } - - if ( lifecycleOverlay == null ) - { - throw new LifecycleNotFoundException( forkedLifecycle ); - } - - for ( Phase phase : lifecycleOverlay.getPhases() ) - { - List forkedExecutions = lifecycleMappings.get( phase.getId() ); - - if ( forkedExecutions != null ) - { - for ( Execution execution : phase.getExecutions() ) - { - for ( String goal : execution.getGoals() ) - { - MojoDescriptor forkedMojoDescriptor; - - if ( goal.indexOf( ':' ) < 0 ) - { - forkedMojoDescriptor = pluginDescriptor.getMojo( goal ); - if ( forkedMojoDescriptor == null ) - { - throw new MojoNotFoundException( goal, pluginDescriptor ); - } - } - else - { - forkedMojoDescriptor = getMojoDescriptor( goal, session, project ); - } - - MojoExecution forkedExecution = - new MojoExecution( forkedMojoDescriptor, mojoExecution.getExecutionId() ); - - Xpp3Dom forkedConfiguration = (Xpp3Dom) execution.getConfiguration(); - - forkedExecution.setConfiguration( forkedConfiguration ); - - populateMojoExecutionConfiguration( project, forkedExecution, true ); - - forkedExecutions.add( forkedExecution ); - } - } - - Xpp3Dom phaseConfiguration = (Xpp3Dom) phase.getConfiguration(); - - if ( phaseConfiguration != null ) - { - for ( MojoExecution forkedExecution : forkedExecutions ) - { - Xpp3Dom forkedConfiguration = forkedExecution.getConfiguration(); - - forkedConfiguration = Xpp3Dom.mergeXpp3Dom( phaseConfiguration, forkedConfiguration ); - - forkedExecution.setConfiguration( forkedConfiguration ); - } - } - } + catapult.fire( listener, event ); } } - private void populateMojoExecutionConfiguration( MavenProject project, MojoExecution mojoExecution, - boolean allowPluginLevelConfig ) - { - String g = mojoExecution.getGroupId(); - - String a = mojoExecution.getArtifactId(); - - Plugin plugin = findPlugin( g, a, project.getBuildPlugins() ); - - if ( plugin == null && project.getPluginManagement() != null ) - { - plugin = findPlugin( g, a, project.getPluginManagement().getPlugins() ); - } - - if ( plugin != null ) - { - PluginExecution pluginExecution = - findPluginExecution( mojoExecution.getExecutionId(), plugin.getExecutions() ); - - Xpp3Dom pomConfiguration = null; - - if ( pluginExecution != null ) - { - pomConfiguration = (Xpp3Dom) pluginExecution.getConfiguration(); - } - else if ( allowPluginLevelConfig ) - { - pomConfiguration = (Xpp3Dom) plugin.getConfiguration(); - } - - Xpp3Dom mojoConfiguration = ( pomConfiguration != null ) ? new Xpp3Dom( pomConfiguration ) : null; - - mojoConfiguration = Xpp3Dom.mergeXpp3Dom( mojoExecution.getConfiguration(), mojoConfiguration ); - - mojoExecution.setConfiguration( mojoConfiguration ); - } - } /** - * Post-processes the effective configuration for the specified mojo execution. This step discards all parameters - * from the configuration that are not applicable to the mojo and injects the default values for any missing - * parameters. - * - * @param mojoExecution The mojo execution whose configuration should be finalized, must not be {@code null}. + * * CRUFT GOES BELOW HERE *** */ - private void finalizeMojoConfiguration( MojoExecution mojoExecution ) + + @Requirement + private MojoDescriptorCreator mojoDescriptorCreator; + + // These methods deal with construction intact Plugin object that look like they come from a standard + // block in a Maven POM. We have to do some wiggling to pull the sources of information + // together and this really shows the problem of constructing a sensible default configuration but + // it's all encapsulated here so it appears normalized to the POM builder. + + // We are going to take the project packaging and find all plugin in the default lifecycle and create + // fully populated Plugin objects, including executions with goals and default configuration taken + // from the plugin.xml inside a plugin. + // + // TODO: This whole method could probably removed by injecting lifeCyclePluginAnalyzer straight into client site. + + public Set getPluginsBoundByDefaultToAllLifecycles( String packaging ) { - MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); - - Xpp3Dom executionConfiguration = mojoExecution.getConfiguration(); - if ( executionConfiguration == null ) - { - executionConfiguration = new Xpp3Dom( "configuration" ); - } - - Xpp3Dom defaultConfiguration = getMojoConfiguration( mojoDescriptor ); - - Xpp3Dom finalConfiguration = new Xpp3Dom( "configuration" ); - - if ( mojoDescriptor.getParameters() != null ) - { - for ( Parameter parameter : mojoDescriptor.getParameters() ) - { - Xpp3Dom parameterConfiguration = executionConfiguration.getChild( parameter.getName() ); - - if ( parameterConfiguration == null ) - { - parameterConfiguration = executionConfiguration.getChild( parameter.getAlias() ); - } - - Xpp3Dom parameterDefaults = defaultConfiguration.getChild( parameter.getName() ); - - parameterConfiguration = Xpp3Dom.mergeXpp3Dom( parameterConfiguration, parameterDefaults, Boolean.TRUE ); - - if ( parameterConfiguration != null ) - { - parameterConfiguration = new Xpp3Dom( parameterConfiguration, parameter.getName() ); - - if ( StringUtils.isEmpty( parameterConfiguration.getAttribute( "implementation" ) ) - && StringUtils.isNotEmpty( parameter.getImplementation() ) ) - { - parameterConfiguration.setAttribute( "implementation", parameter.getImplementation() ); - } - - finalConfiguration.addChild( parameterConfiguration ); - } - } - } - - mojoExecution.setConfiguration( finalConfiguration ); + return lifeCyclePluginAnalyzer.getPluginsBoundByDefaultToAllLifecycles( packaging ); } - // org.apache.maven.plugins:maven-remote-resources-plugin:1.0:process - MojoDescriptor getMojoDescriptor( String task, MavenSession session, MavenProject project ) - throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, PluginVersionResolutionException - { - String goal = null; - - Plugin plugin = null; + // USED BY MAVEN HELP PLUGIN - StringTokenizer tok = new StringTokenizer( task, ":" ); - - int numTokens = tok.countTokens(); - - if ( numTokens == 4 ) - { - // We have everything that we need - // - // org.apache.maven.plugins:maven-remote-resources-plugin:1.0:process - // - // groupId - // artifactId - // version - // goal - // - plugin = new Plugin(); - plugin.setGroupId( tok.nextToken() ); - plugin.setArtifactId( tok.nextToken() ); - plugin.setVersion( tok.nextToken() ); - goal = tok.nextToken(); - - } - else if ( numTokens == 3 ) - { - // We have everything that we need except the version - // - // org.apache.maven.plugins:maven-remote-resources-plugin:???:process - // - // groupId - // artifactId - // ??? - // goal - // - plugin = new Plugin(); - plugin.setGroupId( tok.nextToken() ); - plugin.setArtifactId( tok.nextToken() ); - goal = tok.nextToken(); - } - else if ( numTokens == 2 ) - { - // We have a prefix and goal - // - // idea:idea - // - String prefix = tok.nextToken(); - goal = tok.nextToken(); - - // This is the case where someone has executed a single goal from the command line - // of the form: - // - // mvn remote-resources:process - // - // From the metadata stored on the server which has been created as part of a standard - // Maven plugin deployment we will find the right PluginDescriptor from the remote - // repository. - - plugin = findPluginForPrefix( prefix, session ); - } - - injectPluginDeclarationFromProject( plugin, project ); - - RepositoryRequest repositoryRequest = getRepositoryRequest( session, project ); - - // If there is no version to be found then we need to look in the repository metadata for - // this plugin and see what's specified as the latest release. - // - if ( plugin.getVersion() == null ) - { - resolvePluginVersion( plugin, repositoryRequest ); - } - - return pluginManager.getMojoDescriptor( plugin, goal, repositoryRequest ); + @SuppressWarnings({"UnusedDeclaration"}) + @Deprecated + public Map getPhaseToLifecycleMap() + { + return defaultLifeCycles.getPhaseToLifecycleMap(); } // NOTE: Backward-compat with maven-help-plugin:2.1 + + @SuppressWarnings({"UnusedDeclaration"}) MojoDescriptor getMojoDescriptor( String task, MavenSession session, MavenProject project, String invokedVia, boolean canUsePrefix, boolean isOptionalMojo ) throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, PluginVersionResolutionException { - return getMojoDescriptor( task, session, project ); + return mojoDescriptorCreator.getMojoDescriptor( task, session, project ); } - private void resolvePluginVersion( Plugin plugin, RepositoryRequest repositoryRequest ) - throws PluginVersionResolutionException - { - PluginVersionRequest versionRequest = new DefaultPluginVersionRequest( plugin, repositoryRequest ); - plugin.setVersion( pluginVersionResolver.resolve( versionRequest ).getVersion() ); - } - - private void injectPluginDeclarationFromProject( Plugin plugin, MavenProject project ) - { - Plugin pluginInPom = findPlugin( plugin, project.getBuildPlugins() ); - - if ( pluginInPom == null && project.getPluginManagement() != null ) - { - pluginInPom = findPlugin( plugin, project.getPluginManagement().getPlugins() ); - } - - if ( pluginInPom != null ) - { - if ( plugin.getVersion() == null ) - { - plugin.setVersion( pluginInPom.getVersion() ); - } - - plugin.setDependencies( new ArrayList( pluginInPom.getDependencies() ) ); - } - } - - private Plugin findPlugin( Plugin plugin, Collection plugins ) - { - return findPlugin( plugin.getGroupId(), plugin.getArtifactId(), plugins ); - } - - private Plugin findPlugin( String groupId, String artifactId, Collection plugins ) - { - for ( Plugin plugin : plugins ) - { - if ( artifactId.equals( plugin.getArtifactId() ) && groupId.equals( plugin.getGroupId() ) ) - { - return plugin; - } - } - - return null; - } - - private PluginExecution findPluginExecution( String executionId, Collection executions ) - { - if ( StringUtils.isNotEmpty( executionId ) ) - { - for ( PluginExecution execution : executions ) - { - if ( executionId.equals( execution.getId() ) ) - { - return execution; - } - } - } - - return null; - } - - public void initialize() - throws InitializationException - { - lifecycleMap = new HashMap(); - - // If people are going to make their own lifecycles then we need to tell people how to namespace them correctly so - // that they don't interfere with internally defined lifecycles. - - phaseToLifecycleMap = new HashMap(); - - for ( Lifecycle lifecycle : lifecycles ) - { - for ( String phase : lifecycle.getPhases() ) - { - // The first definition wins. - if ( !phaseToLifecycleMap.containsKey( phase ) ) - { - phaseToLifecycleMap.put( phase, lifecycle ); - } - } - - lifecycleMap.put( lifecycle.getId(), lifecycle ); - } - } - - // These methods deal with construction intact Plugin object that look like they come from a standard - // block in a Maven POM. We have to do some wiggling to pull the sources of information - // together and this really shows the problem of constructing a sensible default configuration but - // it's all encapsulated here so it appears normalized to the POM builder. - - // We are going to take the project packaging and find all plugin in the default lifecycle and create - // fully populated Plugin objects, including executions with goals and default configuration taken - // from the plugin.xml inside a plugin. - // - public Set getPluginsBoundByDefaultToAllLifecycles( String packaging ) - { - if ( logger.isDebugEnabled() ) - { - logger.debug( "Looking up lifecyle mappings for packaging " + packaging + " from " - + Thread.currentThread().getContextClassLoader() ); - } - - LifecycleMapping lifecycleMappingForPackaging = lifecycleMappings.get( packaging ); - - if ( lifecycleMappingForPackaging == null ) - { - return null; - } - - Map plugins = new LinkedHashMap(); - - for ( Lifecycle lifecycle : lifecycles ) - { - org.apache.maven.lifecycle.mapping.Lifecycle lifecycleConfiguration = - lifecycleMappingForPackaging.getLifecycles().get( lifecycle.getId() ); - - Map phaseToGoalMapping = null; - - if ( lifecycleConfiguration != null ) - { - phaseToGoalMapping = lifecycleConfiguration.getPhases(); - } - else if ( lifecycle.getDefaultPhases() != null ) - { - phaseToGoalMapping = lifecycle.getDefaultPhases(); - } - - if ( phaseToGoalMapping != null ) - { - // These are of the form: - // - // compile -> org.apache.maven.plugins:maven-compiler-plugin:compile[,gid:aid:goal,...] - // - for ( Map.Entry goalsForLifecyclePhase : phaseToGoalMapping.entrySet() ) - { - String phase = goalsForLifecyclePhase.getKey(); - String goals = goalsForLifecyclePhase.getValue(); - if ( goals != null ) - { - parseLifecyclePhaseDefinitions( plugins, phase, goals ); - } - } - } - } - - return plugins.keySet(); - } - - private void parseLifecyclePhaseDefinitions( Map plugins, String phase, String goals ) - { - String[] mojos = StringUtils.split( goals, "," ); - - for ( int i = 0; i < mojos.length; i++ ) - { - // either :: or ::: - String goal = mojos[i].trim(); - String[] p = StringUtils.split( goal, ":" ); - - PluginExecution execution = new PluginExecution(); - execution.setId( "default-" + p[p.length - 1] ); - execution.setPhase( phase ); - execution.setPriority( i - mojos.length ); - execution.getGoals().add( p[p.length - 1] ); - - Plugin plugin = new Plugin(); - plugin.setGroupId( p[0] ); - plugin.setArtifactId( p[1] ); - if ( p.length >= 4 ) - { - plugin.setVersion( p[2] ); - } - - Plugin existing = plugins.get( plugin ); - if ( existing != null ) - { - plugin = existing; - } - else - { - plugins.put( plugin, plugin ); - } - - plugin.getExecutions().add( execution ); - } - } - - private void resolveMissingPluginVersions( MavenProject project, MavenSession session ) - throws PluginVersionResolutionException - { - Map versions = new HashMap(); - - for ( Plugin plugin : project.getBuildPlugins() ) - { - if ( plugin.getVersion() == null ) - { - PluginVersionRequest request = new DefaultPluginVersionRequest( plugin, session ); - plugin.setVersion( pluginVersionResolver.resolve( request ).getVersion() ); - } - versions.put( plugin.getKey(), plugin.getVersion() ); - } - - PluginManagement pluginManagement = project.getPluginManagement(); - if ( pluginManagement != null ) - { - for ( Plugin plugin : pluginManagement.getPlugins() ) - { - if ( plugin.getVersion() == null ) - { - plugin.setVersion( versions.get( plugin.getKey() ) ); - if ( plugin.getVersion() == null ) - { - PluginVersionRequest request = new DefaultPluginVersionRequest( plugin, session ); - plugin.setVersion( pluginVersionResolver.resolve( request ).getVersion() ); - } - } - } - } - } - - public Xpp3Dom getMojoConfiguration( MojoDescriptor mojoDescriptor ) - { - return convert( mojoDescriptor ); - } - - Xpp3Dom convert( MojoDescriptor mojoDescriptor ) - { - Xpp3Dom dom = new Xpp3Dom( "configuration" ); - - PlexusConfiguration c = mojoDescriptor.getMojoConfiguration(); - - PlexusConfiguration[] ces = c.getChildren(); - - if ( ces != null ) - { - for ( PlexusConfiguration ce : ces ) - { - String value = ce.getValue( null ); - String defaultValue = ce.getAttribute( "default-value", null ); - if ( value != null || defaultValue != null ) - { - Xpp3Dom e = new Xpp3Dom( ce.getName() ); - e.setValue( value ); - if ( defaultValue != null ) - { - e.setAttribute( "default-value", defaultValue ); - } - dom.addChild( e ); - } - } - } - - return dom; - } - - //TODO: take repo mans into account as one may be aggregating prefixes of many - //TODO: collect at the root of the repository, read the one at the root, and fetch remote if something is missing - // or the user forces the issue - Plugin findPluginForPrefix( String prefix, MavenSession session ) - throws NoPluginFoundForPrefixException - { - // [prefix]:[goal] - - PluginPrefixRequest prefixRequest = new DefaultPluginPrefixRequest( prefix, session ); - PluginPrefixResult prefixResult = pluginPrefixResolver.resolve( prefixRequest ); - - Plugin plugin = new Plugin(); - plugin.setGroupId( prefixResult.getGroupId() ); - plugin.setArtifactId( prefixResult.getArtifactId() ); - - return plugin; - } - - // These are checks that should be available in real time to IDEs - - /* - checkRequiredMavenVersion( plugin, localRepository, project.getRemoteArtifactRepositories() ); - // Validate against non-editable (@readonly) parameters, to make sure users aren't trying to override in the POM. - //validatePomConfiguration( mojoDescriptor, pomConfiguration ); - //checkDeprecatedParameters( mojoDescriptor, pomConfiguration ); - //checkRequiredParameters( mojoDescriptor, pomConfiguration, expressionEvaluator ); - - public void checkRequiredMavenVersion( Plugin plugin, ArtifactRepository localRepository, List remoteRepositories ) - throws PluginVersionResolutionException, InvalidPluginException - { - // if we don't have the required Maven version, then ignore an update - if ( ( pluginProject.getPrerequisites() != null ) && ( pluginProject.getPrerequisites().getMaven() != null ) ) - { - DefaultArtifactVersion requiredVersion = new DefaultArtifactVersion( pluginProject.getPrerequisites().getMaven() ); - - if ( runtimeInformation.getApplicationInformation().getVersion().compareTo( requiredVersion ) < 0 ) - { - throw new PluginVersionResolutionException( plugin.getGroupId(), plugin.getArtifactId(), "Plugin requires Maven version " + requiredVersion ); - } - } - } - - private void checkDeprecatedParameters( MojoDescriptor mojoDescriptor, PlexusConfiguration extractedMojoConfiguration ) - throws PlexusConfigurationException - { - if ( ( extractedMojoConfiguration == null ) || ( extractedMojoConfiguration.getChildCount() < 1 ) ) - { - return; - } - - List parameters = mojoDescriptor.getParameters(); - - if ( ( parameters != null ) && !parameters.isEmpty() ) - { - for ( Parameter param : parameters ) - { - if ( param.getDeprecated() != null ) - { - boolean warnOfDeprecation = false; - PlexusConfiguration child = extractedMojoConfiguration.getChild( param.getName() ); - - if ( ( child != null ) && ( child.getValue() != null ) ) - { - warnOfDeprecation = true; - } - else if ( param.getAlias() != null ) - { - child = extractedMojoConfiguration.getChild( param.getAlias() ); - if ( ( child != null ) && ( child.getValue() != null ) ) - { - warnOfDeprecation = true; - } - } - - if ( warnOfDeprecation ) - { - StringBuilder buffer = new StringBuilder( 128 ); - buffer.append( "In mojo: " ).append( mojoDescriptor.getGoal() ).append( ", parameter: " ).append( param.getName() ); - - if ( param.getAlias() != null ) - { - buffer.append( " (alias: " ).append( param.getAlias() ).append( ")" ); - } - - buffer.append( " is deprecated:" ).append( "\n\n" ).append( param.getDeprecated() ).append( "\n" ); - - logger.warn( buffer.toString() ); - } - } - } - } - } - - private void checkRequiredParameters( MojoDescriptor goal, PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator ) - throws PluginConfigurationException - { - // TODO: this should be built in to the configurator, as we presently double process the expressions - - List parameters = goal.getParameters(); - - if ( parameters == null ) - { - return; - } - - List invalidParameters = new ArrayList(); - - for ( int i = 0; i < parameters.size(); i++ ) - { - Parameter parameter = parameters.get( i ); - - if ( parameter.isRequired() ) - { - // the key for the configuration map we're building. - String key = parameter.getName(); - - Object fieldValue = null; - String expression = null; - PlexusConfiguration value = configuration.getChild( key, false ); - try - { - if ( value != null ) - { - expression = value.getValue( null ); - - fieldValue = expressionEvaluator.evaluate( expression ); - - if ( fieldValue == null ) - { - fieldValue = value.getAttribute( "default-value", null ); - } - } - - if ( ( fieldValue == null ) && StringUtils.isNotEmpty( parameter.getAlias() ) ) - { - value = configuration.getChild( parameter.getAlias(), false ); - if ( value != null ) - { - expression = value.getValue( null ); - fieldValue = expressionEvaluator.evaluate( expression ); - if ( fieldValue == null ) - { - fieldValue = value.getAttribute( "default-value", null ); - } - } - } - } - catch ( ExpressionEvaluationException e ) - { - throw new PluginConfigurationException( goal.getPluginDescriptor(), e.getMessage(), e ); - } - - // only mark as invalid if there are no child nodes - if ( ( fieldValue == null ) && ( ( value == null ) || ( value.getChildCount() == 0 ) ) ) - { - parameter.setExpression( expression ); - invalidParameters.add( parameter ); - } - } - } - - if ( !invalidParameters.isEmpty() ) - { - throw new PluginParameterException( goal, invalidParameters ); - } - } - - private void validatePomConfiguration( MojoDescriptor goal, PlexusConfiguration pomConfiguration ) - throws PluginConfigurationException - { - List parameters = goal.getParameters(); - - if ( parameters == null ) - { - return; - } - - for ( int i = 0; i < parameters.size(); i++ ) - { - Parameter parameter = parameters.get( i ); - - // the key for the configuration map we're building. - String key = parameter.getName(); - - PlexusConfiguration value = pomConfiguration.getChild( key, false ); - - if ( ( value == null ) && StringUtils.isNotEmpty( parameter.getAlias() ) ) - { - key = parameter.getAlias(); - value = pomConfiguration.getChild( key, false ); - } - - if ( value != null ) - { - // Make sure the parameter is either editable/configurable, or else is NOT specified in the POM - if ( !parameter.isEditable() ) - { - StringBuilder errorMessage = new StringBuilder( 128 ).append( "ERROR: Cannot override read-only parameter: " ); - errorMessage.append( key ); - errorMessage.append( " in goal: " ).append( goal.getFullGoalName() ); - - throw new PluginConfigurationException( goal.getPluginDescriptor(), errorMessage.toString() ); - } - - String deprecated = parameter.getDeprecated(); - if ( StringUtils.isNotEmpty( deprecated ) ) - { - logger.warn( "DEPRECATED [" + parameter.getName() + "]: " + deprecated ); - } - } - } - } - - */ - - // USED BY MAVEN HELP PLUGIN - @Deprecated - public Map getPhaseToLifecycleMap() - { - return phaseToLifecycleMap; - } - - private String getLifecyclePhaseList() - { - Set phases = new LinkedHashSet(); - - for ( Lifecycle lifecycle : lifecycles ) - { - phases.addAll( lifecycle.getPhases() ); - } - - return StringUtils.join( phases.iterator(), ", " ); - } - - private boolean requiresProject( MavenSession session ) - { - List goals = session.getGoals(); - if ( goals != null ) - { - for ( String goal : goals ) - { - if ( !isGoalSpecification( goal ) ) - { - return true; - } - } - } - return false; - } } diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycles.java b/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycles.java new file mode 100644 index 0000000000..e90cb76310 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycles.java @@ -0,0 +1,173 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle; + +import org.apache.maven.lifecycle.internal.BuilderCommon; +import org.apache.maven.lifecycle.internal.ExecutionPlanItem; +import org.apache.maven.plugin.*; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException; +import org.codehaus.plexus.util.StringUtils; + +import java.util.*; + +/** + * @author Jason van Zyl + * @author Kristian Rosenvold + */ +//TODO: The configuration for the lifecycle needs to be externalized so that I can use the annotations properly for the wiring and reference and external source for the lifecycle configuration. +public class DefaultLifecycles + implements Initializable +{ + // @Configuration(source="org/apache/maven/lifecycle/lifecycles.xml") + + private List lifecycles; + + private List schedules; + + /** + * We use this to display all the lifecycles available and their phases to users. Currently this is primarily + * used in the IDE integrations where a UI is presented to the user and they can select the lifecycle phase + * they would like to execute. + */ + private Map lifecycleMap; + + /** + * We use this to map all phases to the lifecycle that contains it. This is used so that a user can specify the + * phase they want to execute and we can easily determine what lifecycle we need to run. + */ + private Map phaseToLifecycleMap; + + @SuppressWarnings({"UnusedDeclaration"}) + public DefaultLifecycles() + { + } + + public DefaultLifecycles( List lifecycles, List schedules ) + { + this.lifecycles = lifecycles; + this.schedules = schedules; + } + + public void initialize() + throws InitializationException + { + lifecycleMap = new HashMap(); + + // If people are going to make their own lifecycles then we need to tell people how to namespace them correctly so + // that they don't interfere with internally defined lifecycles. + + phaseToLifecycleMap = new HashMap(); + + for ( Lifecycle lifecycle : lifecycles ) + { + for ( String phase : lifecycle.getPhases() ) + { + // The first definition wins. + if ( !phaseToLifecycleMap.containsKey( phase ) ) + { + phaseToLifecycleMap.put( phase, lifecycle ); + } + } + + lifecycleMap.put( lifecycle.getId(), lifecycle ); + } + } + + + public List createExecutionPlanItem( MavenProject mavenProject, List executions ) + throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException, + PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException, + NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException + { + BuilderCommon.attachToThread( mavenProject ); + + List result = new ArrayList(); + for ( MojoExecution mojoExecution : executions ) + { + String lifeCyclePhase = mojoExecution.getMojoDescriptor().getPhase(); + final Scheduling scheduling = getScheduling( "default" ); + Schedule schedule = null; + if ( scheduling != null ) + { + schedule = scheduling.getSchedule( mojoExecution.getPlugin() ); + if ( schedule == null ) + { + schedule = scheduling.getSchedule( lifeCyclePhase ); + } + } + result.add( new ExecutionPlanItem( mojoExecution, schedule ) ); + + } + return result; + } + + /** + * Gets scheduling associated with a given phase. + *

+ * This is part of the experimental weave mode and therefore not part of the public api. + * + * @param lifecyclePhaseName + * @return + */ + + private Scheduling getScheduling( String lifecyclePhaseName ) + { + for ( Scheduling schedule : schedules ) + { + if ( lifecyclePhaseName.equals( schedule.getLifecycle() ) ) + { + return schedule; + } + } + return null; + } + + public Lifecycle get( String key ) + { + return phaseToLifecycleMap.get( key ); + } + + public Map getPhaseToLifecycleMap() + { + return phaseToLifecycleMap; + } + + public List getLifeCycles() + { + return lifecycles; + } + + public List getSchedules() + { + return schedules; + } + + public String getLifecyclePhaseList() + { + Set phases = new LinkedHashSet(); + + for ( Lifecycle lifecycle : lifecycles ) + { + phases.addAll( lifecycle.getPhases() ); + } + + return StringUtils.join( phases.iterator(), ", " ); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/LifeCyclePluginAnalyzer.java b/maven-core/src/main/java/org/apache/maven/lifecycle/LifeCyclePluginAnalyzer.java new file mode 100644 index 0000000000..a498054811 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/LifeCyclePluginAnalyzer.java @@ -0,0 +1,28 @@ + +/* + * 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. + */ + +package org.apache.maven.lifecycle; + +import java.util.Set; +import org.apache.maven.model.Plugin; + + +/** + * @author Kristian Rosenvold + */ +public interface LifeCyclePluginAnalyzer { + Set getPluginsBoundByDefaultToAllLifecycles(String packaging); +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/Lifecycle.java b/maven-core/src/main/java/org/apache/maven/lifecycle/Lifecycle.java index 241dd12a5d..2b01bdcd69 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/Lifecycle.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/Lifecycle.java @@ -27,6 +27,14 @@ import java.util.Map; */ public class Lifecycle { + public Lifecycle() { + } + + public Lifecycle(String id, List phases, Map defaultPhases) { + this.id = id; + this.phases = phases; + this.defaultPhases = defaultPhases; + } // // clean diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleEventCatapult.java b/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleEventCatapult.java index 34b9f99441..32094f1b2c 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleEventCatapult.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleEventCatapult.java @@ -28,7 +28,7 @@ import org.apache.maven.execution.ExecutionListener; * * @author Benjamin Bentmann */ -interface LifecycleEventCatapult +public interface LifecycleEventCatapult { /** diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutionException.java b/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutionException.java index f4504f3016..c4a3f4c4c8 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutionException.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutionException.java @@ -1,4 +1,18 @@ package org.apache.maven.lifecycle; +/* + * 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. + */ /* * Licensed to the Apache Software Foundation (ASF) under one @@ -52,19 +66,19 @@ public class LifecycleExecutionException this.project = project; } - LifecycleExecutionException( String message, MojoExecution execution, MavenProject project ) + public LifecycleExecutionException( String message, MojoExecution execution, MavenProject project ) { super( message ); this.project = project; } - LifecycleExecutionException( String message, MojoExecution execution, MavenProject project, Throwable cause ) + public LifecycleExecutionException( String message, MojoExecution execution, MavenProject project, Throwable cause ) { super( message, cause ); this.project = project; } - LifecycleExecutionException( MojoExecution execution, MavenProject project, Throwable cause ) + public LifecycleExecutionException( MojoExecution execution, MavenProject project, Throwable cause ) { this( createMessage( execution, project, cause ), execution, project, cause ); } diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutor.java b/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutor.java index e326865cc7..6e5abc98fa 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutor.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutor.java @@ -19,21 +19,10 @@ package org.apache.maven.lifecycle; * under the License. */ -import java.util.List; import java.util.Set; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Plugin; -import org.apache.maven.plugin.InvalidPluginDescriptorException; -import org.apache.maven.plugin.MojoExecution; -import org.apache.maven.plugin.MojoNotFoundException; -import org.apache.maven.plugin.PluginDescriptorParsingException; -import org.apache.maven.plugin.PluginManagerException; -import org.apache.maven.plugin.PluginNotFoundException; -import org.apache.maven.plugin.PluginResolutionException; -import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; -import org.apache.maven.plugin.version.PluginVersionResolutionException; -import org.apache.maven.project.MavenProject; /** * @author Jason van Zyl @@ -45,25 +34,10 @@ public interface LifecycleExecutor @Deprecated String ROLE = LifecycleExecutor.class.getName(); - /** - * Calculate the list of {@link org.apache.maven.plugin.descriptor.MojoDescriptor} objects to run for the selected lifecycle phase. - * - * @param phase - * @param session - * @return - * @throws InvalidPluginDescriptorException - * @throws LifecycleExecutionException - */ - MavenExecutionPlan calculateExecutionPlan( MavenSession session, String... tasks ) - throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, - MojoNotFoundException, NoPluginFoundForPrefixException, - InvalidPluginDescriptorException, PluginManagerException, LifecyclePhaseNotFoundException, - LifecycleNotFoundException, PluginVersionResolutionException; - // For a given project packaging find all the plugins that are bound to any registered // lifecycles. The project builder needs to now what default plugin information needs to be // merged into POM being built. Once the POM builder has this plugin information, versions can be assigned - // by the POM builder because they will have to be defined in plugin management. Once this is done then it + // by the POM builder because they will have to be defined in plugin management. Once this is setComplete then it // can be passed back so that the default configuraiton information can be populated. // // We need to know the specific version so that we can lookup the right version of the plugin descriptor @@ -77,33 +51,4 @@ public interface LifecycleExecutor void execute( MavenSession session ); - /** - * Calculates the forked mojo executions requested by the mojo associated with the specified mojo execution. - * - * @param mojoExecution The mojo execution for which to calculate the forked mojo executions, must not be {@code - * null}. - * @param session The current build session that holds the projects and further settings, must not be {@code null}. - */ - void calculateForkedExecutions( MojoExecution mojoExecution, MavenSession session ) - throws MojoNotFoundException, PluginNotFoundException, PluginResolutionException, - PluginDescriptorParsingException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, - LifecyclePhaseNotFoundException, LifecycleNotFoundException, PluginVersionResolutionException; - - /** - * Executes the previously calculated forked mojo executions of the given mojo execution. If the specified mojo - * execution requires no forking, this method does nothing. The return value denotes a subset of the projects from - * the session that have been forked. The method {@link MavenProject#getExecutionProject()} of those projects - * returns the project clone on which the forked execution were performed. It is the responsibility of the caller to - * reset those execution projects to {@code null} once they are no longer needed to free memory and to avoid - * accidental usage by unrelated mojos. - * - * @param mojoExecution The mojo execution whose forked mojo executions should be processed, must not be {@code - * null}. - * @param session The current build session that holds the projects and further settings, must not be {@code null}. - * @return The (unmodifiable) list of projects that have been forked, can be empty if no forking was required but - * will never be {@code null}. - */ - List executeForkedExecutions( MojoExecution mojoExecution, MavenSession session ) - throws LifecycleExecutionException; - } diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/MavenExecutionPlan.java b/maven-core/src/main/java/org/apache/maven/lifecycle/MavenExecutionPlan.java index 0f0972f839..e966f4272e 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/MavenExecutionPlan.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/MavenExecutionPlan.java @@ -19,10 +19,11 @@ package org.apache.maven.lifecycle; * under the License. */ -import java.util.List; -import java.util.Set; +import java.util.*; +import org.apache.maven.lifecycle.internal.ExecutionPlanItem; import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.descriptor.MojoDescriptor; //TODO: lifecycles being executed //TODO: what runs in each phase @@ -30,29 +31,77 @@ import org.apache.maven.plugin.MojoExecution; //TODO: project dependencies that need downloading //TODO: unfortunately the plugins need to be downloaded in order to get the plugin.xml file. need to externalize this from the plugin archive. //TODO: this will be the class that people get in IDEs to modify -public class MavenExecutionPlan +public class MavenExecutionPlan implements Iterable { - /** Individual executions that must be performed. */ - private List executions; - + + /* + At the moment, this class is totally immutable, and this is in line with thoughts about the + pre-calculated execution plan that stays the same during the execution. + + If deciding to add mutable state to this class, it should be at least considered to + separate this into a separate mutable structure. + + */ /** For project dependency resolution, the scopes of resolution required if any. */ - private Set requiredDependencyResolutionScopes; + private final Set requiredDependencyResolutionScopes; /** For project dependency collection, the scopes of collection required if any. */ - private Set requiredDependencyCollectionScopes; + private final Set requiredDependencyCollectionScopes; - public MavenExecutionPlan( List executions, Set requiredDependencyResolutionScopes, - Set requiredDependencyCollectionScopes ) - { - this.executions = executions; + private final List planItem; + + private final Map lastInPhase; + private final List phasesInOrder; + + public MavenExecutionPlan(Set requiredDependencyResolutionScopes, Set requiredDependencyCollectionScopes, List planItem) { this.requiredDependencyResolutionScopes = requiredDependencyResolutionScopes; this.requiredDependencyCollectionScopes = requiredDependencyCollectionScopes; + this.planItem = planItem; + lastInPhase = new HashMap(); + phasesInOrder = new ArrayList(); + for (ExecutionPlanItem executionPlanItem : getExecutionPlanItems()) { + final String phaseName = getPhase( executionPlanItem ); + if (!lastInPhase.containsKey( phaseName )){ + phasesInOrder.add( phaseName ); + } + lastInPhase.put( phaseName, executionPlanItem ); + } + + } - public List getExecutions() - { - return executions; + private String getPhase( ExecutionPlanItem executionPlanItem){ + final MojoExecution mojoExecution = executionPlanItem.getMojoExecution(); + final MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); + return mojoDescriptor.getPhase(); + } + public Iterator iterator() { + return getExecutionPlanItems().iterator(); + } + + /** + * Returns the last ExecutionPlanItem in the supplied phase. If no items are in the specified phase, + * the closest upstream item will be returned. + * @param executionPlanItem The execution plan item + * @return The ExecutionPlanItem or null if none can be found + */ + public ExecutionPlanItem findLastInPhase( ExecutionPlanItem executionPlanItem){ + ExecutionPlanItem executionPlanItem1 = lastInPhase.get( getPhase( executionPlanItem ) ); + return executionPlanItem1; + } + + private List getExecutionPlanItems() + { + return planItem; + } + + public void forceAllComplete(){ + for (ExecutionPlanItem executionPlanItem : getExecutionPlanItems()) { + executionPlanItem.forceComplete(); + } + } + public Set getRequiredResolutionScopes() { @@ -64,4 +113,17 @@ public class MavenExecutionPlan return requiredDependencyCollectionScopes; } + + public List getMojoExecutions(){ + List result = new ArrayList(); + for ( ExecutionPlanItem executionPlanItem : planItem ) + { + result.add( executionPlanItem.getMojoExecution()); + } + return result; + } + + public int size() { + return planItem.size(); + } } diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/Schedule.java b/maven-core/src/main/java/org/apache/maven/lifecycle/Schedule.java new file mode 100644 index 0000000000..e63ba7356f --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/Schedule.java @@ -0,0 +1,90 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle; + +import org.apache.maven.model.Plugin; +import org.apache.maven.plugin.descriptor.MojoDescriptor; + +/** + * @author Kristian Rosenvold + */ +public class Schedule { + private String phase; + private String mojoClass; + private boolean mojoSynchronized; + // Indicates that this phase/mojo does not need to respect the reactor-dependency graph + // (Module lifecycle order still must be respected ) + private boolean parallel; + + public Schedule() { + } + + public Schedule( String phase, boolean mojoSynchronized, boolean parallel ) { + this.phase = phase; + this.mojoSynchronized = mojoSynchronized; + this.parallel = parallel; + } + + + public boolean isMissingPhase(){ + return null == phase; + } + public String getPhase() { + return phase; + } + + public void setPhase(String phase) { + this.phase = phase; + } + + public String getMojoClass() { + return mojoClass; + } + + public void setMojoClass(String mojoClass) { + this.mojoClass = mojoClass; + } + + public boolean isMojoSynchronized() { + return mojoSynchronized; + } + + public void setMojoSynchronized(boolean mojoSynchronized) { + this.mojoSynchronized = mojoSynchronized; + } + + + public boolean isParallel() + { + return parallel; + } + + public void setParallel( boolean parallel ) + { + this.parallel = parallel; + } + + + @Override + public String toString() { + return "Schedule{" + + "phase='" + phase + '\'' + + ", mojoClass='" + mojoClass + '\'' + + ", mojoSynchronized=" + mojoSynchronized + + ", parallel=" + parallel + + '}'; + } +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/Scheduling.java b/maven-core/src/main/java/org/apache/maven/lifecycle/Scheduling.java new file mode 100644 index 0000000000..85ad571f2e --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/Scheduling.java @@ -0,0 +1,74 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle; + +import org.apache.maven.model.Plugin; + +import java.util.List; +import java.util.Map; + +/** + * Class Scheduling. + */ +public class Scheduling +{ + private String lifecycle; + + private List schedules; + + public Scheduling() { + } + + public Scheduling(String lifecycle, List schedules) { + this.lifecycle = lifecycle; + this.schedules = schedules; + } + + public String getLifecycle() { + return lifecycle; + } + + public void setLifecycle(String lifecycle) { + this.lifecycle = lifecycle; + } + + public List getSchedules() { + return schedules; + } + + + public Schedule getSchedule(String phaseName){ + if (phaseName == null) return null; + for (Schedule schedule : schedules) { + if (phaseName.equals(schedule.getPhase()) ) + return schedule; + } + return null; + } + + public Schedule getSchedule(Plugin mojoClass){ + if (mojoClass == null) return null; + for (Schedule schedule : schedules) { + if (mojoClass.getKey().equals(schedule.getMojoClass()) ) + return schedule; + } + return null; + } + + public void setSchedules(List schedules) { + this.schedules = schedules; + } +} \ No newline at end of file diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/BuildListCalculator.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/BuildListCalculator.java new file mode 100644 index 0000000000..c51f12f45c --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/BuildListCalculator.java @@ -0,0 +1,102 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.LifecycleNotFoundException; +import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException; +import org.apache.maven.plugin.*; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.util.StringUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author Kristian Rosenvold + * This class is not part of any public api and can be changed or deleted without prior notice. + */ +@Component(role = BuildListCalculator.class) +public class BuildListCalculator +{ + @Requirement + private LifecycleTaskSegmentCalculator lifeCycleTaskSegmentCalculator; + + @SuppressWarnings({"UnusedDeclaration"}) + public BuildListCalculator() + { + } + + public BuildListCalculator( LifecycleTaskSegmentCalculator lifeCycleTaskSegmentCalculator ) + { + this.lifeCycleTaskSegmentCalculator = lifeCycleTaskSegmentCalculator; + } + + public List calculateTaskSegments( MavenSession session ) + throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, + MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, + PluginVersionResolutionException, LifecyclePhaseNotFoundException, LifecycleNotFoundException + { + + MavenProject rootProject = session.getTopLevelProject(); + + List tasks = session.getGoals(); + + if ( tasks == null || tasks.isEmpty() ) + { + if ( !StringUtils.isEmpty( rootProject.getDefaultGoal() ) ) + { + tasks = Arrays.asList( StringUtils.split( rootProject.getDefaultGoal() ) ); + } + } + + return lifeCycleTaskSegmentCalculator.calculateTaskSegments( session, tasks ); + } + + public ProjectBuildList calculateProjectBuilds( MavenSession session, List taskSegments ) + { + List projectBuilds = new ArrayList(); + + MavenProject rootProject = session.getTopLevelProject(); + + for ( TaskSegment taskSegment : taskSegments ) + { + List projects; + + if ( taskSegment.isAggregating() ) + { + projects = Collections.singletonList( rootProject ); + } + else + { + projects = session.getProjects(); + } + for ( MavenProject project : projects ) + { + BuilderCommon.attachToThread( project ); // Not totally sure if this is needed for anything + MavenSession copiedSession = session.clone(); + copiedSession.setCurrentProject( project ); + projectBuilds.add( new ProjectSegment( project, taskSegment, copiedSession ) ); + } + } + return new ProjectBuildList( projectBuilds ); + } +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/BuilderCommon.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/BuilderCommon.java new file mode 100644 index 0000000000..648624749b --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/BuilderCommon.java @@ -0,0 +1,131 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.BuildFailure; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.*; +import org.apache.maven.plugin.*; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; + +/** + * Common code that is shared by the LifecycleModuleBuilder and the LifeCycleWeaveBuilder + * + * @author Kristian Rosenvold + * Builds one or more lifecycles for a full module + * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +@Component(role = BuilderCommon.class) +public class BuilderCommon +{ + @Requirement + private LifecycleDebugLogger lifecycleDebugLogger; + + @Requirement + private LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator; + + @Requirement + private LifecycleDependencyResolver lifecycleDependencyResolver; + + @SuppressWarnings({"UnusedDeclaration"}) + public BuilderCommon() + { + } + + public BuilderCommon( LifecycleDebugLogger lifecycleDebugLogger, + LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator, + LifecycleDependencyResolver lifecycleDependencyResolver ) + { + this.lifecycleDebugLogger = lifecycleDebugLogger; + this.lifeCycleExecutionPlanCalculator = lifeCycleExecutionPlanCalculator; + this.lifecycleDependencyResolver = lifecycleDependencyResolver; + } + + public MavenExecutionPlan resolveBuildPlan( MavenSession session, MavenProject project, TaskSegment taskSegment ) + throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException, + PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException, + NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException, + LifecycleExecutionException + { + MavenExecutionPlan executionPlan = + lifeCycleExecutionPlanCalculator.calculateExecutionPlan( session, project, taskSegment.getTasks() ); + lifecycleDebugLogger.debugProjectPlan( project, executionPlan ); + + // TODO: once we have calculated the build plan then we should accurately be able to download + // the project dependencies. Having it happen in the plugin manager is a tangled mess. We can optimize + // this later by looking at the build plan. Would be better to just batch download everything required + // by the reactor. + + lifecycleDependencyResolver.resolveDependencies( taskSegment.isAggregating(), project, session, executionPlan ); + return executionPlan; + } + + + public static void handleBuildError( final ReactorContext buildContext, final MavenSession rootSession, + final MavenProject mavenProject, final Exception e, final long buildStartTime ) + { + buildContext.getResult().addException( e ); + + long buildEndTime = System.currentTimeMillis(); + + buildContext.getResult().addBuildSummary( new BuildFailure( mavenProject, buildEndTime - buildStartTime, e ) ); + + DefaultLifecycleExecutor.fireEvent( rootSession, null, LifecycleEventCatapult.PROJECT_FAILED ); + + if ( MavenExecutionRequest.REACTOR_FAIL_NEVER.equals( rootSession.getReactorFailureBehavior() ) ) + { + // continue the build + } + else if ( MavenExecutionRequest.REACTOR_FAIL_AT_END.equals( rootSession.getReactorFailureBehavior() ) ) + { + // continue the build but ban all projects that depend on the failed one + buildContext.getReactorBuildStatus().blackList( mavenProject ); + } + else if ( MavenExecutionRequest.REACTOR_FAIL_FAST.equals( rootSession.getReactorFailureBehavior() ) ) + { + buildContext.getReactorBuildStatus().halt(); + } + else + { + throw new IllegalArgumentException( + "invalid reactor failure behavior " + rootSession.getReactorFailureBehavior() ); + } + } + + public static void attachToThread( MavenProject currentProject ) + { + ClassRealm projectRealm = currentProject.getClassRealm(); + if ( projectRealm != null ) + { + Thread.currentThread().setContextClassLoader( projectRealm ); + } + } + + // Todo: I'm really wondering where this method belongs; smells like it should be on MavenProject, but for some reason it isn't ? + // This localization is kind-of a code smell. + + public static String getKey( MavenProject project ) + { + return project.getGroupId() + ':' + project.getArtifactId() + ':' + project.getVersion(); + } + + +} \ No newline at end of file diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/BuiltLogItem.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/BuiltLogItem.java new file mode 100644 index 0000000000..600bf50b28 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/BuiltLogItem.java @@ -0,0 +1,98 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.project.MavenProject; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Kristian Rosenvold + * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +public class BuiltLogItem +{ + private final ExecutionPlanItem executionPlanItem; + + private final MavenProject project; + + private final long startTime; + + private long endTime; + + private final List waits = new ArrayList(); + + public BuiltLogItem( MavenProject project, ExecutionPlanItem executionPlanItem ) + { + this.executionPlanItem = executionPlanItem; + this.project = project; + startTime = System.currentTimeMillis(); + + } + + public void setComplete() + { + endTime = System.currentTimeMillis(); + } + + public void addWait( MavenProject upstreamProject, ExecutionPlanItem inSchedule, long startWait ) + { + long now = System.currentTimeMillis(); + if ( ( now - startWait ) > 1 ) + { + waits.add( new WaitLogEntry( upstreamProject, inSchedule, startWait, now ) ); + } + } + + public String toString( long rootStart ) + { + StringBuilder result = new StringBuilder(); + result.append( String.format( "%1d %2d ", startTime - rootStart, endTime - rootStart ) ); + result.append( project.getName() ); + result.append( " " ); + result.append( executionPlanItem.getMojoExecution().getArtifactId() ); + for ( WaitLogEntry waitLogEntry : waits ) + { + result.append( waitLogEntry.toString() ); + } + return result.toString(); + } + + class WaitLogEntry + { + private final ExecutionPlanItem executionPlanItem; + + private final MavenProject upstreamProject; + + private final long start; + + private final long stop; + + WaitLogEntry( MavenProject upstreamProject, ExecutionPlanItem executionPlanItem, long start, long stop ) + { + this.upstreamProject = upstreamProject; + this.executionPlanItem = executionPlanItem; + this.start = start; + this.stop = stop; + } + + public String toString() + { + return upstreamProject.getName() + " " + executionPlanItem.getMojoExecution().getArtifactId() + ", wait=" + + ( stop - start ); + } + } +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ConcurrencyDependencyGraph.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ConcurrencyDependencyGraph.java new file mode 100644 index 0000000000..82a0bf61e2 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ConcurrencyDependencyGraph.java @@ -0,0 +1,104 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.ProjectDependencyGraph; +import org.apache.maven.project.MavenProject; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +/** + * Presents a view of the Dependency Graph that is suited for concurrent building. + * + * @author Kristian Rosenvold + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +public class ConcurrencyDependencyGraph +{ + + private final ProjectBuildList projectBuilds; + + private final ProjectDependencyGraph projectDependencyGraph; + + private final HashSet finishedProjects = new HashSet(); + + + public ConcurrencyDependencyGraph( ProjectBuildList projectBuilds, ProjectDependencyGraph projectDependencyGraph ) + { + this.projectDependencyGraph = projectDependencyGraph; + this.projectBuilds = projectBuilds; + } + + + public int getNumberOfBuilds() + { + return projectBuilds.size(); + } + + /** + * Gets all the builds that have no reactor-dependencies + * + * @return A list of all the initial builds + */ + + public List getRootSchedulableBuilds() + { + List result = new ArrayList(); + for ( ProjectSegment projectBuild : projectBuilds ) + { + if ( projectDependencyGraph.getUpstreamProjects( projectBuild.getProject(), false ).size() == 0 ) + { + result.add( projectBuild.getProject() ); + } + } + return result; + } + + /** + * Marks the provided project as finished. Returns a list of + * + * @param mavenProject The project + * @return The list of builds that are eligible for starting now that the provided project is done + */ + public List markAsFinished( MavenProject mavenProject ) + { + finishedProjects.add( mavenProject ); + return getSchedulableNewProcesses( mavenProject ); + } + + private List getSchedulableNewProcesses( MavenProject finishedProject ) + { + List result = new ArrayList(); + // schedule dependent projects, if all of their requirements are met + for ( MavenProject dependentProject : projectDependencyGraph.getDownstreamProjects( finishedProject, false ) ) + { + final List upstreamProjects = + projectDependencyGraph.getUpstreamProjects( dependentProject, false ); + if ( finishedProjects.containsAll( upstreamProjects ) ) + { + result.add( dependentProject ); + } + } + return result; + } + + public ProjectBuildList getProjectBuilds() + { + return projectBuilds; + } +} \ No newline at end of file diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ConcurrentBuildLogger.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ConcurrentBuildLogger.java new file mode 100644 index 0000000000..5616339f5e --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ConcurrentBuildLogger.java @@ -0,0 +1,75 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.project.MavenProject; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Handles all concurrency-related logging. + *

+ * The logging/diagnostic needs of a concurrent build are different from a linear build. This + * delta required to analyze a concurrent build is located here. + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + * + * @author Kristian Rosenvold + */ +public class ConcurrentBuildLogger +{ + private final long startTime; + + private final Map threadMap = new ConcurrentHashMap(); + + public ConcurrentBuildLogger() + { + startTime = System.currentTimeMillis(); + } + + + List items = Collections.synchronizedList( new ArrayList() ); + + public BuiltLogItem createBuildLogItem( MavenProject project, ExecutionPlanItem current ) + { + threadMap.put( project, Thread.currentThread() ); + BuiltLogItem result = new BuiltLogItem( project, current ); + items.add( result ); + return result; + } + + public String toString() + { + StringBuilder result = new StringBuilder(); + for ( Map.Entry mavenProjectThreadEntry : threadMap.entrySet() ) + { + result.append( mavenProjectThreadEntry.getKey().getName() ); + result.append( " ran on " ); + result.append( mavenProjectThreadEntry.getValue().getName() ); + result.append( "\n" ); + } + + for ( BuiltLogItem builtLogItem : items ) + { + result.append( builtLogItem.toString( startTime ) ); + result.append( "\n" ); + } + return result.toString(); + } +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/DependencyContext.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/DependencyContext.java new file mode 100644 index 0000000000..2fb50ea65b --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/DependencyContext.java @@ -0,0 +1,115 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.project.MavenProject; + +import java.util.Collection; + +/** + * Context of dependency artifacts for the entire build. + * + * @author Benjamin Bentmann + * @author Kristian Rosenvold (class extract only) + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +// TODO: From a concurrecy perspective, this class is not good. The combination of mutable/immutable state is not nice +public class DependencyContext +{ + + private final Collection scopesToCollect; + + private final Collection scopesToResolve; + + private final boolean aggregating; + + private volatile MavenProject lastProject; + + private volatile Collection lastDependencyArtifacts; + + private volatile int lastDependencyArtifactCount; + + private DependencyContext( Collection scopesToCollect, Collection scopesToResolve, + boolean aggregating ) + { + this.scopesToCollect = scopesToCollect; + this.scopesToResolve = scopesToResolve; + this.aggregating = aggregating; + } + + public DependencyContext( MavenExecutionPlan executionPlan, boolean aggregating ) + { + this( executionPlan.getRequiredCollectionScopes(), executionPlan.getRequiredResolutionScopes(), aggregating ); + } + + public void setLastDependencyArtifacts( Collection lastDependencyArtifacts ) + { + this.lastDependencyArtifacts = lastDependencyArtifacts; + lastDependencyArtifactCount = ( lastDependencyArtifacts != null ) ? lastDependencyArtifacts.size() : 0; + } + + public MavenProject getLastProject() + { + return lastProject; + } + + public void setLastProject( MavenProject lastProject ) + { + this.lastProject = lastProject; + } + + public Collection getScopesToCollect() + { + return scopesToCollect; + } + + public Collection getScopesToResolve() + { + return scopesToResolve; + } + + public boolean isAggregating() + { + return aggregating; + } + + public DependencyContext clone() + { + return new DependencyContext( scopesToCollect, scopesToResolve, aggregating ); + } + + public boolean isSameProject( MavenSession session ) + { + return ( lastProject == session.getCurrentProject() ); + } + + public boolean isSameButUpdatedProject( MavenSession session ) + { + if ( isSameProject( session ) ) + { + if ( lastDependencyArtifacts != lastProject.getDependencyArtifacts() || + ( lastDependencyArtifacts != null && lastDependencyArtifactCount != lastDependencyArtifacts.size() ) ) + { + return true; + + } + } + return false; + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ExecutionPlanItem.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ExecutionPlanItem.java new file mode 100644 index 0000000000..deda72246f --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ExecutionPlanItem.java @@ -0,0 +1,128 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.lifecycle.Schedule; +import org.apache.maven.model.Plugin; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.descriptor.MojoDescriptor; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Wraps individual MojoExecutions, containing information about completion status and scheduling. + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + * + * @author Kristian Rosenvold + */ +public class ExecutionPlanItem +{ + private final MojoExecution mojoExecution; + + private final Schedule schedule; + // Completeness just indicates that it has been run or failed + + private final AtomicBoolean complete = new AtomicBoolean( false ); + + private final Object monitor = new Object(); + + public ExecutionPlanItem( MojoExecution mojoExecution, Schedule schedule ) + { + this.mojoExecution = mojoExecution; + this.schedule = schedule; + } + + public MojoExecution getMojoExecution() + { + return mojoExecution; + } + + public void setComplete() + { + boolean transitionSuccessful = ensureComplete(); + if ( !transitionSuccessful ) + { + throw new IllegalStateException( "Expected to be able to setComplete node, but was complete already" ); + } + } + + public boolean ensureComplete() + { + boolean f = complete.compareAndSet( false, true ); + notifyListeners(); + return f; + } + + private void notifyListeners() + { + synchronized ( monitor ) + { + monitor.notifyAll(); + } + } + + public void forceComplete() + { + final boolean b = complete.getAndSet( true ); + if ( !b ) + { + notifyListeners(); + } // Release anyone waiting for us + } + + public void waitUntilDone() + throws InterruptedException + { + synchronized ( monitor ) + { + while ( !complete.get() ) + { + monitor.wait( 100 ); + } + } + } + + public Schedule getSchedule() + { + return schedule; + } + + public boolean hasSchedule( Schedule other ) + { + if ( getSchedule() != null && !getSchedule().isMissingPhase() ) + { + if ( other.getPhase().equals( getSchedule().getPhase() ) ) + { + return true; + } + } + return false; + } + + public Plugin getPlugin() + { + final MojoDescriptor mojoDescriptor = getMojoExecution().getMojoDescriptor(); + return mojoDescriptor.getPluginDescriptor().getPlugin(); + } + + @Override + public String toString() + { + return "ExecutionPlanItem{" + ", mojoExecution=" + mojoExecution + ", schedule=" + schedule + '}' + + super.toString(); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/GoalTask.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/GoalTask.java new file mode 100644 index 0000000000..0b2ebcdf14 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/GoalTask.java @@ -0,0 +1,39 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +/** + * A task that is a goal + *

+ * TODO: From a concurrecy perspective, this class is not good. The combination of mutable/immutable state is not nice + * + * @author Benjamin Bentmann + */ +public final class GoalTask +{ + + final String pluginGoal; + + public GoalTask( String pluginGoal ) + { + this.pluginGoal = pluginGoal; + } + + @Override + public String toString() + { + return pluginGoal; + } +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDebugLogger.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDebugLogger.java new file mode 100644 index 0000000000..6c6f63344a --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDebugLogger.java @@ -0,0 +1,172 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.execution.ProjectDependencyGraph; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.logging.Logger; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Logs debug output from the various lifecycle phases. + * + * @author Benjamin Bentmann + * @author Jason van Zyl + * @author Kristian Rosenvold (extracted class only) + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +@Component(role = LifecycleDebugLogger.class) +public class LifecycleDebugLogger +{ + @Requirement + private Logger logger; + + + @SuppressWarnings({"UnusedDeclaration"}) + public LifecycleDebugLogger() + { + } + + public LifecycleDebugLogger( Logger logger ) + { + this.logger = logger; + } + + + public void debug( String s ) + { + logger.debug( s ); + } + + public void info( String s ) + { + logger.info( s ); + } + + public void debugReactorPlan( ProjectBuildList projectBuilds ) + { + if ( !logger.isDebugEnabled() ) + { + return; + } + logger.debug( "=== REACTOR BUILD PLAN ================================================" ); + + for ( Iterator it = projectBuilds.iterator(); it.hasNext(); ) + { + ProjectSegment projectBuild = it.next(); + + logger.debug( "Project: " + projectBuild.getProject().getId() ); + logger.debug( "Tasks: " + projectBuild.getTaskSegment().getTasks() ); + logger.debug( "Style: " + ( projectBuild.getTaskSegment().isAggregating() ? "Aggregating" : "Regular" ) ); + + if ( it.hasNext() ) + { + logger.debug( "-----------------------------------------------------------------------" ); + } + } + + logger.debug( "=======================================================================" ); + } + + + public void debugProjectPlan( MavenProject currentProject, MavenExecutionPlan executionPlan ) + { + logger.debug( "=== PROJECT BUILD PLAN ================================================" ); + logger.debug( "Project: " + BuilderCommon.getKey( currentProject ) ); + logger.debug( "Dependencies (collect): " + executionPlan.getRequiredCollectionScopes() ); + logger.debug( "Dependencies (resolve): " + executionPlan.getRequiredResolutionScopes() ); + + for ( ExecutionPlanItem mojoExecution : executionPlan ) + { + debugMojoExecution( mojoExecution.getMojoExecution() ); + } + + logger.debug( "=======================================================================" ); + } + + private void debugMojoExecution( MojoExecution mojoExecution ) + { + String mojoExecId = + mojoExecution.getGroupId() + ':' + mojoExecution.getArtifactId() + ':' + mojoExecution.getVersion() + ':' + + mojoExecution.getGoal() + " (" + mojoExecution.getExecutionId() + ')'; + + Map> forkedExecutions = mojoExecution.getForkedExecutions(); + if ( !forkedExecutions.isEmpty() ) + { + for ( Map.Entry> fork : forkedExecutions.entrySet() ) + { + logger.debug( "--- init fork of " + fork.getKey() + " for " + mojoExecId + " ---" ); + + for ( MojoExecution forkedExecution : fork.getValue() ) + { + debugMojoExecution( forkedExecution ); + } + + logger.debug( "--- exit fork of " + fork.getKey() + " for " + mojoExecId + " ---" ); + } + } + + logger.debug( "-----------------------------------------------------------------------" ); + logger.debug( "Goal: " + mojoExecId ); + logger.debug( + "Style: " + ( mojoExecution.getMojoDescriptor().isAggregator() ? "Aggregating" : "Regular" ) ); + logger.debug( "Configuration: " + mojoExecution.getConfiguration() ); + } + + public void logWeavePlan( MavenSession session ) + { + if ( !logger.isInfoEnabled() ) + { + return; + } + + final ProjectDependencyGraph dependencyGraph = session.getProjectDependencyGraph(); + logger.info( "=== WEAVE CONCURRENCY BUILD PLAN ================================================" ); + for ( MavenProject mavenProject : dependencyGraph.getSortedProjects() ) + { + + StringBuilder item = new StringBuilder(); + item.append( "Project: " ); + item.append( mavenProject.getArtifactId() ); + final List upstreamProjects = dependencyGraph.getUpstreamProjects( mavenProject, false ); + if ( upstreamProjects.size() > 0 ) + { + item.append( " ( " ); + for ( Iterator it = upstreamProjects.iterator(); it.hasNext(); ) + { + final MavenProject kid = it.next(); + item.append( kid.getArtifactId() ); + if ( it.hasNext() ) + { + item.append( ", " ); + } + } + item.append( ")" ); + } + logger.info( item.toString() ); + + } + logger.info( "=======================================================================" ); + } +} \ No newline at end of file diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java new file mode 100644 index 0000000000..9d64e74706 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java @@ -0,0 +1,254 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.ProjectDependenciesResolver; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.ArtifactUtils; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.MultipleArtifactsNotFoundException; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.LifecycleExecutionException; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.model.Dependency; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.logging.Logger; + +import java.util.*; + +/** + * Resolves dependencies for the artifacts in context of the lifecycle build + * + * @author Benjamin Bentmann + * @author Jason van Zyl + * @author Kristian Rosenvold (extracted class) + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +@Component(role = LifecycleDependencyResolver.class) +public class LifecycleDependencyResolver +{ + @Requirement + private ProjectDependenciesResolver projectDependenciesResolver; + + @Requirement + private Logger logger; + + @SuppressWarnings({"UnusedDeclaration"}) + public LifecycleDependencyResolver() + { + } + + public LifecycleDependencyResolver( ProjectDependenciesResolver projectDependenciesResolver, Logger logger ) + { + this.projectDependenciesResolver = projectDependenciesResolver; + this.logger = logger; + } + + public void resolveDependencies( boolean aggregating, MavenProject currentProject, + MavenSession sessionForThisModule, MavenExecutionPlan executionPlan ) + throws LifecycleExecutionException + { + List projectsToResolve = getProjects( currentProject, sessionForThisModule, aggregating ); + resolveDependencies( aggregating, sessionForThisModule, executionPlan, projectsToResolve ); + } + + public static List getProjects( MavenProject project, MavenSession session, boolean aggregator ) + { + if ( aggregator ) + { + return session.getProjects(); + } + else + { + return Collections.singletonList( project ); + } + } + + public void checkForUpdate( MavenSession session, DependencyContext dependenctContext ) + throws LifecycleExecutionException + { + + if ( dependenctContext.isSameButUpdatedProject( session ) ) + { + resolveProjectDependencies( dependenctContext.getLastProject(), dependenctContext.getScopesToCollect(), + dependenctContext.getScopesToResolve(), session, + dependenctContext.isAggregating() ); + } + + dependenctContext.setLastProject( session.getCurrentProject() ); + dependenctContext.setLastDependencyArtifacts( session.getCurrentProject().getDependencyArtifacts() ); + } + + private void resolveDependencies( boolean aggregating, MavenSession session, MavenExecutionPlan executionPlan, + List projectsToResolve ) + throws LifecycleExecutionException + { + for ( MavenProject project : projectsToResolve ) + { + resolveDependencies( project, aggregating, session, executionPlan ); + } + } + + private void resolveDependencies( MavenProject project, boolean aggregating, MavenSession session, + MavenExecutionPlan executionPlan ) + throws LifecycleExecutionException + { + resolveProjectDependencies( project, executionPlan.getRequiredCollectionScopes(), + executionPlan.getRequiredResolutionScopes(), session, aggregating ); + } + + private void resolveProjectDependencies( MavenProject project, Collection scopesToCollect, + Collection scopesToResolve, MavenSession session, + boolean aggregating ) + throws LifecycleExecutionException + { + Set artifacts = + getProjectDependencies( project, scopesToCollect, scopesToResolve, session, aggregating ); + updateProjectArtifacts( project, artifacts ); + } + + private void updateProjectArtifacts( MavenProject project, Set artifacts ) + { + project.setResolvedArtifacts( artifacts ); + + if ( project.getDependencyArtifacts() == null ) + { + project.setDependencyArtifacts( getDependencyArtifacts( project, artifacts ) ); + } + } + + private Set getProjectDependencies( MavenProject project, Collection scopesToCollect, + Collection scopesToResolve, MavenSession session, + boolean aggregating ) + throws LifecycleExecutionException + { + Set artifacts; + try + { + try + { + artifacts = projectDependenciesResolver.resolve( project, scopesToCollect, scopesToResolve, session ); + } + catch ( MultipleArtifactsNotFoundException e ) + { + /* + * MNG-2277, the check below compensates for our bad plugin support where we ended up with aggregator + * plugins that require dependency resolution although they usually run in phases of the build where project + * artifacts haven't been assembled yet. The prime example of this is "mvn release:prepare". + */ + if ( aggregating && areAllArtifactsInReactor( session.getProjects(), e.getMissingArtifacts() ) ) + { + logger.warn( "The following artifacts could not be resolved at this point of the build" + + " but seem to be part of the reactor:" ); + + for ( Artifact artifact : e.getMissingArtifacts() ) + { + logger.warn( "o " + artifact.getId() ); + } + + logger.warn( "Try running the build up to the lifecycle phase \"package\"" ); + + artifacts = new LinkedHashSet( e.getResolvedArtifacts() ); + } + else + { + throw e; + } + } + + return artifacts; + } + catch ( ArtifactResolutionException e ) + { + throw new LifecycleExecutionException( null, project, e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new LifecycleExecutionException( null, project, e ); + } + + } + + + private Set getDependencyArtifacts( MavenProject project, Set artifacts ) + { + Set directDependencies = new HashSet( project.getDependencies().size() * 2 ); + for ( Dependency dependency : project.getDependencies() ) + { + directDependencies.add( dependency.getManagementKey() ); + } + + Set dependencyArtifacts = new LinkedHashSet( project.getDependencies().size() * 2 ); + for ( Artifact artifact : artifacts ) + { + if ( directDependencies.contains( artifact.getDependencyConflictId() ) ) + { + dependencyArtifacts.add( artifact ); + } + } + return dependencyArtifacts; + } + + private boolean areAllArtifactsInReactor( Collection projects, Collection artifacts ) + { + Set projectKeys = getReactorProjectKeys( projects ); + + for ( Artifact artifact : artifacts ) + { + String key = ArtifactUtils.key( artifact ); + if ( !projectKeys.contains( key ) ) + { + return false; + } + } + + return true; + } + + private Set getReactorProjectKeys( Collection projects ) + { + Set projectKeys = new HashSet( projects.size() * 2 ); + for ( MavenProject project : projects ) + { + String key = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() ); + projectKeys.add( key ); + } + return projectKeys; + } + + + public void reResolveReactorArtifacts( ProjectBuildList projectBuilds, boolean aggregating, MavenProject project, + MavenSession session, MavenExecutionPlan executionPlan ) + throws LifecycleExecutionException + { + final Set reactorProjectKeys = projectBuilds.getReactorProjectKeys(); + final Set artifactSet = project.getArtifacts(); + for ( Artifact artifact : artifactSet ) + { + String key = ArtifactUtils.key( artifact ); + if ( reactorProjectKeys.contains( key ) ) + { + artifact.setResolved( false ); + } + + } + resolveDependencies( aggregating, project, session, executionPlan ); + } +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleExecutionPlanCalculator.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleExecutionPlanCalculator.java new file mode 100644 index 0000000000..3389daf3bb --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleExecutionPlanCalculator.java @@ -0,0 +1,42 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.LifecycleNotFoundException; +import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.plugin.*; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.project.MavenProject; + +import java.util.List; + +/** + * @author Benjamin Bentmann + * @author Kristian Rosenvold (extract interface only) + *

+ * NOTE: interface is not part of any public api and can be changed or deleted without prior notice. + */ +public interface LifecycleExecutionPlanCalculator +{ + MavenExecutionPlan calculateExecutionPlan( MavenSession session, MavenProject project, List tasks ) + throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException, + PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException, + NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException; + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleExecutionPlanCalculatorImpl.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleExecutionPlanCalculatorImpl.java new file mode 100644 index 0000000000..7cbf0e037f --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleExecutionPlanCalculatorImpl.java @@ -0,0 +1,673 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.artifact.repository.DefaultRepositoryRequest; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.*; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginExecution; +import org.apache.maven.plugin.*; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.Parameter; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.lifecycle.Execution; +import org.apache.maven.plugin.lifecycle.Phase; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.plugin.version.PluginVersionResolver; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +import java.io.IOException; +import java.util.*; + +/** + * @author Benjamin Bentmann + * @author Kristian Rosenvold (Extract class) + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +@Component(role = LifecycleExecutionPlanCalculator.class) +public class LifecycleExecutionPlanCalculatorImpl + implements LifecycleExecutionPlanCalculator +{ + @Requirement + private PluginVersionResolver pluginVersionResolver; + + @Requirement + private BuildPluginManager pluginManager; + + @Requirement + private DefaultLifecycles defaultLifeCycles; + + @Requirement + private MojoDescriptorCreator mojoDescriptorCreator; + + @Requirement + private LifecyclePluginResolver lifecyclePluginResolver; + + @SuppressWarnings({"UnusedDeclaration"}) + public LifecycleExecutionPlanCalculatorImpl() + { + } + + public LifecycleExecutionPlanCalculatorImpl( BuildPluginManager pluginManager, DefaultLifecycles defaultLifeCycles, + MojoDescriptorCreator mojoDescriptorCreator, + LifecyclePluginResolver lifecyclePluginResolver ) + { + this.pluginManager = pluginManager; + this.defaultLifeCycles = defaultLifeCycles; + this.mojoDescriptorCreator = mojoDescriptorCreator; + this.lifecyclePluginResolver = lifecyclePluginResolver; + } + + public MavenExecutionPlan calculateExecutionPlan( MavenSession session, MavenProject project, List tasks ) + throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException, + PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException, + NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException + { + Set requiredDependencyResolutionScopes = new TreeSet(); + Set requiredDependencyCollectionScopes = new TreeSet(); + + final List executions = + calculateExecutionPlan( session, project, tasks, requiredDependencyResolutionScopes, + requiredDependencyCollectionScopes ); + final List planItem = defaultLifeCycles.createExecutionPlanItem( project, executions ); + + return new MavenExecutionPlan( requiredDependencyResolutionScopes, requiredDependencyCollectionScopes, + planItem ); + + + } + + public List calculateExecutionPlan( MavenSession session, MavenProject project, List tasks, + Set requiredDependencyResolutionScopes, + Set requiredDependencyCollectionScopes ) + throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException, + PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException, + NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException + { + lifecyclePluginResolver.resolveMissingPluginVersions( project, session ); + + List mojoExecutions = new ArrayList(); + + for ( Object task : tasks ) + { + if ( task instanceof GoalTask ) + { + String pluginGoal = ( (GoalTask) task ).pluginGoal; + + MojoDescriptor mojoDescriptor = mojoDescriptorCreator.getMojoDescriptor( pluginGoal, session, project ); + + MojoExecution mojoExecution = + new MojoExecution( mojoDescriptor, "default-cli", MojoExecution.Source.CLI ); + + mojoExecutions.add( mojoExecution ); + } + else if ( task instanceof LifecycleTask ) + { + String lifecyclePhase = ( (LifecycleTask) task ).getLifecyclePhase(); + + Map> phaseToMojoMapping = + calculateLifecycleMappings( session, project, lifecyclePhase ); + + for ( List mojoExecutionsFromLifecycle : phaseToMojoMapping.values() ) + { + mojoExecutions.addAll( mojoExecutionsFromLifecycle ); + } + } + else + { + throw new IllegalStateException( "unexpected task " + task ); + } + } + + for ( MojoExecution mojoExecution : mojoExecutions ) + { + MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); + + if ( mojoDescriptor == null ) + { + mojoDescriptor = pluginManager.getMojoDescriptor( mojoExecution.getPlugin(), mojoExecution.getGoal(), + DefaultRepositoryRequest.getRepositoryRequest( + session, project ) ); + + mojoExecution.setMojoDescriptor( mojoDescriptor ); + } + + populateMojoExecutionConfiguration( project, mojoExecution, + MojoExecution.Source.CLI.equals( mojoExecution.getSource() ) ); + + finalizeMojoConfiguration( mojoExecution ); + + calculateForkedExecutions( mojoExecution, session, project, new HashSet() ); + + collectDependencyRequirements( requiredDependencyResolutionScopes, requiredDependencyCollectionScopes, + mojoExecution ); + } + + return mojoExecutions; + } + + private static void collectDependencyRequirements( Collection requiredDependencyResolutionScopes, + Collection requiredDependencyCollectionScopes, + MojoExecution mojoExecution ) + { + MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); + + String requiredDependencyResolutionScope = mojoDescriptor.getDependencyResolutionRequired(); + + if ( StringUtils.isNotEmpty( requiredDependencyResolutionScope ) ) + { + requiredDependencyResolutionScopes.add( requiredDependencyResolutionScope ); + } + + String requiredDependencyCollectionScope = mojoDescriptor.getDependencyCollectionRequired(); + + if ( StringUtils.isNotEmpty( requiredDependencyCollectionScope ) ) + { + requiredDependencyCollectionScopes.add( requiredDependencyCollectionScope ); + } + + for ( List forkedExecutions : mojoExecution.getForkedExecutions().values() ) + { + for ( MojoExecution forkedExecution : forkedExecutions ) + { + collectDependencyRequirements( requiredDependencyResolutionScopes, requiredDependencyCollectionScopes, + forkedExecution ); + } + } + } + + + private Map> calculateLifecycleMappings( MavenSession session, MavenProject project, + String lifecyclePhase ) + throws LifecyclePhaseNotFoundException, PluginNotFoundException, PluginResolutionException, + PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException + { + /* + * Determine the lifecycle that corresponds to the given phase. + */ + + Lifecycle lifecycle = defaultLifeCycles.get( lifecyclePhase ); + + if ( lifecycle == null ) + { + throw new LifecyclePhaseNotFoundException( + "Unknown lifecycle phase \"" + lifecyclePhase + "\". You must specify a valid lifecycle phase" + + " or a goal in the format : or" + + " :[:]:. Available lifecycle phases are: " + + defaultLifeCycles.getLifecyclePhaseList() + ".", lifecyclePhase ); + } + + /* + * Initialize mapping from lifecycle phase to bound mojos. The key set of this map denotes the phases the caller + * is interested in, i.e. all phases up to and including the specified phase. + */ + + Map>> mappings = + new LinkedHashMap>>(); + + for ( String phase : lifecycle.getPhases() ) + { + Map> phaseBindings = new TreeMap>(); + + mappings.put( phase, phaseBindings ); + + if ( phase.equals( lifecyclePhase ) ) + { + break; + } + } + + /* + * Grab plugin executions that are bound to the selected lifecycle phases from project. The effective model of + * the project already contains the plugin executions induced by the project's packaging type. Remember, all + * phases of interest and only those are in the lifecyle mapping, if a phase has no value in the map, we are not + * interested in any of the executions bound to it. + */ + + for ( Plugin plugin : project.getBuild().getPlugins() ) + { + for ( PluginExecution execution : plugin.getExecutions() ) + { + // if the phase is specified then I don't have to go fetch the plugin yet and pull it down + // to examine the phase it is associated to. + if ( execution.getPhase() != null ) + { + Map> phaseBindings = mappings.get( execution.getPhase() ); + if ( phaseBindings != null ) + { + for ( String goal : execution.getGoals() ) + { + MojoExecution mojoExecution = new MojoExecution( plugin, goal, execution.getId() ); + mojoExecution.setLifecyclePhase( execution.getPhase() ); + addMojoExecution( phaseBindings, mojoExecution, execution.getPriority() ); + } + } + } + // if not then i need to grab the mojo descriptor and look at the phase that is specified + else + { + for ( String goal : execution.getGoals() ) + { + MojoDescriptor mojoDescriptor = pluginManager.getMojoDescriptor( plugin, goal, + DefaultRepositoryRequest.getRepositoryRequest( + session, project ) ); + + Map> phaseBindings = mappings.get( mojoDescriptor.getPhase() ); + if ( phaseBindings != null ) + { + MojoExecution mojoExecution = new MojoExecution( mojoDescriptor, execution.getId() ); + mojoExecution.setLifecyclePhase( mojoDescriptor.getPhase() ); + addMojoExecution( phaseBindings, mojoExecution, execution.getPriority() ); + } + } + } + } + } + + Map> lifecycleMappings = new LinkedHashMap>(); + + for ( Map.Entry>> entry : mappings.entrySet() ) + { + List mojoExecutions = new ArrayList(); + + for ( List executions : entry.getValue().values() ) + { + mojoExecutions.addAll( executions ); + } + + lifecycleMappings.put( entry.getKey(), mojoExecutions ); + } + + return lifecycleMappings; + } + + private void addMojoExecution( Map> phaseBindings, MojoExecution mojoExecution, + int priority ) + { + List mojoExecutions = phaseBindings.get( priority ); + + if ( mojoExecutions == null ) + { + mojoExecutions = new ArrayList(); + phaseBindings.put( priority, mojoExecutions ); + } + + mojoExecutions.add( mojoExecution ); + } + + private void populateMojoExecutionConfiguration( MavenProject project, MojoExecution mojoExecution, + boolean allowPluginLevelConfig ) + { + String g = mojoExecution.getGroupId(); + + String a = mojoExecution.getArtifactId(); + + Plugin plugin = findPlugin( g, a, project.getBuildPlugins() ); + + if ( plugin == null && project.getPluginManagement() != null ) + { + plugin = findPlugin( g, a, project.getPluginManagement().getPlugins() ); + } + + if ( plugin != null ) + { + PluginExecution pluginExecution = + findPluginExecution( mojoExecution.getExecutionId(), plugin.getExecutions() ); + + Xpp3Dom pomConfiguration = null; + + if ( pluginExecution != null ) + { + pomConfiguration = (Xpp3Dom) pluginExecution.getConfiguration(); + } + else if ( allowPluginLevelConfig ) + { + pomConfiguration = (Xpp3Dom) plugin.getConfiguration(); + } + + Xpp3Dom mojoConfiguration = ( pomConfiguration != null ) ? new Xpp3Dom( pomConfiguration ) : null; + + mojoConfiguration = Xpp3Dom.mergeXpp3Dom( mojoExecution.getConfiguration(), mojoConfiguration ); + + mojoExecution.setConfiguration( mojoConfiguration ); + } + } + + private Plugin findPlugin( String groupId, String artifactId, Collection plugins ) + { + for ( Plugin plugin : plugins ) + { + if ( artifactId.equals( plugin.getArtifactId() ) && groupId.equals( plugin.getGroupId() ) ) + { + return plugin; + } + } + + return null; + } + + private PluginExecution findPluginExecution( String executionId, Collection executions ) + { + if ( StringUtils.isNotEmpty( executionId ) ) + { + for ( PluginExecution execution : executions ) + { + if ( executionId.equals( execution.getId() ) ) + { + return execution; + } + } + } + + return null; + } + + /** + * Post-processes the effective configuration for the specified mojo execution. This step discards all parameters + * from the configuration that are not applicable to the mojo and injects the default values for any missing + * parameters. + * + * @param mojoExecution The mojo execution whose configuration should be finalized, must not be {@code null}. + */ + private void finalizeMojoConfiguration( MojoExecution mojoExecution ) + { + MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); + + Xpp3Dom executionConfiguration = mojoExecution.getConfiguration(); + if ( executionConfiguration == null ) + { + executionConfiguration = new Xpp3Dom( "configuration" ); + } + + Xpp3Dom defaultConfiguration = getMojoConfiguration( mojoDescriptor ); + + Xpp3Dom finalConfiguration = new Xpp3Dom( "configuration" ); + + if ( mojoDescriptor.getParameters() != null ) + { + for ( Parameter parameter : mojoDescriptor.getParameters() ) + { + Xpp3Dom parameterConfiguration = executionConfiguration.getChild( parameter.getName() ); + + if ( parameterConfiguration == null ) + { + parameterConfiguration = executionConfiguration.getChild( parameter.getAlias() ); + } + + Xpp3Dom parameterDefaults = defaultConfiguration.getChild( parameter.getName() ); + + parameterConfiguration = + Xpp3Dom.mergeXpp3Dom( parameterConfiguration, parameterDefaults, Boolean.TRUE ); + + if ( parameterConfiguration != null ) + { + parameterConfiguration = new Xpp3Dom( parameterConfiguration, parameter.getName() ); + + if ( StringUtils.isEmpty( parameterConfiguration.getAttribute( "implementation" ) ) && + StringUtils.isNotEmpty( parameter.getImplementation() ) ) + { + parameterConfiguration.setAttribute( "implementation", parameter.getImplementation() ); + } + + finalConfiguration.addChild( parameterConfiguration ); + } + } + } + + mojoExecution.setConfiguration( finalConfiguration ); + } + + private Xpp3Dom getMojoConfiguration( MojoDescriptor mojoDescriptor ) + { + return MojoDescriptorCreator.convert( mojoDescriptor ); + } + + private void calculateForkedExecutions( MojoExecution mojoExecution, MavenSession session, MavenProject project, + Collection alreadyForkedExecutions ) + throws MojoNotFoundException, PluginNotFoundException, PluginResolutionException, + PluginDescriptorParsingException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, + LifecyclePhaseNotFoundException, LifecycleNotFoundException, PluginVersionResolutionException + { + MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); + + if ( !mojoDescriptor.isForking() ) + { + return; + } + + if ( !alreadyForkedExecutions.add( mojoDescriptor ) ) + { + return; + } + + List forkedProjects = + LifecycleDependencyResolver.getProjects( project, session, mojoDescriptor.isAggregator() ); + + for ( MavenProject forkedProject : forkedProjects ) + { + List forkedExecutions; + + if ( StringUtils.isNotEmpty( mojoDescriptor.getExecutePhase() ) ) + { + forkedExecutions = + calculateForkedLifecycle( mojoExecution, session, forkedProject, alreadyForkedExecutions ); + } + else + { + forkedExecutions = + calculateForkedGoal( mojoExecution, session, forkedProject, alreadyForkedExecutions ); + } + + mojoExecution.setForkedExecutions( BuilderCommon.getKey( forkedProject ), forkedExecutions ); + } + + alreadyForkedExecutions.remove( mojoDescriptor ); + } + + private List calculateForkedLifecycle( MojoExecution mojoExecution, MavenSession session, + MavenProject project, + Collection alreadyForkedExecutions ) + throws MojoNotFoundException, PluginNotFoundException, PluginResolutionException, + PluginDescriptorParsingException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, + LifecyclePhaseNotFoundException, LifecycleNotFoundException, PluginVersionResolutionException + { + MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); + + String forkedPhase = mojoDescriptor.getExecutePhase(); + + Map> lifecycleMappings = + calculateLifecycleMappings( session, project, forkedPhase ); + + for ( List forkedExecutions : lifecycleMappings.values() ) + { + for ( MojoExecution forkedExecution : forkedExecutions ) + { + if ( forkedExecution.getMojoDescriptor() == null ) + { + MojoDescriptor forkedMojoDescriptor = + pluginManager.getMojoDescriptor( forkedExecution.getPlugin(), forkedExecution.getGoal(), + DefaultRepositoryRequest.getRepositoryRequest( session, + project ) ); + + forkedExecution.setMojoDescriptor( forkedMojoDescriptor ); + } + + populateMojoExecutionConfiguration( project, forkedExecution, false ); + } + } + + injectLifecycleOverlay( lifecycleMappings, mojoExecution, session, project ); + + List mojoExecutions = new ArrayList(); + + for ( List forkedExecutions : lifecycleMappings.values() ) + { + for ( MojoExecution forkedExecution : forkedExecutions ) + { + if ( !alreadyForkedExecutions.contains( forkedExecution.getMojoDescriptor() ) ) + { + finalizeMojoConfiguration( forkedExecution ); + + calculateForkedExecutions( forkedExecution, session, project, alreadyForkedExecutions ); + + mojoExecutions.add( forkedExecution ); + } + } + } + + return mojoExecutions; + } + + private void injectLifecycleOverlay( Map> lifecycleMappings, + MojoExecution mojoExecution, MavenSession session, MavenProject project ) + throws PluginDescriptorParsingException, LifecycleNotFoundException, MojoNotFoundException, + PluginNotFoundException, PluginResolutionException, NoPluginFoundForPrefixException, + InvalidPluginDescriptorException, PluginVersionResolutionException + { + MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); + + PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor(); + + String forkedLifecycle = mojoDescriptor.getExecuteLifecycle(); + + if ( StringUtils.isEmpty( forkedLifecycle ) ) + { + return; + } + + org.apache.maven.plugin.lifecycle.Lifecycle lifecycleOverlay; + + try + { + lifecycleOverlay = pluginDescriptor.getLifecycleMapping( forkedLifecycle ); + } + catch ( IOException e ) + { + throw new PluginDescriptorParsingException( pluginDescriptor.getPlugin(), pluginDescriptor.getSource(), e ); + } + catch ( XmlPullParserException e ) + { + throw new PluginDescriptorParsingException( pluginDescriptor.getPlugin(), pluginDescriptor.getSource(), e ); + } + + if ( lifecycleOverlay == null ) + { + throw new LifecycleNotFoundException( forkedLifecycle ); + } + + for ( Phase phase : lifecycleOverlay.getPhases() ) + { + List forkedExecutions = lifecycleMappings.get( phase.getId() ); + + if ( forkedExecutions != null ) + { + for ( Execution execution : phase.getExecutions() ) + { + for ( String goal : execution.getGoals() ) + { + MojoDescriptor forkedMojoDescriptor; + + if ( goal.indexOf( ':' ) < 0 ) + { + forkedMojoDescriptor = pluginDescriptor.getMojo( goal ); + if ( forkedMojoDescriptor == null ) + { + throw new MojoNotFoundException( goal, pluginDescriptor ); + } + } + else + { + forkedMojoDescriptor = mojoDescriptorCreator.getMojoDescriptor( goal, session, project ); + } + + MojoExecution forkedExecution = + new MojoExecution( forkedMojoDescriptor, mojoExecution.getExecutionId() ); + + Xpp3Dom forkedConfiguration = (Xpp3Dom) execution.getConfiguration(); + + forkedExecution.setConfiguration( forkedConfiguration ); + + populateMojoExecutionConfiguration( project, forkedExecution, true ); + + forkedExecutions.add( forkedExecution ); + } + } + + Xpp3Dom phaseConfiguration = (Xpp3Dom) phase.getConfiguration(); + + if ( phaseConfiguration != null ) + { + for ( MojoExecution forkedExecution : forkedExecutions ) + { + Xpp3Dom forkedConfiguration = forkedExecution.getConfiguration(); + + forkedConfiguration = Xpp3Dom.mergeXpp3Dom( phaseConfiguration, forkedConfiguration ); + + forkedExecution.setConfiguration( forkedConfiguration ); + } + } + } + } + } + // org.apache.maven.plugins:maven-remote-resources-plugin:1.0:process + //TODO: take repo mans into account as one may be aggregating prefixes of many + //TODO: collect at the root of the repository, read the one at the root, and fetch remote if something is missing + // or the user forces the issue + + private List calculateForkedGoal( MojoExecution mojoExecution, MavenSession session, + MavenProject project, + Collection alreadyForkedExecutions ) + throws MojoNotFoundException, PluginNotFoundException, PluginResolutionException, + PluginDescriptorParsingException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, + LifecyclePhaseNotFoundException, LifecycleNotFoundException, PluginVersionResolutionException + { + MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); + + PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor(); + + String forkedGoal = mojoDescriptor.getExecuteGoal(); + + MojoDescriptor forkedMojoDescriptor = pluginDescriptor.getMojo( forkedGoal ); + if ( forkedMojoDescriptor == null ) + { + throw new MojoNotFoundException( forkedGoal, pluginDescriptor ); + } + + if ( alreadyForkedExecutions.contains( forkedMojoDescriptor ) ) + { + return Collections.emptyList(); + } + + MojoExecution forkedExecution = new MojoExecution( forkedMojoDescriptor, forkedGoal ); + + populateMojoExecutionConfiguration( project, forkedExecution, true ); + + finalizeMojoConfiguration( forkedExecution ); + + calculateForkedExecutions( forkedExecution, session, project, alreadyForkedExecutions ); + + return Collections.singletonList( forkedExecution ); + } + + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java new file mode 100644 index 0000000000..ac7788915d --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java @@ -0,0 +1,96 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.BuildSuccess; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.DefaultLifecycleExecutor; +import org.apache.maven.lifecycle.LifecycleEventCatapult; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; + +/** + * Builds one or more lifecycles for a full module + * + * @author Benjamin Bentmann + * @author Jason van Zyl + * @author Kristian Rosenvold (extracted class) + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +@Component(role = LifecycleModuleBuilder.class) +public class LifecycleModuleBuilder +{ + @Requirement + private MojoExecutor mojoExecutor; + + @Requirement + private BuilderCommon builderCommon; + + public void buildProject( MavenSession session, ReactorContext reactorContext, MavenProject currentProject, + TaskSegment taskSegment ) + { + buildProject( session, session, reactorContext, currentProject, taskSegment ); + } + + public void buildProject( MavenSession session, MavenSession rootSession, ReactorContext reactorContext, + MavenProject currentProject, TaskSegment taskSegment ) + { + boolean isAggregating = taskSegment.isAggregating(); + + session.setCurrentProject( currentProject ); + + long buildStartTime = System.currentTimeMillis(); + + try + { + + if ( reactorContext.getReactorBuildStatus().isHaltedOrBlacklisted( currentProject ) ) + { + DefaultLifecycleExecutor.fireEvent( session, null, LifecycleEventCatapult.PROJECT_SKIPPED ); + return; + } + + DefaultLifecycleExecutor.fireEvent( session, null, LifecycleEventCatapult.PROJECT_STARTED ); + + BuilderCommon.attachToThread( currentProject ); + MavenExecutionPlan executionPlan = builderCommon.resolveBuildPlan( session, currentProject, taskSegment ); + + DependencyContext dependencyContext = new DependencyContext( executionPlan, isAggregating ); + mojoExecutor.execute( session, executionPlan.getMojoExecutions(), reactorContext.getProjectIndex(), + dependencyContext ); + + long buildEndTime = System.currentTimeMillis(); + + reactorContext.getResult().addBuildSummary( + new BuildSuccess( currentProject, buildEndTime - buildStartTime ) ); + + DefaultLifecycleExecutor.fireEvent( session, null, LifecycleEventCatapult.PROJECT_SUCCEEDED ); + } + catch ( Exception e ) + { + BuilderCommon.handleBuildError( reactorContext, rootSession, currentProject, e, buildStartTime ); + } + finally + { + session.setCurrentProject( null ); + + Thread.currentThread().setContextClassLoader( reactorContext.getOriginalContextClassLoader() ); + } + } +} \ No newline at end of file diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecyclePluginAnalyzerImpl.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecyclePluginAnalyzerImpl.java new file mode 100644 index 0000000000..b47074bcca --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecyclePluginAnalyzerImpl.java @@ -0,0 +1,161 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.lifecycle.DefaultLifecycles; +import org.apache.maven.lifecycle.LifeCyclePluginAnalyzer; +import org.apache.maven.lifecycle.Lifecycle; +import org.apache.maven.lifecycle.mapping.LifecycleMapping; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginExecution; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.logging.Logger; +import org.codehaus.plexus.util.StringUtils; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +/** + * @author Benjamin Bentmann + * @author Jason van Zyl + * @author jdcasey + * @author Kristian Rosenvold (extracted class only) + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +@Component(role = LifecyclePluginAnalyzerImpl.class) +public class LifecyclePluginAnalyzerImpl + implements LifeCyclePluginAnalyzer +{ + @Requirement + private Map lifecycleMappings; + + @Requirement + private DefaultLifecycles defaultLifeCycles; + + @Requirement + private Logger logger; + + + public LifecyclePluginAnalyzerImpl() + { + } + + // These methods deal with construction intact Plugin object that look like they come from a standard + // block in a Maven POM. We have to do some wiggling to pull the sources of information + // together and this really shows the problem of constructing a sensible default configuration but + // it's all encapsulated here so it appears normalized to the POM builder. + + // We are going to take the project packaging and find all plugin in the default lifecycle and create + // fully populated Plugin objects, including executions with goals and default configuration taken + // from the plugin.xml inside a plugin. + // + + public Set getPluginsBoundByDefaultToAllLifecycles( String packaging ) + { + if ( logger.isDebugEnabled() ) + { + logger.debug( "Looking up lifecyle mappings for packaging " + packaging + " from " + + Thread.currentThread().getContextClassLoader() ); + } + + LifecycleMapping lifecycleMappingForPackaging = lifecycleMappings.get( packaging ); + + if ( lifecycleMappingForPackaging == null ) + { + return null; + } + + Map plugins = new LinkedHashMap(); + + for ( Lifecycle lifecycle : defaultLifeCycles.getLifeCycles() ) + { + org.apache.maven.lifecycle.mapping.Lifecycle lifecycleConfiguration = + lifecycleMappingForPackaging.getLifecycles().get( lifecycle.getId() ); + + Map phaseToGoalMapping = null; + + if ( lifecycleConfiguration != null ) + { + phaseToGoalMapping = lifecycleConfiguration.getPhases(); + } + else if ( lifecycle.getDefaultPhases() != null ) + { + phaseToGoalMapping = lifecycle.getDefaultPhases(); + } + + if ( phaseToGoalMapping != null ) + { + // These are of the form: + // + // compile -> org.apache.maven.plugins:maven-compiler-plugin:compile[,gid:aid:goal,...] + // + for ( Map.Entry goalsForLifecyclePhase : phaseToGoalMapping.entrySet() ) + { + String phase = goalsForLifecyclePhase.getKey(); + String goals = goalsForLifecyclePhase.getValue(); + if ( goals != null ) + { + parseLifecyclePhaseDefinitions( plugins, phase, goals ); + } + } + } + } + + return plugins.keySet(); + } + + private void parseLifecyclePhaseDefinitions( Map plugins, String phase, String goals ) + { + String[] mojos = StringUtils.split( goals, "," ); + + for ( int i = 0; i < mojos.length; i++ ) + { + // either :: or ::: + String goal = mojos[i].trim(); + String[] p = StringUtils.split( goal, ":" ); + + PluginExecution execution = new PluginExecution(); + execution.setId( "default-" + p[p.length - 1] ); + execution.setPhase( phase ); + execution.setPriority( i - mojos.length ); + execution.getGoals().add( p[p.length - 1] ); + + Plugin plugin = new Plugin(); + plugin.setGroupId( p[0] ); + plugin.setArtifactId( p[1] ); + if ( p.length >= 4 ) + { + plugin.setVersion( p[2] ); + } + + Plugin existing = plugins.get( plugin ); + if ( existing != null ) + { + plugin = existing; + } + else + { + plugins.put( plugin, plugin ); + } + + plugin.getExecutions().add( execution ); + } + } + + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecyclePluginResolver.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecyclePluginResolver.java new file mode 100644 index 0000000000..8e2c02eb45 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecyclePluginResolver.java @@ -0,0 +1,86 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginManagement; +import org.apache.maven.plugin.version.DefaultPluginVersionRequest; +import org.apache.maven.plugin.version.PluginVersionRequest; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.plugin.version.PluginVersionResolver; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Benjamin Bentmann + * @author Kristian Rosenvold (Extract class) + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +@Component(role = LifecyclePluginResolver.class) +public class LifecyclePluginResolver +{ + @Requirement + private PluginVersionResolver pluginVersionResolver; + + + public LifecyclePluginResolver( PluginVersionResolver pluginVersionResolver ) + { + this.pluginVersionResolver = pluginVersionResolver; + } + + @SuppressWarnings({"UnusedDeclaration"}) + public LifecyclePluginResolver() + { + } + + public void resolveMissingPluginVersions( MavenProject project, MavenSession session ) + throws PluginVersionResolutionException + { + Map versions = new HashMap(); + + for ( Plugin plugin : project.getBuildPlugins() ) + { + if ( plugin.getVersion() == null ) + { + PluginVersionRequest request = new DefaultPluginVersionRequest( plugin, session ); + plugin.setVersion( pluginVersionResolver.resolve( request ).getVersion() ); + } + versions.put( plugin.getKey(), plugin.getVersion() ); + } + + PluginManagement pluginManagement = project.getPluginManagement(); + if ( pluginManagement != null ) + { + for ( Plugin plugin : pluginManagement.getPlugins() ) + { + if ( plugin.getVersion() == null ) + { + plugin.setVersion( versions.get( plugin.getKey() ) ); + if ( plugin.getVersion() == null ) + { + PluginVersionRequest request = new DefaultPluginVersionRequest( plugin, session ); + plugin.setVersion( pluginVersionResolver.resolve( request ).getVersion() ); + } + } + } + } + } +} \ No newline at end of file diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleTask.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleTask.java new file mode 100644 index 0000000000..2d8cd23031 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleTask.java @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal; + +/** + * A task that is a lifecycle + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + * + * @author Benjamin Bentmann + */ +public final class LifecycleTask +{ + + private final String lifecyclePhase; + + public LifecycleTask( String lifecyclePhase ) + { + this.lifecyclePhase = lifecyclePhase; + } + + @Override + public String toString() + { + return getLifecyclePhase(); + } + + public String getLifecyclePhase() + { + return lifecyclePhase; + } +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleTaskSegmentCalculator.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleTaskSegmentCalculator.java new file mode 100644 index 0000000000..52b49d6836 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleTaskSegmentCalculator.java @@ -0,0 +1,44 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.*; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; + +import java.util.List; + +/** + * Calculates the task segments in the build + * + * @author Benjamin Bentmann + * @author Jason van Zyl + * @author jdcasey + * @author Kristian Rosenvold (extracted interface) + *

+ * NOTE: This interface is not part of any public api and can be changed or deleted without prior notice. + */ + +public interface LifecycleTaskSegmentCalculator +{ + List calculateTaskSegments( MavenSession session, List tasks ) + throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, + MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, + PluginVersionResolutionException; + + public boolean requiresProject( MavenSession session ); + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleTaskSegmentCalculatorImpl.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleTaskSegmentCalculatorImpl.java new file mode 100644 index 0000000000..9bcd9aa3eb --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleTaskSegmentCalculatorImpl.java @@ -0,0 +1,122 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.*; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; + +import java.util.ArrayList; +import java.util.List; + +/** + * Calculates the task segments in the build + * + * @author Benjamin Bentmann + * @author Jason van Zyl + * @author jdcasey + * @author Kristian Rosenvold (extracted class) + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ + +@Component(role = LifecycleTaskSegmentCalculator.class) +public class LifecycleTaskSegmentCalculatorImpl + implements LifecycleTaskSegmentCalculator +{ + @Requirement + private MojoDescriptorCreator mojoDescriptorCreator; + + @Requirement + private LifecyclePluginResolver lifecyclePluginResolver; + + public LifecycleTaskSegmentCalculatorImpl() + { + } + + public List calculateTaskSegments( MavenSession session, List tasks ) + throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, + MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, + PluginVersionResolutionException + { + List taskSegments = new ArrayList( tasks.size() ); + + TaskSegment currentSegment = null; + + for ( String task : tasks ) + { + if ( isGoalSpecification( task ) ) + { + // "pluginPrefix:goal" or "groupId:artifactId[:version]:goal" + + lifecyclePluginResolver.resolveMissingPluginVersions( session.getTopLevelProject(), session ); + + MojoDescriptor mojoDescriptor = + mojoDescriptorCreator.getMojoDescriptor( task, session, session.getTopLevelProject() ); + + boolean aggregating = mojoDescriptor.isAggregator() || !mojoDescriptor.isProjectRequired(); + + if ( currentSegment == null || currentSegment.isAggregating() != aggregating ) + { + currentSegment = new TaskSegment( aggregating ); + taskSegments.add( currentSegment ); + } + + currentSegment.getTasks().add( new GoalTask( task ) ); + } + else + { + // lifecycle phase + + if ( currentSegment == null || currentSegment.isAggregating() ) + { + currentSegment = new TaskSegment( false ); + taskSegments.add( currentSegment ); + } + + currentSegment.getTasks().add( new LifecycleTask( task ) ); + } + } + + return taskSegments; + } + + public boolean requiresProject( MavenSession session ) + { + List goals = session.getGoals(); + if ( goals != null ) + { + for ( String goal : goals ) + { + if ( !isGoalSpecification( goal ) ) + { + return true; + } + } + } + return false; + } + + + private boolean isGoalSpecification( String task ) + { + return task.indexOf( ':' ) >= 0; + } + +} \ No newline at end of file diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleThreadedBuilder.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleThreadedBuilder.java new file mode 100644 index 0000000000..8f0b407b56 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleThreadedBuilder.java @@ -0,0 +1,173 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.logging.Logger; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * Builds the full lifecycle in weave-mode (phase by phase as opposed to project-by-project) + * + * @author Kristian Rosenvold + * Builds one or more lifecycles for a full module + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +@Component(role = LifecycleThreadedBuilder.class) +public class LifecycleThreadedBuilder +{ + + + @Requirement + private Logger logger; + + @Requirement + private LifecycleModuleBuilder lifecycleModuleBuilder; + + + @SuppressWarnings({"UnusedDeclaration"}) + public LifecycleThreadedBuilder() + { + } + + public void build( MavenSession session, ReactorContext reactorContext, ProjectBuildList projectBuilds, + List currentTaskSegment, ConcurrencyDependencyGraph analyzer, + CompletionService service ) + { + + // Currently disabled + ThreadOutputMuxer muxer = null; // new ThreadOutputMuxer( analyzer.getProjectBuilds(), System.out ); + + for ( TaskSegment taskSegment : currentTaskSegment ) + { + Map projectBuildMap = projectBuilds.selectSegment( taskSegment ); + try + { + multiThreadedProjectTaskSegmentBuild( analyzer, reactorContext, session, service, taskSegment, projectBuildMap, muxer ); + if ( reactorContext.getReactorBuildStatus().isHalted() ) + { + break; + } + + if ( reactorContext.getReactorBuildStatus().isHalted() ) + { + break; + } + } + catch ( Exception e ) + { + break; // Why are we just ignoring this exception? Are exceptions are being used for flow control + } + + } + } + + private void multiThreadedProjectTaskSegmentBuild( ConcurrencyDependencyGraph analyzer, + ReactorContext reactorContext, MavenSession rootSession, + CompletionService service, + TaskSegment taskSegment, Map projectBuildList, + ThreadOutputMuxer muxer ) + { + + // schedule independent projects + for ( MavenProject mavenProject : analyzer.getRootSchedulableBuilds() ) + { + ProjectSegment projectSegment = projectBuildList.get( mavenProject ); + logger.debug( "Scheduling: " + projectSegment.getProject() ); + Callable cb = + createBuildCallable( rootSession, projectSegment, reactorContext, taskSegment, muxer ); + service.submit( cb ); + } + + // for each finished project + for ( int i = 0; i < analyzer.getNumberOfBuilds(); i++ ) + { + try + { + ProjectSegment projectBuild = service.take().get(); + if ( reactorContext.getReactorBuildStatus().isHalted() ) + { + break; + } + final List newItemsThatCanBeBuilt = + analyzer.markAsFinished( projectBuild.getProject() ); + for ( MavenProject mavenProject : newItemsThatCanBeBuilt ) + { + ProjectSegment scheduledDependent = projectBuildList.get( mavenProject ); + logger.debug( "Scheduling: " + scheduledDependent ); + Callable cb = + createBuildCallable( rootSession, scheduledDependent, reactorContext, taskSegment, muxer ); + service.submit( cb ); + } + } + catch ( InterruptedException e ) + { + break; + } + catch ( ExecutionException e ) + { + break; + } + } + + // cancel outstanding builds (if any) - this can happen if an exception is thrown in above block + + Future unprocessed; + while ( ( unprocessed = service.poll() ) != null ) + { + try + { + unprocessed.get(); + } + catch ( InterruptedException e ) + { + throw new RuntimeException( e ); + } + catch ( ExecutionException e ) + { + throw new RuntimeException( e ); + } + } + } + + private Callable createBuildCallable( final MavenSession rootSession, + final ProjectSegment projectBuild, + final ReactorContext reactorContext, + final TaskSegment taskSegment, final ThreadOutputMuxer muxer ) + { + return new Callable() + { + public ProjectSegment call() + { + // muxer.associateThreadWithProjectSegment( projectBuild ); + lifecycleModuleBuilder.buildProject( projectBuild.getSession(), rootSession, reactorContext, + projectBuild.getProject(), taskSegment ); + // muxer.setThisModuleComplete( projectBuild ); + + return projectBuild; + } + }; + } +} \ No newline at end of file diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleWeaveBuilder.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleWeaveBuilder.java new file mode 100644 index 0000000000..aceecb8ba0 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleWeaveBuilder.java @@ -0,0 +1,306 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.BuildSuccess; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.*; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.logging.Logger; + +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * Builds the full lifecycle in weave-mode (phase by phase as opposed to project-by-project) + *

+ * NOTE: Weave mode is still experimental. It may be either promoted to first class citizen + * at some later point in time, and it may also be removed entirely. Weave mode has much more aggressive + * concurrency behaviour than regular threaded mode, and as such is still under test wrt cross platform stability. + *

+ * To remove weave mode from m3, the following should be removed: + * ExecutionPlanItem.schedule w/setters and getters + * DefaultLifeCycles.getScheduling() and all its use + * ReactorArtifactRepository has a reference to isWeave too. + * This class and its usage + * + * @author Kristian Rosenvold + * Builds one or more lifecycles for a full module + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +@Component(role = LifecycleWeaveBuilder.class) +public class LifecycleWeaveBuilder +{ + @Requirement + private MojoExecutor mojoExecutor; + + @Requirement + private BuilderCommon builderCommon; + + @Requirement + private Logger logger; + + @Requirement + private LifecycleDependencyResolver lifecycleDependencyResolver; + + + private final Map executionPlans = + Collections.synchronizedMap( new HashMap() ); + + + @SuppressWarnings({"UnusedDeclaration"}) + public LifecycleWeaveBuilder() + { + } + + public LifecycleWeaveBuilder( MojoExecutor mojoExecutor, BuilderCommon builderCommon, Logger logger, + LifecycleDependencyResolver lifecycleDependencyResolver ) + { + this.mojoExecutor = mojoExecutor; + this.builderCommon = builderCommon; + this.logger = logger; + this.lifecycleDependencyResolver = lifecycleDependencyResolver; + } + + public void build( ProjectBuildList projectBuilds, ReactorContext buildContext, List taskSegments, + MavenSession session, CompletionService service, ReactorBuildStatus reactorBuildStatus ) + throws ExecutionException, InterruptedException + { + ConcurrentBuildLogger concurrentBuildLogger = new ConcurrentBuildLogger(); + try + { + final List> futures = new ArrayList>(); + + for ( TaskSegment taskSegment : taskSegments ) + { + ProjectBuildList segmentChunks = projectBuilds.getByTaskSegment( taskSegment ); + ThreadOutputMuxer muxer = null; // new ThreadOutputMuxer( segmentChunks, System.out ); + for ( ProjectSegment projectBuild : segmentChunks ) + { + try + { + MavenExecutionPlan executionPlan = + builderCommon.resolveBuildPlan( projectBuild.getSession(), projectBuild.getProject(), + projectBuild.getTaskSegment() ); + executionPlans.put( projectBuild.getProject(), executionPlan ); + DependencyContext dependencyContext = + new DependencyContext( executionPlan, projectBuild.getTaskSegment().isAggregating() ); + + final Callable projectBuilder = + createCallableForBuildingOneFullModule( buildContext, session, reactorBuildStatus, + executionPlan, projectBuild, muxer, + dependencyContext, concurrentBuildLogger, + projectBuilds ); + + futures.add( service.submit( projectBuilder ) ); + } + catch ( Exception e ) + { + throw new ExecutionException( e ); + } + } + + for ( Future buildFuture : futures ) + { + buildFuture.get(); // At this point, this build *is* finished. + // Do not leak threads past here or evil gremlins will get you! + } + futures.clear(); + } + } + finally + { + projectBuilds.closeAll(); + } + logger.info( concurrentBuildLogger.toString() ); + } + + private Callable createCallableForBuildingOneFullModule( final ReactorContext reactorContext, + final MavenSession rootSession, + final ReactorBuildStatus reactorBuildStatus, + final MavenExecutionPlan executionPlan, + final ProjectSegment projectBuild, + final ThreadOutputMuxer muxer, + final DependencyContext dependencyContext, + final ConcurrentBuildLogger concurrentBuildLogger, + final ProjectBuildList projectBuilds ) + { + return new Callable() + { + public ProjectSegment call() + throws Exception + { + Iterator planItems = executionPlan.iterator(); + ExecutionPlanItem current = planItems.hasNext() ? planItems.next() : null; + long buildStartTime = System.currentTimeMillis(); + + //muxer.associateThreadWithProjectSegment( projectBuild ); + + if ( reactorBuildStatus.isHaltedOrBlacklisted( projectBuild.getProject() ) ) + { + DefaultLifecycleExecutor.fireEvent( projectBuild.getSession(), null, + LifecycleEventCatapult.PROJECT_SKIPPED ); + return null; + } + + DefaultLifecycleExecutor.fireEvent( projectBuild.getSession(), null, + LifecycleEventCatapult.PROJECT_STARTED ); + + boolean packagePhaseSeen = false; + boolean runBAbyRun = false; + try + { + while ( current != null && !reactorBuildStatus.isHalted() && + !reactorBuildStatus.isBlackListed( projectBuild.getProject() ) ) + { + final String phase = current.getMojoExecution().getMojoDescriptor().getPhase(); + PhaseRecorder phaseRecorder = new PhaseRecorder( projectBuild.getProject() ); + + if ( !packagePhaseSeen && phase != null && phase.equals( "package" ) ) + { + // Re-resolve. A bit of a kludge ATM + packagePhaseSeen = true; + lifecycleDependencyResolver.reResolveReactorArtifacts( projectBuilds, false, + projectBuild.getProject(), + projectBuild.getSession(), + executionPlan ); + + } + + BuiltLogItem builtLogItem = + concurrentBuildLogger.createBuildLogItem( projectBuild.getProject(), current ); + final Schedule schedule = current.getSchedule(); + + if ( schedule != null && schedule.isMojoSynchronized() ) + { + synchronized ( current.getPlugin() ) + { + buildExecutionPlanItem( reactorContext, current, projectBuild, dependencyContext, + phaseRecorder ); + } + } + else + { + buildExecutionPlanItem( reactorContext, current, projectBuild, dependencyContext, + phaseRecorder ); + } + + current.setComplete(); + builtLogItem.setComplete(); + + ExecutionPlanItem next = planItems.hasNext() ? planItems.next() : null; + if ( next != null ) + { + final Schedule scheduleOfNext = next.getSchedule(); + if ( !runBAbyRun && ( scheduleOfNext == null || !scheduleOfNext.isParallel() ) ) + { + for ( MavenProject upstreamProject : projectBuild.getImmediateUpstreamProjects() ) + { + final MavenExecutionPlan upstreamPlan = executionPlans.get( upstreamProject ); + final ExecutionPlanItem inSchedule = upstreamPlan.findLastInPhase( next ); + if ( inSchedule != null ) + { + long startWait = System.currentTimeMillis(); + inSchedule.waitUntilDone(); + builtLogItem.addWait( upstreamProject, inSchedule, startWait ); + } + } + } + } + current = next; + + if ( packagePhaseSeen && !runBAbyRun ) + { + runBAbyRun = true; + } + } + + final long wallClockTime = System.currentTimeMillis() - buildStartTime; + final BuildSuccess summary = + new BuildSuccess( projectBuild.getProject(), wallClockTime ); // - waitingTime + reactorContext.getResult().addBuildSummary( summary ); + DefaultLifecycleExecutor.fireEvent( projectBuild.getSession(), null, + LifecycleEventCatapult.PROJECT_SUCCEEDED ); + + } + catch ( Exception e ) + { + BuilderCommon.handleBuildError( reactorContext, rootSession, projectBuild.getProject(), e, + buildStartTime ); + } + finally + { + if ( current != null ) + { + executionPlan.forceAllComplete(); + } + // muxer.setThisModuleComplete( projectBuild ); + } + return null; + } + }; + } + + private void buildExecutionPlanItem( ReactorContext reactorContext, ExecutionPlanItem node, + ProjectSegment projectBuild, DependencyContext dependencyContext, + PhaseRecorder phaseRecorder ) + throws LifecycleExecutionException + { + + MavenProject currentProject = projectBuild.getProject(); + + long buildStartTime = System.currentTimeMillis(); + + MavenSession sessionForThisModule = projectBuild.getSession(); + try + { + + if ( reactorContext.getReactorBuildStatus().isHaltedOrBlacklisted( currentProject ) ) + { + return; + } + + BuilderCommon.attachToThread( currentProject ); + + mojoExecutor.execute( sessionForThisModule, node.getMojoExecution(), reactorContext.getProjectIndex(), + dependencyContext, phaseRecorder ); + + final BuildSuccess summary = + new BuildSuccess( currentProject, System.currentTimeMillis() - buildStartTime ); + reactorContext.getResult().addBuildSummary( summary ); + } + finally + { + Thread.currentThread().setContextClassLoader( reactorContext.getOriginalContextClassLoader() ); + } + } + + public static boolean isWeaveMode( MavenExecutionRequest request ) + { + return "true".equals( request.getUserProperties().getProperty( "maven3.weaveMode" ) ); + } + + public static void setWeaveMode( Properties properties ) + { + properties.setProperty( "maven3.weaveMode", "true" ); + } +} \ No newline at end of file diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoDescriptorCreator.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoDescriptorCreator.java new file mode 100644 index 0000000000..887ecfb50d --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoDescriptorCreator.java @@ -0,0 +1,256 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.artifact.repository.DefaultRepositoryRequest; +import org.apache.maven.artifact.repository.RepositoryRequest; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Plugin; +import org.apache.maven.plugin.*; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.prefix.*; +import org.apache.maven.plugin.version.DefaultPluginVersionRequest; +import org.apache.maven.plugin.version.PluginVersionRequest; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.plugin.version.PluginVersionResolver; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.configuration.PlexusConfiguration; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.StringTokenizer; + +/** + * Resolves dependencies for the artifacts in context of the lifecycle build + * + * @author Benjamin Bentmann + * @author Jason van Zyl + * @author jdcasey + * @author Kristian Rosenvold (extracted class only) + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ + +@Component(role = MojoDescriptorCreator.class) +public class MojoDescriptorCreator +{ + @Requirement + private PluginVersionResolver pluginVersionResolver; + + @Requirement + private BuildPluginManager pluginManager; + + @Requirement + private PluginPrefixResolver pluginPrefixResolver; + + @SuppressWarnings({"UnusedDeclaration"}) + public MojoDescriptorCreator() + { + } + + public MojoDescriptorCreator( PluginVersionResolver pluginVersionResolver, BuildPluginManager pluginManager, + PluginPrefixResolver pluginPrefixResolver ) + { + this.pluginVersionResolver = pluginVersionResolver; + this.pluginManager = pluginManager; + this.pluginPrefixResolver = pluginPrefixResolver; + } + + private Plugin findPlugin( String groupId, String artifactId, Collection plugins ) + { + for ( Plugin plugin : plugins ) + { + if ( artifactId.equals( plugin.getArtifactId() ) && groupId.equals( plugin.getGroupId() ) ) + { + return plugin; + } + } + + return null; + } + + public static Xpp3Dom convert( MojoDescriptor mojoDescriptor ) + { + Xpp3Dom dom = new Xpp3Dom( "configuration" ); + + PlexusConfiguration c = mojoDescriptor.getMojoConfiguration(); + + PlexusConfiguration[] ces = c.getChildren(); + + if ( ces != null ) + { + for ( PlexusConfiguration ce : ces ) + { + String value = ce.getValue( null ); + String defaultValue = ce.getAttribute( "default-value", null ); + if ( value != null || defaultValue != null ) + { + Xpp3Dom e = new Xpp3Dom( ce.getName() ); + e.setValue( value ); + if ( defaultValue != null ) + { + e.setAttribute( "default-value", defaultValue ); + } + dom.addChild( e ); + } + } + } + + return dom; + } + + // org.apache.maven.plugins:maven-remote-resources-plugin:1.0:process + + public MojoDescriptor getMojoDescriptor( String task, MavenSession session, MavenProject project ) + throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, + MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, + PluginVersionResolutionException + { + String goal = null; + + Plugin plugin = null; + + StringTokenizer tok = new StringTokenizer( task, ":" ); + + int numTokens = tok.countTokens(); + + if ( numTokens == 4 ) + { + // We have everything that we need + // + // org.apache.maven.plugins:maven-remote-resources-plugin:1.0:process + // + // groupId + // artifactId + // version + // goal + // + plugin = new Plugin(); + plugin.setGroupId( tok.nextToken() ); + plugin.setArtifactId( tok.nextToken() ); + plugin.setVersion( tok.nextToken() ); + goal = tok.nextToken(); + + } + else if ( numTokens == 3 ) + { + // We have everything that we need except the version + // + // org.apache.maven.plugins:maven-remote-resources-plugin:???:process + // + // groupId + // artifactId + // ??? + // goal + // + plugin = new Plugin(); + plugin.setGroupId( tok.nextToken() ); + plugin.setArtifactId( tok.nextToken() ); + goal = tok.nextToken(); + } + else if ( numTokens == 2 ) + { + // We have a prefix and goal + // + // idea:idea + // + String prefix = tok.nextToken(); + goal = tok.nextToken(); + + // This is the case where someone has executed a single goal from the command line + // of the form: + // + // mvn remote-resources:process + // + // From the metadata stored on the server which has been created as part of a standard + // Maven plugin deployment we will find the right PluginDescriptor from the remote + // repository. + + plugin = findPluginForPrefix( prefix, session ); + } + + injectPluginDeclarationFromProject( plugin, project ); + + RepositoryRequest repositoryRequest = DefaultRepositoryRequest.getRepositoryRequest( session, project ); + + // If there is no version to be found then we need to look in the repository metadata for + // this plugin and see what's specified as the latest release. + // + if ( plugin.getVersion() == null ) + { + resolvePluginVersion( plugin, repositoryRequest ); + } + + return pluginManager.getMojoDescriptor( plugin, goal, repositoryRequest ); + } + //TODO: take repo mans into account as one may be aggregating prefixes of many + //TODO: collect at the root of the repository, read the one at the root, and fetch remote if something is missing + // or the user forces the issue + + public Plugin findPluginForPrefix( String prefix, MavenSession session ) + throws NoPluginFoundForPrefixException + { + // [prefix]:[goal] + + PluginPrefixRequest prefixRequest = new DefaultPluginPrefixRequest( prefix, session ); + PluginPrefixResult prefixResult = pluginPrefixResolver.resolve( prefixRequest ); + + Plugin plugin = new Plugin(); + plugin.setGroupId( prefixResult.getGroupId() ); + plugin.setArtifactId( prefixResult.getArtifactId() ); + + return plugin; + } + + private void resolvePluginVersion( Plugin plugin, RepositoryRequest repositoryRequest ) + throws PluginVersionResolutionException + { + PluginVersionRequest versionRequest = new DefaultPluginVersionRequest( plugin, repositoryRequest ); + plugin.setVersion( pluginVersionResolver.resolve( versionRequest ).getVersion() ); + } + + + private void injectPluginDeclarationFromProject( Plugin plugin, MavenProject project ) + { + Plugin pluginInPom = findPlugin( plugin, project.getBuildPlugins() ); + + if ( pluginInPom == null && project.getPluginManagement() != null ) + { + pluginInPom = findPlugin( plugin, project.getPluginManagement().getPlugins() ); + } + + if ( pluginInPom != null ) + { + if ( plugin.getVersion() == null ) + { + plugin.setVersion( pluginInPom.getVersion() ); + } + + plugin.setDependencies( new ArrayList( pluginInPom.getDependencies() ) ); + } + } + + private Plugin findPlugin( Plugin plugin, Collection plugins ) + { + return findPlugin( plugin.getGroupId(), plugin.getArtifactId(), plugins ); + } + + +} + diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java new file mode 100644 index 0000000000..6b3f013d7e --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java @@ -0,0 +1,258 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.DefaultLifecycleExecutor; +import org.apache.maven.lifecycle.LifecycleEventCatapult; +import org.apache.maven.lifecycle.LifecycleExecutionException; +import org.apache.maven.lifecycle.MissingProjectException; +import org.apache.maven.plugin.*; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.util.StringUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Executes an individual mojo + * + * @author Jason van Zyl + * @author Benjamin Bentmann + * @author Kristian Rosenvold + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +@Component(role = MojoExecutor.class) +public class MojoExecutor +{ + + @Requirement + private BuildPluginManager pluginManager; + + @Requirement + private LifecycleDependencyResolver lifeCycleDependencyResolver; + + public MojoExecutor() + { + } + + public void execute( MavenSession session, List mojoExecutions, ProjectIndex projectIndex, + DependencyContext dependencyContext ) + throws LifecycleExecutionException + + { + PhaseRecorder phaseRecorder = new PhaseRecorder( session.getCurrentProject() ); + for ( MojoExecution mojoExecution : mojoExecutions ) + { + execute( session, mojoExecution, projectIndex, dependencyContext, phaseRecorder ); + } + + } + + public void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex, + DependencyContext dependencyContext, PhaseRecorder phaseRecorder ) + throws LifecycleExecutionException + { + execute( session, mojoExecution, projectIndex, dependencyContext ); + phaseRecorder.observeExecution( mojoExecution ); + } + + @SuppressWarnings({"ThrowableInstanceNeverThrown"}) + private void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex, + DependencyContext dependencyContext ) + throws LifecycleExecutionException + { + MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); + + if ( mojoDescriptor.isProjectRequired() && !session.isUsingPOMsFromFilesystem() ) + { + Throwable cause = new MissingProjectException( + "Goal requires a project to execute" + " but there is no POM in this directory (" + + session.getExecutionRootDirectory() + ")." + + " Please verify you invoked Maven from the correct directory." ); + throw new LifecycleExecutionException( mojoExecution, null, cause ); + } + + if ( mojoDescriptor.isOnlineRequired() && session.isOffline() ) + { + if ( MojoExecution.Source.CLI.equals( mojoExecution.getSource() ) ) + { + Throwable cause = new IllegalStateException( + "Goal requires online mode for execution" + " but Maven is currently offline." ); + throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), cause ); + } + else + { + DefaultLifecycleExecutor.fireEvent( session, mojoExecution, LifecycleEventCatapult.MOJO_SKIPPED ); + + return; + } + } + + lifeCycleDependencyResolver.checkForUpdate( session, dependencyContext ); + + List forkedProjects = + executeForkedExecutions( mojoExecution, session, projectIndex, dependencyContext ); + + DefaultLifecycleExecutor.fireEvent( session, mojoExecution, LifecycleEventCatapult.MOJO_STARTED ); + + ArtifactFilter artifactFilter = getArtifactFilter( mojoDescriptor ); + List resolvedProjects = + LifecycleDependencyResolver.getProjects( session.getCurrentProject(), session, + mojoDescriptor.isAggregator() ); + for ( MavenProject project : resolvedProjects ) + { + project.setArtifactFilter( artifactFilter ); + } + + try + { + try + { + pluginManager.executeMojo( session, mojoExecution ); + + DefaultLifecycleExecutor.fireEvent( session, mojoExecution, LifecycleEventCatapult.MOJO_SUCCEEDED ); + } + catch ( MojoFailureException e ) + { + throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); + } + catch ( MojoExecutionException e ) + { + throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); + } + catch ( PluginConfigurationException e ) + { + throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); + } + catch ( PluginManagerException e ) + { + throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); + } + + DefaultLifecycleExecutor.fireEvent( session, mojoExecution, LifecycleEventCatapult.MOJO_SUCCEEDED ); + } + catch ( LifecycleExecutionException e ) + { + DefaultLifecycleExecutor.fireEvent( session, mojoExecution, LifecycleEventCatapult.MOJO_FAILED ); + + throw e; + } + finally + { + for ( MavenProject forkedProject : forkedProjects ) + { + forkedProject.setExecutionProject( null ); + } + } + } + + private ArtifactFilter getArtifactFilter( MojoDescriptor mojoDescriptor ) + { + String scopeToResolve = mojoDescriptor.getDependencyResolutionRequired(); + String scopeToCollect = mojoDescriptor.getDependencyCollectionRequired(); + + List scopes = new ArrayList( 2 ); + if ( StringUtils.isNotEmpty( scopeToCollect ) ) + { + scopes.add( scopeToCollect ); + } + if ( StringUtils.isNotEmpty( scopeToResolve ) ) + { + scopes.add( scopeToResolve ); + } + + if ( scopes.isEmpty() ) + { + return null; + } + else + { + return new CumulativeScopeArtifactFilter( scopes ); + } + } + + private List executeForkedExecutions( MojoExecution mojoExecution, MavenSession session, + ProjectIndex projectIndex, DependencyContext dependencyContext ) + throws LifecycleExecutionException + { + List forkedProjects = Collections.emptyList(); + + Map> forkedExecutions = mojoExecution.getForkedExecutions(); + + if ( !forkedExecutions.isEmpty() ) + { + DefaultLifecycleExecutor.fireEvent( session, mojoExecution, LifecycleEventCatapult.FORK_STARTED ); + + MavenProject project = session.getCurrentProject(); + + forkedProjects = new ArrayList( forkedExecutions.size() ); + + dependencyContext = dependencyContext.clone(); + + try + { + for ( Map.Entry> fork : forkedExecutions.entrySet() ) + { + int index = projectIndex.getIndices().get( fork.getKey() ); + + MavenProject forkedProject = projectIndex.getProjects().get( fork.getKey() ); + + forkedProjects.add( forkedProject ); + + MavenProject executedProject = forkedProject.clone(); + + forkedProject.setExecutionProject( executedProject ); + + try + { + session.setCurrentProject( executedProject ); + session.getProjects().set( index, executedProject ); + projectIndex.getProjects().put( fork.getKey(), executedProject ); + + execute( session, fork.getValue(), projectIndex, dependencyContext ); + + } + finally + { + projectIndex.getProjects().put( fork.getKey(), forkedProject ); + session.getProjects().set( index, forkedProject ); + session.setCurrentProject( project ); + } + } + + DefaultLifecycleExecutor.fireEvent( session, mojoExecution, LifecycleEventCatapult.FORK_SUCCEEDED ); + } + catch ( LifecycleExecutionException e ) + { + DefaultLifecycleExecutor.fireEvent( session, mojoExecution, LifecycleEventCatapult.FORK_FAILED ); + + throw e; + } + } + + return forkedProjects; + } + + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/PhaseRecorder.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/PhaseRecorder.java new file mode 100644 index 0000000000..21a7f791a5 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/PhaseRecorder.java @@ -0,0 +1,60 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.project.MavenProject; + +/** + * @author Benjamin Bentmann + * @author Kristian Rosenvold (extrace class) + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +public class PhaseRecorder +{ + private String lastLifecyclePhase; + + private final MavenProject project; + + public PhaseRecorder( MavenProject project ) + { + this.project = project; + } + + public void observeExecution( MojoExecution mojoExecution ) + { + String lifecyclePhase = mojoExecution.getLifecyclePhase(); + + if ( lifecyclePhase != null ) + { + if ( lastLifecyclePhase == null ) + { + lastLifecyclePhase = lifecyclePhase; + } + else if ( !lifecyclePhase.equals( lastLifecyclePhase ) ) + { + project.addLifecyclePhase( lastLifecyclePhase ); + lastLifecyclePhase = lifecyclePhase; + } + } + + if ( lastLifecyclePhase != null ) + { + project.addLifecyclePhase( lastLifecyclePhase ); + } + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ProjectBuildList.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ProjectBuildList.java new file mode 100644 index 0000000000..19c6ebe9b4 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ProjectBuildList.java @@ -0,0 +1,131 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.artifact.ArtifactUtils; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; + +import java.util.*; + +/** + * A list of project segments, ordered so that all ProjectSegments from first TaskSegment come before any + * subsequent TaskSegments. + * + * @author Kristian Rosenvold + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +public class ProjectBuildList + implements Iterable +{ + private final List items; + + + + + public ProjectBuildList( List items ) + { + this.items = Collections.unmodifiableList( items ); + } + + // TODO: Optimize; or maybe just rewrite the whole way aggregating mojos are being run. + /** + * Returns aProjectBuildList that contains only items for the specified taskSegment + * @param taskSegment the requested tasksegment + * @return a project build list for the supplied task segment + */ + public ProjectBuildList getByTaskSegment(TaskSegment taskSegment) + { + List currentSegment = new ArrayList(); + for ( ProjectSegment projectBuild : items ) + { + if ( taskSegment == projectBuild.getTaskSegment()){ // NOTE: There's no notion of taskSegment equality. + currentSegment.add( projectBuild ); + } + } + return new ProjectBuildList( currentSegment ); + } + + public Map selectSegment(TaskSegment taskSegment) + { + Map result = new HashMap( ); + for ( ProjectSegment projectBuild : items ) + { + if ( taskSegment == projectBuild.getTaskSegment()){ // NOTE: There's no notion of taskSegment equality. + result.put( projectBuild.getProject(), projectBuild ); + } + } + return result; + } + + /** + * Finds the first ProjectSegment matching the supplied project + * @param mavenProject the requested project + * @return The projectSegment or null. + */ + public ProjectSegment findByMavenProject(MavenProject mavenProject) + { + for ( ProjectSegment projectBuild : items ) + { + if ( mavenProject.equals( projectBuild.getProject() )){ + return projectBuild; + } + } + return null; + } + + public Iterator iterator() + { + return items.iterator(); + } + + public void closeAll() + { + for ( ProjectSegment item : items ) + { + MavenSession sessionForThisModule = item.getSession(); + sessionForThisModule.setCurrentProject( null ); + } + } + + public int size() + { + return items.size(); + } + + ProjectSegment get( int index ) + { + return items.get( index ); + } + + public Set getReactorProjectKeys() + { + Set projectKeys = new HashSet( items.size() * 2 ); + for ( ProjectSegment projectBuild : items ) + { + MavenProject project = projectBuild.getProject(); + String key = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() ); + projectKeys.add( key ); + } + return projectKeys; + } + + + public boolean isEmpty() + { + return items.isEmpty(); + } +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ProjectIndex.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ProjectIndex.java new file mode 100644 index 0000000000..ab63de4e43 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ProjectIndex.java @@ -0,0 +1,63 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.project.MavenProject; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Provides the positional index of the project + * + * @author Benjamin Bentmann + * @author Kristian Rosenvold (extracted class only) + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +// Todo: Kristian wonders if this class really is necessary and if it overlaps other concepts. +public final class ProjectIndex +{ + + private final Map projects; + + private final Map indices; + + public ProjectIndex( List projects ) + { + this.projects = new HashMap( projects.size() * 2 ); + this.indices = new HashMap( projects.size() * 2 ); + + for ( int i = 0; i < projects.size(); i++ ) + { + MavenProject project = projects.get( i ); + String key = BuilderCommon.getKey( project ); + + this.getProjects().put( key, project ); + this.getIndices().put( key, i ); + } + } + + public Map getProjects() + { + return projects; + } + + public Map getIndices() + { + return indices; + } +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ProjectSegment.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ProjectSegment.java new file mode 100644 index 0000000000..97a0ddfaa3 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ProjectSegment.java @@ -0,0 +1,90 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.execution.ProjectDependencyGraph; +import org.apache.maven.project.MavenProject; + +import java.util.List; + +/** + * A build context that matches a mavenproject to a given tasksegment, and the session to be used. + *

+ * A note to the reader; + *

+ * There are several issues/discussions regarding how "aggregator" plugins should be handled. + * Read for instance http://docs.codehaus.org/display/MAVEN/Deterministic+Lifecycle+Planning + *

+ * In their current implementation they are "bolted" onto the lifecycle by separating them + * into TaskSegments. This class represents the execution context of one such task segment. + *

+ * Wise voices have suggested that maybe aggregators shouldn't be bound to the ordinary + * lifecycle at all, in which case we wouldn't be needing this class at all ( and + * ProjectBuildList.getByTaskSegments). Or maybe they should be introduced in the calculation + * of the execution plan instead, which seems much nicer. + *

+ * Additionally this class contains a clone of the MavenSession, which is *only* needed + * because it has as notion of a "current" project. + * + * @author Jason van Zyl + * @author Benjamin Bentmann + * @author Kristian Rosenvold + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +public final class ProjectSegment +{ + private final MavenProject project; + + private final TaskSegment taskSegment; + + private final MavenSession session; + + + public ProjectSegment( MavenProject project, TaskSegment taskSegment, MavenSession copiedSession ) + { + this.project = project; + this.taskSegment = taskSegment; + this.session = copiedSession; + } + + public MavenSession getSession() + { + return session; + } + + public MavenProject getProject() + { + return project; + } + + public TaskSegment getTaskSegment() + { + return taskSegment; + } + + public List getImmediateUpstreamProjects() + { + final ProjectDependencyGraph dependencyGraph = getSession().getProjectDependencyGraph(); + return dependencyGraph.getUpstreamProjects( getProject(), false ); + } + + @Override + public String toString() + { + return getProject().getId() + " -> " + getTaskSegment(); + } +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ReactorBuildStatus.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ReactorBuildStatus.java new file mode 100644 index 0000000000..f76c68a8b6 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ReactorBuildStatus.java @@ -0,0 +1,73 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.ProjectDependencyGraph; +import org.apache.maven.project.MavenProject; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; + +/** + * Contains status information that is global to an entire reactor build. + * + * @author Kristian Rosenvold + */ +public class ReactorBuildStatus +{ + private final ProjectDependencyGraph projectDependencyGraph; + + private final Collection blackListedProjects = Collections.synchronizedSet( new HashSet() ); + + private volatile boolean halted = false; + + public ReactorBuildStatus( ProjectDependencyGraph projectDependencyGraph ) + { + this.projectDependencyGraph = projectDependencyGraph; + } + + public boolean isBlackListed( MavenProject project ) + { + return blackListedProjects.contains( BuilderCommon.getKey( project ) ); + } + + public void blackList( MavenProject project ) + { + if ( blackListedProjects.add( BuilderCommon.getKey( project ) ) && projectDependencyGraph != null ) + { + for ( MavenProject downstreamProject : projectDependencyGraph.getDownstreamProjects( project, true ) ) + { + blackListedProjects.add( BuilderCommon.getKey( downstreamProject ) ); + } + } + } + + public void halt() + { + halted = true; + } + + public boolean isHalted() + { + return halted; + } + + public boolean isHaltedOrBlacklisted( MavenProject mavenProject ) + { + return isBlackListed( mavenProject ) || isHalted(); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ReactorContext.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ReactorContext.java new file mode 100644 index 0000000000..e8e24b0f93 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ReactorContext.java @@ -0,0 +1,67 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.MavenExecutionResult; + +/** + * Context that is fixed for the entire reactor build. + * + * @author Jason van Zyl + * @author Kristian Rosenvold + * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +public class ReactorContext +{ + private final MavenExecutionResult result; + + private final ProjectIndex projectIndex; + + private final ClassLoader originalContextClassLoader; + + private final ReactorBuildStatus reactorBuildStatus; + + + public ReactorContext( MavenExecutionResult result, ProjectIndex projectIndex, + ClassLoader originalContextClassLoader, ReactorBuildStatus reactorBuildStatus ) + { + this.result = result; + this.projectIndex = projectIndex; + this.originalContextClassLoader = originalContextClassLoader; + this.reactorBuildStatus = reactorBuildStatus; + } + + public ReactorBuildStatus getReactorBuildStatus() + { + return reactorBuildStatus; + } + + public MavenExecutionResult getResult() + { + return result; + } + + public ProjectIndex getProjectIndex() + { + return projectIndex; + } + + public ClassLoader getOriginalContextClassLoader() + { + return originalContextClassLoader; + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/TaskSegment.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/TaskSegment.java new file mode 100644 index 0000000000..953224c50e --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/TaskSegment.java @@ -0,0 +1,67 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Describes the required task segment as provided on the maven command line; i.e. "clean jetty:run install" + * + * @author Benjamin Bentmann + * @author Kristian Rosenvold (extracted class only) + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + */ +public final class TaskSegment +{ + + // Can be both "LifeCycleTask" (clean/install) and "GoalTask" (org.mortbay.jetty:maven-jetty-plugin:6.1.19:run) + + private final List tasks; + + private final boolean aggregating; + + public TaskSegment( boolean aggregating ) + { + this.aggregating = aggregating; + tasks = new ArrayList(); + } + + public TaskSegment( boolean aggregating, Object... tasks ) + { + this.aggregating = aggregating; + this.tasks = new ArrayList( Arrays.asList( tasks ) ); + } + + @Override + public String toString() + { + return getTasks().toString(); + } + + public List getTasks() + { + return tasks; + } + + public boolean isAggregating() + { + return aggregating; + } + + // TODO: Consider throwing UnsupprtedSomething on hashCode/equals +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ThreadConfigurationService.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ThreadConfigurationService.java new file mode 100644 index 0000000000..1f9f491146 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ThreadConfigurationService.java @@ -0,0 +1,90 @@ +package org.apache.maven.lifecycle.internal; + +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.logging.Logger; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Component(role = ThreadConfigurationService.class) +public class ThreadConfigurationService +{ + @Requirement + private Logger logger; + + private final int cpuCores; + + + @SuppressWarnings({"UnusedDeclaration"}) + public ThreadConfigurationService() + { + cpuCores = Runtime.getRuntime().availableProcessors(); + } + + public ThreadConfigurationService( Logger logger, int cpuCores ) + { + this.logger = logger; + this.cpuCores = cpuCores; + } + + + public ExecutorService getExecutorService( String threadCountConfiguration, boolean perCoreThreadCount, + int largestBuildListSize ) + { + Integer threadCount = getThreadCount( threadCountConfiguration, perCoreThreadCount, largestBuildListSize ); + return getExecutorService( threadCount ); + + + } + + private ExecutorService getExecutorService( Integer threadCount ) + { + if ( threadCount == null ) + { + logger.info( "Building with unlimited threads" ); + return Executors.newCachedThreadPool(); + } + + logger.info( "Building with " + threadCount + " threads" ); + return Executors.newFixedThreadPool( threadCount ); + } + + /** + * Returns the thread count to use or null for unlimited threads. + * + * @param threadCountConfiguration The property passed from the command line. + * @param perCoreThreadCount Indicates if the threa count should be scaled per cpu core. + * @param largestBuildListSize the size of the largest module list (the number of modules) + * @return The number of threads to use or null if unlimited + */ + + Integer getThreadCount( String threadCountConfiguration, boolean perCoreThreadCount, int largestBuildListSize ) + { + // Default to a value that is not larger than what we can use ;) + float threadCount = Math.min( cpuCores, largestBuildListSize ); + if ( threadCountConfiguration != null ) + { + try + { + threadCount = Float.parseFloat( threadCountConfiguration ); + } + catch ( NumberFormatException e ) + { + logger.warn( + "Couldn't parse thread count, will default to " + threadCount + ": " + threadCountConfiguration ); + } + } + if ( perCoreThreadCount ) + { + threadCount = threadCount * cpuCores; + } + + final int endResult = Math.round( threadCount ); + if ( logger.isDebugEnabled() ) + { + logger.debug( "Thread pool size: " + endResult ); + } + return endResult; + } +} \ No newline at end of file diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ThreadOutputMuxer.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ThreadOutputMuxer.java new file mode 100644 index 0000000000..0950eafd97 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/ThreadOutputMuxer.java @@ -0,0 +1,467 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * @author Kristian Rosenvold + *

+ * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. + * This class in particular may spontaneusly self-combust and be replaced by a plexus-compliant thread aware + * logger implementation at any time. + */ +@SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter"}) +public class ThreadOutputMuxer +{ + private final Iterator projects; + + private final ThreadLocal projectBuildThreadLocal = new ThreadLocal(); + + private final Map streams = new HashMap(); + + private final Map printStreams = new HashMap(); + + private final ByteArrayOutputStream defaultOutputStreamForUnknownData = new ByteArrayOutputStream(); + + private final PrintStream defaultPringStream = new PrintStream( defaultOutputStreamForUnknownData ); + + private final Set completedBuilds = Collections.synchronizedSet( new HashSet() ); + + private volatile ProjectSegment currentBuild; + + private final PrintStream originalSystemOUtStream; + + private final ConsolePrinter printer; + + /** + * A simple but safe solution for printing to the console. + */ + + class ConsolePrinter + implements Runnable + { + public volatile boolean running; + + private final ProjectBuildList projectBuildList; + + ConsolePrinter( ProjectBuildList projectBuildList ) + { + this.projectBuildList = projectBuildList; + } + + public void run() + { + running = true; + for ( ProjectSegment projectBuild : projectBuildList ) + { + final PrintStream projectStream = printStreams.get( projectBuild ); + ByteArrayOutputStream projectOs = streams.get( projectBuild ); + + do + { + synchronized ( projectStream ) + { + try + { + projectStream.wait( 100 ); + } + catch ( InterruptedException e ) + { + throw new RuntimeException( e ); + } + try + { + projectOs.writeTo( originalSystemOUtStream ); + } + catch ( IOException e ) + { + throw new RuntimeException( e ); + } + + projectOs.reset(); + } + } + while ( !completedBuilds.contains( projectBuild ) ); + } + running = false; + } + + /* + Wait until we are sure the print-stream thread is running. + */ + + public void waitUntilRunning( boolean expect ) + { + while ( !running == expect ) + { + try + { + Thread.sleep( 10 ); + } + catch ( InterruptedException e ) + { + throw new RuntimeException( e ); + } + } + } + } + + public ThreadOutputMuxer( ProjectBuildList segmentChunks, PrintStream originalSystemOut ) + { + projects = segmentChunks.iterator(); + for ( ProjectSegment segmentChunk : segmentChunks ) + { + final ByteArrayOutputStream value = new ByteArrayOutputStream(); + streams.put( segmentChunk, value ); + printStreams.put( segmentChunk, new PrintStream( value ) ); + } + setNext(); + this.originalSystemOUtStream = originalSystemOut; + System.setOut( new ThreadBoundPrintStream( this.originalSystemOUtStream ) ); + printer = new ConsolePrinter( segmentChunks ); + new Thread( printer ).start(); + printer.waitUntilRunning( true ); + } + + public void close() + { + printer.waitUntilRunning( false ); + System.setOut( this.originalSystemOUtStream ); + } + + private void setNext() + { + currentBuild = projects.hasNext() ? projects.next() : null; + } + + private boolean ownsRealOutputStream( ProjectSegment projectBuild ) + { + return projectBuild.equals( currentBuild ); + } + + private PrintStream getThreadBoundPrintStream() + { + ProjectSegment threadProject = projectBuildThreadLocal.get(); + if ( threadProject == null ) + { + return defaultPringStream; + } + if ( ownsRealOutputStream( threadProject ) ) + { + return originalSystemOUtStream; + } + return printStreams.get( threadProject ); + } + + public void associateThreadWithProjectSegment( ProjectSegment projectBuild ) + { + projectBuildThreadLocal.set( projectBuild ); + } + + public void setThisModuleComplete( ProjectSegment projectBuild ) + { + completedBuilds.add( projectBuild ); + PrintStream stream = printStreams.get( projectBuild ); + synchronized ( stream ) + { + stream.notifyAll(); + } + disconnectThreadFromProject(); + } + + private void disconnectThreadFromProject() + { + projectBuildThreadLocal.remove(); + } + + private class ThreadBoundPrintStream + extends PrintStream + { + + public ThreadBoundPrintStream( PrintStream systemOutStream ) + { + super( systemOutStream ); + } + + private PrintStream getOutputStreamForCurrentThread() + { + return getThreadBoundPrintStream(); + } + + @Override + public void println() + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.println(); + currentStream.notifyAll(); + } + } + + @Override + public void print( char c ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.print( c ); + currentStream.notifyAll(); + } + } + + @Override + public void println( char x ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.println( x ); + currentStream.notifyAll(); + } + } + + @Override + public void print( double d ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.print( d ); + currentStream.notifyAll(); + } + } + + @Override + public void println( double x ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.println( x ); + currentStream.notifyAll(); + } + } + + @Override + public void print( float f ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.print( f ); + currentStream.notifyAll(); + } + } + + @Override + public void println( float x ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.println( x ); + currentStream.notifyAll(); + } + } + + @Override + public void print( int i ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.print( i ); + currentStream.notifyAll(); + } + } + + @Override + public void println( int x ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.println( x ); + currentStream.notifyAll(); + } + } + + @Override + public void print( long l ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.print( l ); + currentStream.notifyAll(); + } + } + + @Override + public void println( long x ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.print( x ); + currentStream.notifyAll(); + } + } + + @Override + public void print( boolean b ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.print( b ); + currentStream.notifyAll(); + } + } + + @Override + public void println( boolean x ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.print( x ); + currentStream.notifyAll(); + } + } + + @Override + public void print( char s[] ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.print( s ); + currentStream.notifyAll(); + } + } + + @Override + public void println( char x[] ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.print( x ); + currentStream.notifyAll(); + } + } + + @Override + public void print( Object obj ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.print( obj ); + currentStream.notifyAll(); + } + } + + @Override + public void println( Object x ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.println( x ); + currentStream.notifyAll(); + } + } + + @Override + public void print( String s ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.print( s ); + currentStream.notifyAll(); + } + } + + @Override + public void println( String x ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.println( x ); + currentStream.notifyAll(); + } + } + + @Override + public void write( byte b[], int off, int len ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.write( b, off, len ); + currentStream.notifyAll(); + } + } + + @Override + public void close() + { + getOutputStreamForCurrentThread().close(); + } + + @Override + public void flush() + { + getOutputStreamForCurrentThread().flush(); + } + + @Override + public void write( int b ) + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.write( b ); + currentStream.notifyAll(); + } + } + + @Override + public void write( byte b[] ) + throws IOException + { + final PrintStream currentStream = getOutputStreamForCurrentThread(); + synchronized ( currentStream ) + { + currentStream.write( b ); + currentStream.notifyAll(); + } + } + } +} diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java index 5dc88b1985..a7dec04c8a 100644 --- a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java +++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java @@ -475,7 +475,7 @@ public class DefaultMavenPluginManager } } - private void populatePluginFields( Object mojo, MojoDescriptor mojoDescriptor, ClassRealm pluginRealm, + private synchronized void populatePluginFields( Object mojo, MojoDescriptor mojoDescriptor, ClassRealm pluginRealm, PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator ) throws PluginConfigurationException { diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginManager.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginManager.java index 35b169d222..a5c1a09189 100644 --- a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginManager.java +++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginManager.java @@ -85,23 +85,6 @@ public class DefaultPluginManager @Requirement private LegacySupport legacySupport; - private RepositoryRequest getRepositoryRequest( MavenSession session, MavenProject project ) - { - RepositoryRequest request = new DefaultRepositoryRequest(); - - request.setCache( session.getRepositoryCache() ); - request.setLocalRepository( session.getLocalRepository() ); - if ( project != null ) - { - request.setRemoteRepositories( project.getPluginArtifactRepositories() ); - } - request.setOffline( session.isOffline() ); - request.setForceUpdate( session.getRequest().isUpdateSnapshots() ); - request.setTransferListener( session.getRequest().getTransferListener() ); - - return request; - } - public void executeMojo( MavenProject project, MojoExecution execution, MavenSession session ) throws MojoExecutionException, ArtifactResolutionException, MojoFailureException, ArtifactNotFoundException, InvalidDependencyVersionException, PluginManagerException, PluginConfigurationException @@ -117,7 +100,7 @@ public class DefaultPluginManager PluginDescriptor pluginDescriptor; try { - RepositoryRequest repositoryRequest = getRepositoryRequest( session, session.getCurrentProject() ); + RepositoryRequest repositoryRequest = DefaultRepositoryRequest.getRepositoryRequest( session, session.getCurrentProject() ); pluginDescriptor = pluginManager.getPluginDescriptor( plugin, repositoryRequest ); @@ -149,7 +132,7 @@ public class DefaultPluginManager PluginDescriptor pluginDescriptor; try { - RepositoryRequest repositoryRequest = getRepositoryRequest( session, session.getCurrentProject() ); + RepositoryRequest repositoryRequest = DefaultRepositoryRequest.getRepositoryRequest( session, session.getCurrentProject() ); pluginDescriptor = pluginManager.getPluginDescriptor( plugin, repositoryRequest ); diff --git a/maven-core/src/main/resources/META-INF/plexus/components.xml b/maven-core/src/main/resources/META-INF/plexus/components.xml index 9d400b7ae7..0995832e64 100644 --- a/maven-core/src/main/resources/META-INF/plexus/components.xml +++ b/maven-core/src/main/resources/META-INF/plexus/components.xml @@ -17,103 +17,146 @@ --> - - org.apache.maven.plugin.MavenPluginCollector - org.apache.maven.plugin.MavenPluginCollector - - - - org.apache.maven.lifecycle.LifecycleExecutor - org.apache.maven.lifecycle.DefaultLifecycleExecutor - - - - org.codehaus.plexus.logging.Logger - default - logger - - - org.apache.maven.plugin.BuildPluginManager - - - org.apache.maven.ProjectDependenciesResolver - - - org.apache.maven.repository.RepositorySystem - - - org.apache.maven.lifecycle.mapping.LifecycleMapping - lifecycleMappings - - - org.apache.maven.plugin.version.PluginVersionResolver - - - org.apache.maven.plugin.prefix.PluginPrefixResolver - - - - - - default - - - validate - initialize - generate-sources - process-sources - generate-resources - process-resources - compile - process-classes - generate-test-sources - process-test-sources - generate-test-resources - process-test-resources - test-compile - process-test-classes - test - prepare-package - package - pre-integration-test - integration-test - post-integration-test - verify - install - deploy - - - - - clean - - pre-clean - clean - post-clean - - - org.apache.maven.plugins:maven-clean-plugin:clean - - - - - site - - pre-site - site - post-site - site-deploy - - - org.apache.maven.plugins:maven-site-plugin:site - - org.apache.maven.plugins:maven-site-plugin:deploy - - - - - - + + org.apache.maven.plugin.MavenPluginCollector + org.apache.maven.plugin.MavenPluginCollector + + + + + org.apache.maven.lifecycle.DefaultLifecycles + org.apache.maven.lifecycle.DefaultLifecycles + + + + + default + + + validate + initialize + generate-sources + process-sources + generate-resources + process-resources + compile + process-classes + generate-test-sources + process-test-sources + generate-test-resources + process-test-resources + test-compile + process-test-classes + test + prepare-package + package + pre-integration-test + integration-test + post-integration-test + verify + install + deploy + + + + + clean + + pre-clean + clean + post-clean + + + org.apache.maven.plugins:maven-clean-plugin:clean + + + + + site + + pre-site + site + post-site + site-deploy + + + org.apache.maven.plugins:maven-site-plugin:site + + org.apache.maven.plugins:maven-site-plugin:deploy + + + + + + + default + + + test + false + true + + + org.apache.maven.plugins:maven-assembly-plugin + true + + + + + + + + + org.apache.maven.lifecycle.LifeCyclePluginAnalyzer + org.apache.maven.lifecycle.internal.LifecyclePluginAnalyzerImpl + + + org.apache.maven.lifecycle.DefaultLifecycles + + + org.apache.maven.lifecycle.mapping.LifecycleMapping + lifecycleMappings + + + org.codehaus.plexus.logging.Logger + default + logger + + + org.sonatype.plexus.components.sec.dispatcher.SecDispatcher diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java new file mode 100644 index 0000000000..5046626bfe --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java @@ -0,0 +1,76 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle; + +import org.codehaus.plexus.PlexusTestCase; +import org.codehaus.plexus.component.annotations.Requirement; + +import java.util.List; + +/** + * @author Kristian Rosenvold + */ + +public class DefaultLifecyclesTest + extends PlexusTestCase +{ + @Requirement + private DefaultLifecycles defaultLifeCycles; + + + protected void setUp() + throws Exception + { + super.setUp(); + defaultLifeCycles = lookup( DefaultLifecycles.class ); + } + + @Override + protected void tearDown() + throws Exception + { + defaultLifeCycles = null; + super.tearDown(); + } + + public void testLifecycle() + throws Exception + { + final List cycles = defaultLifeCycles.getLifeCycles(); + assertNotNull( cycles ); + final Lifecycle lifecycle = cycles.get( 0 ); + assertEquals( "default", lifecycle.getId() ); + assertEquals( 23, lifecycle.getPhases().size() ); + + } + + public void testScheduling() + throws Exception + { + final List schedulings = defaultLifeCycles.getSchedules(); + assertNotNull( schedulings ); + assertTrue( schedulings.size() > 0 ); + Scheduling first = schedulings.get( 0 ); + assertNotNull( first.getLifecycle() ); + final List schedules = first.getSchedules(); + assertNotNull( schedules ); + // Ok so if we ever change the first schedule this test will have to change + Schedule firstSchedule = schedules.get( 0 ); + assertEquals( "test", firstSchedule.getPhase() ); + assertTrue( "Should be parllel", firstSchedule.isParallel() ); + + } + +} \ No newline at end of file diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorSubModulesTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorSubModulesTest.java new file mode 100644 index 0000000000..bbc14e4229 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorSubModulesTest.java @@ -0,0 +1,99 @@ +/* + * 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. + */ + + +package org.apache.maven.lifecycle; + +import org.apache.maven.AbstractCoreMavenComponentTestCase; +import org.apache.maven.exception.ExceptionHandler; +import org.apache.maven.lifecycle.internal.LifecycleDependencyResolver; +import org.apache.maven.lifecycle.internal.LifecycleExecutionPlanCalculator; +import org.apache.maven.lifecycle.internal.LifecycleModuleBuilder; +import org.apache.maven.lifecycle.internal.LifecyclePluginAnalyzerImpl; +import org.apache.maven.lifecycle.internal.LifecycleTaskSegmentCalculator; +import org.apache.maven.lifecycle.internal.MojoExecutor; +import org.codehaus.plexus.component.annotations.Requirement; + +/** + * Just asserts that it's able to create those components. Handy when plexus gets a nervous breakdown. + * + * @author Kristian Rosenvold + */ + +public class LifecycleExecutorSubModulesTest + extends AbstractCoreMavenComponentTestCase +{ + @Requirement + private DefaultLifecycles defaultLifeCycles; + + @Requirement + private MojoExecutor mojoExecutor; + + @Requirement + private LifecycleModuleBuilder lifeCycleBuilder; + + @Requirement + private LifecycleDependencyResolver lifeCycleDependencyResolver; + + @Requirement + private LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator; + + @Requirement + private LifeCyclePluginAnalyzer lifeCyclePluginAnalyzer; + + @Requirement + private LifecycleTaskSegmentCalculator lifeCycleTaskSegmentCalculator; + + + protected void setUp() + throws Exception + { + super.setUp(); + defaultLifeCycles = lookup( DefaultLifecycles.class ); + mojoExecutor = lookup( MojoExecutor.class ); + lifeCycleBuilder = lookup( LifecycleModuleBuilder.class ); + lifeCycleDependencyResolver = lookup( LifecycleDependencyResolver.class ); + lifeCycleExecutionPlanCalculator = lookup( LifecycleExecutionPlanCalculator.class ); + lifeCyclePluginAnalyzer = lookup( LifecyclePluginAnalyzerImpl.class ); + lifeCycleTaskSegmentCalculator = lookup( LifecycleTaskSegmentCalculator.class ); + lookup( ExceptionHandler.class ); + } + + @Override + protected void tearDown() + throws Exception + { + defaultLifeCycles = null; + super.tearDown(); + } + + protected String getProjectsDirectory() + { + return "src/test/projects/lifecycle-executor"; + } + + public void testCrweation() + throws Exception + { + assertNotNull( defaultLifeCycles ); + assertNotNull( mojoExecutor ); + assertNotNull( lifeCycleBuilder ); + assertNotNull( lifeCycleDependencyResolver ); + assertNotNull( lifeCycleExecutionPlanCalculator ); + assertNotNull( lifeCyclePluginAnalyzer ); + assertNotNull( lifeCycleTaskSegmentCalculator ); + } + +} \ No newline at end of file diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorTest.java index e2dadd0104..c59156b7c8 100644 --- a/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorTest.java +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorTest.java @@ -1,30 +1,74 @@ -package org.apache.maven.lifecycle; +/* + * 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.File; -import java.util.ArrayList; -import java.util.List; + +package org.apache.maven.lifecycle; import org.apache.maven.AbstractCoreMavenComponentTestCase; import org.apache.maven.artifact.Artifact; import org.apache.maven.exception.ExceptionHandler; import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.internal.ExecutionPlanItem; +import org.apache.maven.lifecycle.internal.LifecycleExecutionPlanCalculator; +import org.apache.maven.lifecycle.internal.LifecycleTaskSegmentCalculator; +import org.apache.maven.lifecycle.internal.LifecycleTaskSegmentCalculatorImpl; +import org.apache.maven.lifecycle.internal.MojoDescriptorCreator; import org.apache.maven.model.Plugin; +import org.apache.maven.plugin.InvalidPluginDescriptorException; import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.MojoNotFoundException; +import org.apache.maven.plugin.PluginDescriptorParsingException; +import org.apache.maven.plugin.PluginManagerException; +import org.apache.maven.plugin.PluginNotFoundException; +import org.apache.maven.plugin.PluginResolutionException; import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.util.xml.Xpp3Dom; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + public class LifecycleExecutorTest extends AbstractCoreMavenComponentTestCase { @Requirement private DefaultLifecycleExecutor lifecycleExecutor; - + + @Requirement + private LifecycleTaskSegmentCalculatorImpl lifeCycleTaskSegmentCalculator; + + @Requirement + private LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator; + + @Requirement + private MojoDescriptorCreator mojoDescriptorCreator; + + protected void setUp() throws Exception { super.setUp(); lifecycleExecutor = (DefaultLifecycleExecutor) lookup( LifecycleExecutor.class ); + lifeCycleTaskSegmentCalculator = + (LifecycleTaskSegmentCalculatorImpl) lookup( LifecycleTaskSegmentCalculator.class ); + lifeCycleExecutionPlanCalculator = lookup( LifecycleExecutionPlanCalculator.class ); + mojoDescriptorCreator = lookup( MojoDescriptorCreator.class ); lookup( ExceptionHandler.class ); } @@ -40,11 +84,11 @@ public class LifecycleExecutorTest { return "src/test/projects/lifecycle-executor"; } - + // ----------------------------------------------------------------------------------------------- // Tests which exercise the lifecycle executor when it is dealing with default lifecycle phases. // ----------------------------------------------------------------------------------------------- - + public void testCalculationOfBuildPlanWithIndividualTaskWherePluginIsSpecifiedInThePom() throws Exception { @@ -54,12 +98,14 @@ public class LifecycleExecutorTest MavenSession session = createMavenSession( pom ); assertEquals( "project-basic", session.getCurrentProject().getArtifactId() ); assertEquals( "1.0", session.getCurrentProject().getVersion() ); - List executionPlan = lifecycleExecutor.calculateExecutionPlan( session, "resources:resources" ).getExecutions(); + List executionPlan = getExecutions( calculateExecutionPlan( session, "resources:resources" ) ); assertEquals( 1, executionPlan.size() ); MojoExecution mojoExecution = executionPlan.get( 0 ); assertNotNull( mojoExecution ); - assertEquals( "org.apache.maven.plugins", mojoExecution.getMojoDescriptor().getPluginDescriptor().getGroupId() ); - assertEquals( "maven-resources-plugin", mojoExecution.getMojoDescriptor().getPluginDescriptor().getArtifactId() ); + assertEquals( "org.apache.maven.plugins", + mojoExecution.getMojoDescriptor().getPluginDescriptor().getGroupId() ); + assertEquals( "maven-resources-plugin", + mojoExecution.getMojoDescriptor().getPluginDescriptor().getArtifactId() ); assertEquals( "0.1", mojoExecution.getMojoDescriptor().getPluginDescriptor().getVersion() ); } @@ -72,15 +118,16 @@ public class LifecycleExecutorTest MavenSession session = createMavenSession( pom ); assertEquals( "project-basic", session.getCurrentProject().getArtifactId() ); assertEquals( "1.0", session.getCurrentProject().getVersion() ); - List executionPlan = lifecycleExecutor.calculateExecutionPlan( session, "clean" ).getExecutions(); + List executionPlan = getExecutions( calculateExecutionPlan( session, "clean" ) ); assertEquals( 1, executionPlan.size() ); MojoExecution mojoExecution = executionPlan.get( 0 ); assertNotNull( mojoExecution ); - assertEquals( "org.apache.maven.plugins", mojoExecution.getMojoDescriptor().getPluginDescriptor().getGroupId() ); + assertEquals( "org.apache.maven.plugins", + mojoExecution.getMojoDescriptor().getPluginDescriptor().getGroupId() ); assertEquals( "maven-clean-plugin", mojoExecution.getMojoDescriptor().getPluginDescriptor().getArtifactId() ); assertEquals( "0.1", mojoExecution.getMojoDescriptor().getPluginDescriptor().getVersion() ); } - + public void testCalculationOfBuildPlanWithIndividualTaskOfTheCleanCleanGoal() throws Exception { @@ -90,15 +137,26 @@ public class LifecycleExecutorTest MavenSession session = createMavenSession( pom ); assertEquals( "project-basic", session.getCurrentProject().getArtifactId() ); assertEquals( "1.0", session.getCurrentProject().getVersion() ); - List executionPlan = lifecycleExecutor.calculateExecutionPlan( session, "clean:clean" ).getExecutions(); + List executionPlan = getExecutions( calculateExecutionPlan( session, "clean:clean" ) ); assertEquals( 1, executionPlan.size() ); MojoExecution mojoExecution = executionPlan.get( 0 ); assertNotNull( mojoExecution ); - assertEquals( "org.apache.maven.plugins", mojoExecution.getMojoDescriptor().getPluginDescriptor().getGroupId() ); + assertEquals( "org.apache.maven.plugins", + mojoExecution.getMojoDescriptor().getPluginDescriptor().getGroupId() ); assertEquals( "maven-clean-plugin", mojoExecution.getMojoDescriptor().getPluginDescriptor().getArtifactId() ); assertEquals( "0.1", mojoExecution.getMojoDescriptor().getPluginDescriptor().getVersion() ); } + List getExecutions( MavenExecutionPlan mavenExecutionPlan ) + { + List result = new ArrayList(); + for ( ExecutionPlanItem executionPlanItem : mavenExecutionPlan ) + { + result.add( executionPlanItem.getMojoExecution() ); + } + return result; + } + // We need to take in multiple lifecycles public void testCalculationOfBuildPlanTasksOfTheCleanLifecycleAndTheInstallLifecycle() throws Exception @@ -107,8 +165,8 @@ public class LifecycleExecutorTest MavenSession session = createMavenSession( pom ); assertEquals( "project-with-additional-lifecycle-elements", session.getCurrentProject().getArtifactId() ); assertEquals( "1.0", session.getCurrentProject().getVersion() ); - List executionPlan = lifecycleExecutor.calculateExecutionPlan( session, "clean", "install" ).getExecutions(); - + List executionPlan = getExecutions( calculateExecutionPlan( session, "clean", "install" ) ); + //[01] clean:clean //[02] resources:resources //[03] compiler:compile @@ -121,7 +179,7 @@ public class LifecycleExecutorTest //[10] install:install // assertEquals( 10, executionPlan.size() ); - + assertEquals( "clean:clean", executionPlan.get( 0 ).getMojoDescriptor().getFullGoalName() ); assertEquals( "resources:resources", executionPlan.get( 1 ).getMojoDescriptor().getFullGoalName() ); assertEquals( "compiler:compile", executionPlan.get( 2 ).getMojoDescriptor().getFullGoalName() ); @@ -130,8 +188,8 @@ public class LifecycleExecutorTest assertEquals( "compiler:testCompile", executionPlan.get( 5 ).getMojoDescriptor().getFullGoalName() ); assertEquals( "it:generate-test-metadata", executionPlan.get( 6 ).getMojoDescriptor().getFullGoalName() ); assertEquals( "surefire:test", executionPlan.get( 7 ).getMojoDescriptor().getFullGoalName() ); - assertEquals( "jar:jar", executionPlan.get( 8 ).getMojoDescriptor().getFullGoalName() ); - assertEquals( "install:install", executionPlan.get( 9 ).getMojoDescriptor().getFullGoalName() ); + assertEquals( "jar:jar", executionPlan.get( 8 ).getMojoDescriptor().getFullGoalName() ); + assertEquals( "install:install", executionPlan.get( 9 ).getMojoDescriptor().getFullGoalName() ); } // We need to take in multiple lifecycles @@ -142,15 +200,15 @@ public class LifecycleExecutorTest MavenSession session = createMavenSession( pom ); assertEquals( "project-with-multiple-executions", session.getCurrentProject().getArtifactId() ); assertEquals( "1.0.1", session.getCurrentProject().getVersion() ); - - MavenExecutionPlan plan = lifecycleExecutor.calculateExecutionPlan( session, "clean", "install" ); - + + MavenExecutionPlan plan = calculateExecutionPlan( session, "clean", "install" ); + assertTrue( plan.getRequiredResolutionScopes().contains( Artifact.SCOPE_COMPILE ) ); assertTrue( plan.getRequiredResolutionScopes().contains( Artifact.SCOPE_RUNTIME ) ); assertTrue( plan.getRequiredResolutionScopes().contains( Artifact.SCOPE_TEST ) ); - - List executions = plan.getExecutions(); - + + List executions = getExecutions( plan ); + //[01] clean:clean //[02] modello:xpp3-writer //[03] modello:java @@ -168,9 +226,9 @@ public class LifecycleExecutorTest //[15] plugin:addPluginArtifactMetadata //[16] install:install // - - assertEquals( 16, executions.size() ); - + + assertEquals( 16, executions.size() ); + assertEquals( "clean:clean", executions.get( 0 ).getMojoDescriptor().getFullGoalName() ); assertEquals( "it:xpp3-writer", executions.get( 1 ).getMojoDescriptor().getFullGoalName() ); assertEquals( "it:java", executions.get( 2 ).getMojoDescriptor().getFullGoalName() ); @@ -184,23 +242,27 @@ public class LifecycleExecutorTest assertEquals( "resources:testResources", executions.get( 10 ).getMojoDescriptor().getFullGoalName() ); assertEquals( "compiler:testCompile", executions.get( 11 ).getMojoDescriptor().getFullGoalName() ); assertEquals( "surefire:test", executions.get( 12 ).getMojoDescriptor().getFullGoalName() ); - assertEquals( "jar:jar", executions.get( 13 ).getMojoDescriptor().getFullGoalName() ); - assertEquals( "plugin:addPluginArtifactMetadata", executions.get( 14 ).getMojoDescriptor().getFullGoalName() ); + assertEquals( "jar:jar", executions.get( 13 ).getMojoDescriptor().getFullGoalName() ); + assertEquals( "plugin:addPluginArtifactMetadata", executions.get( 14 ).getMojoDescriptor().getFullGoalName() ); assertEquals( "install:install", executions.get( 15 ).getMojoDescriptor().getFullGoalName() ); - - assertEquals( "src/main/mdo/remote-resources.mdo", new MojoExecutionXPathContainer( executions.get( 1 ) ).getValue( "configuration/models[1]/model" ) ); - assertEquals( "src/main/mdo/supplemental-model.mdo", new MojoExecutionXPathContainer( executions.get( 4 ) ).getValue( "configuration/models[1]/model" ) ); - } - + + assertEquals( "src/main/mdo/remote-resources.mdo", + new MojoExecutionXPathContainer( executions.get( 1 ) ).getValue( + "configuration/models[1]/model" ) ); + assertEquals( "src/main/mdo/supplemental-model.mdo", + new MojoExecutionXPathContainer( executions.get( 4 ) ).getValue( + "configuration/models[1]/model" ) ); + } + public void testLifecycleQueryingUsingADefaultLifecyclePhase() throws Exception - { + { File pom = getProject( "project-with-additional-lifecycle-elements" ); MavenSession session = createMavenSession( pom ); assertEquals( "project-with-additional-lifecycle-elements", session.getCurrentProject().getArtifactId() ); assertEquals( "1.0", session.getCurrentProject().getVersion() ); - List executionPlan = lifecycleExecutor.calculateExecutionPlan( session, "package" ).getExecutions(); - + List executionPlan = getExecutions( calculateExecutionPlan( session, "package" ) ); + //[01] resources:resources //[02] compiler:compile //[03] it:generate-metadata @@ -211,7 +273,7 @@ public class LifecycleExecutorTest //[08] jar:jar // assertEquals( 8, executionPlan.size() ); - + assertEquals( "resources:resources", executionPlan.get( 0 ).getMojoDescriptor().getFullGoalName() ); assertEquals( "compiler:compile", executionPlan.get( 1 ).getMojoDescriptor().getFullGoalName() ); assertEquals( "it:generate-metadata", executionPlan.get( 2 ).getMojoDescriptor().getFullGoalName() ); @@ -219,48 +281,74 @@ public class LifecycleExecutorTest assertEquals( "compiler:testCompile", executionPlan.get( 4 ).getMojoDescriptor().getFullGoalName() ); assertEquals( "it:generate-test-metadata", executionPlan.get( 5 ).getMojoDescriptor().getFullGoalName() ); assertEquals( "surefire:test", executionPlan.get( 6 ).getMojoDescriptor().getFullGoalName() ); - assertEquals( "jar:jar", executionPlan.get( 7 ).getMojoDescriptor().getFullGoalName() ); - } - + assertEquals( "jar:jar", executionPlan.get( 7 ).getMojoDescriptor().getFullGoalName() ); + } + public void testLifecyclePluginsRetrievalForDefaultLifecycle() throws Exception { - List plugins = new ArrayList( lifecycleExecutor.getPluginsBoundByDefaultToAllLifecycles( "jar" ) ); - + List plugins = + new ArrayList( lifecycleExecutor.getPluginsBoundByDefaultToAllLifecycles( "jar" ) ); + assertEquals( 8, plugins.size() ); } - + public void testPluginConfigurationCreation() throws Exception { File pom = getProject( "project-with-additional-lifecycle-elements" ); MavenSession session = createMavenSession( pom ); MojoDescriptor mojoDescriptor = - lifecycleExecutor.getMojoDescriptor( "org.apache.maven.its.plugins:maven-it-plugin:0.1:java", - session, session.getCurrentProject() ); - Xpp3Dom dom = lifecycleExecutor.convert( mojoDescriptor ); + mojoDescriptorCreator.getMojoDescriptor( "org.apache.maven.its.plugins:maven-it-plugin:0.1:java", session, + session.getCurrentProject() ); + Xpp3Dom dom = MojoDescriptorCreator.convert( mojoDescriptor ); System.out.println( dom ); } + // Todo: This method is kind of an oddity. It is only called from the LifecycleExecutorTest, hence it should + // really not exist, or at least be moved into the test class. + + MavenExecutionPlan calculateExecutionPlan( MavenSession session, String... tasks ) + throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, + MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, + PluginManagerException, LifecyclePhaseNotFoundException, LifecycleNotFoundException, + PluginVersionResolutionException + { + List taskSegments = + lifeCycleTaskSegmentCalculator.calculateTaskSegments( session, Arrays.asList( tasks ) ); + + org.apache.maven.lifecycle.internal.TaskSegment mergedSegment = + new org.apache.maven.lifecycle.internal.TaskSegment( false ); + + for ( org.apache.maven.lifecycle.internal.TaskSegment taskSegment : taskSegments ) + { + mergedSegment.getTasks().addAll( taskSegment.getTasks() ); + } + + return lifeCycleExecutionPlanCalculator.calculateExecutionPlan( session, session.getCurrentProject(), + mergedSegment.getTasks() ); + } + + public void testPluginPrefixRetrieval() throws Exception { File pom = getProject( "project-basic" ); MavenSession session = createMavenSession( pom ); - Plugin plugin = lifecycleExecutor.findPluginForPrefix( "resources", session ); + Plugin plugin = mojoDescriptorCreator.findPluginForPrefix( "resources", session ); assertEquals( "org.apache.maven.plugins", plugin.getGroupId() ); assertEquals( "maven-resources-plugin", plugin.getArtifactId() ); - } - + } + // Prefixes - + public void testFindingPluginPrefixforCleanClean() throws Exception { File pom = getProject( "project-basic" ); MavenSession session = createMavenSession( pom ); - Plugin plugin = lifecycleExecutor.findPluginForPrefix( "clean", session ); + Plugin plugin = mojoDescriptorCreator.findPluginForPrefix( "clean", session ); assertNotNull( plugin ); } - + } diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/MavenExecutionPlanTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/MavenExecutionPlanTest.java new file mode 100644 index 0000000000..0e764a1b59 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/MavenExecutionPlanTest.java @@ -0,0 +1,52 @@ +/* + * 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. + */ + + +package org.apache.maven.lifecycle; + +import junit.framework.TestCase; +import org.apache.maven.lifecycle.internal.ExecutionPlanItem; +import org.apache.maven.lifecycle.internal.stub.DefaultLifecyclesStub; +import org.apache.maven.lifecycle.internal.stub.LifecycleExecutionPlanCalculatorStub; + +import java.util.Iterator; +import java.util.List; + +/** + * @author Kristian Rosenvold + */ +public class MavenExecutionPlanTest + extends TestCase +{ + public void testFindFirstWithMatchingSchedule() + throws Exception + { + MavenExecutionPlan plan = LifecycleExecutionPlanCalculatorStub.getProjectAExceutionPlan(); + final List cycles = DefaultLifecyclesStub.getSchedulingList(); + final Schedule schedule = cycles.get( 0 ).getSchedules().get( 0 ); + + } + + public void testForceAllComplete() + throws Exception + { + MavenExecutionPlan plan = LifecycleExecutionPlanCalculatorStub.getProjectAExceutionPlan(); + plan.forceAllComplete(); + final Iterator planItemIterator = plan.iterator(); + assertFalse( planItemIterator.next().ensureComplete() ); + assertFalse( planItemIterator.next().ensureComplete() ); + } + +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/BuildListCalculatorTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/BuildListCalculatorTest.java new file mode 100644 index 0000000000..9e97a2b647 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/BuildListCalculatorTest.java @@ -0,0 +1,49 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal; + +import junit.framework.TestCase; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.internal.stub.LifecycleTaskSegmentCalculatorStub; +import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub; + +import java.util.List; + +public class BuildListCalculatorTest + extends TestCase +{ + + public void testCalculateProjectBuilds() + throws Exception + { + BuildListCalculator buildListCalculator = createBuildListCalculator(); + final MavenSession session = ProjectDependencyGraphStub.getMavenSession(); + List taskSegments = buildListCalculator.calculateTaskSegments( session ); + final ProjectBuildList buildList = buildListCalculator.calculateProjectBuilds( session, taskSegments ); + final ProjectBuildList segments = buildList.getByTaskSegment( taskSegments.get( 0 ) ); + assertEquals( "Stub data contains 3 segments", 3, taskSegments.size() ); + assertEquals( "Stub data contains 6 items", 6, segments.size() ); + final ProjectSegment build = segments.get( 0 ); + assertNotNull( build ); + } + + public static BuildListCalculator createBuildListCalculator() + { + LifecycleTaskSegmentCalculator lifecycleTaskSegmentCalculator = new LifecycleTaskSegmentCalculatorStub(); + return new BuildListCalculator( lifecycleTaskSegmentCalculator ); + } + +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/BuilderCommonTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/BuilderCommonTest.java new file mode 100644 index 0000000000..aed549606d --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/BuilderCommonTest.java @@ -0,0 +1,72 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal; + +import junit.framework.TestCase; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.lifecycle.internal.stub.LifecycleExecutionPlanCalculatorStub; +import org.apache.maven.lifecycle.internal.stub.LoggerStub; +import org.apache.maven.lifecycle.internal.stub.ProjectDependenciesResolverStub; +import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub; + +/** + * @author Kristian Rosenvold + */ +public class BuilderCommonTest + extends TestCase +{ + public void testResolveBuildPlan() + throws Exception + { + MavenSession original = ProjectDependencyGraphStub.getMavenSession(); + + final TaskSegment taskSegment1 = new TaskSegment( false ); + final MavenSession session1 = original.clone(); + session1.setCurrentProject( ProjectDependencyGraphStub.A ); + + final BuilderCommon builderCommon = getBuilderCommon(); + final MavenExecutionPlan plan = + builderCommon.resolveBuildPlan( session1, ProjectDependencyGraphStub.A, taskSegment1 ); + assertEquals( LifecycleExecutionPlanCalculatorStub.getProjectAExceutionPlan().size(), plan.size() ); + + } + + + public void testHandleBuildError() + throws Exception + { + } + + public void testAttachToThread() + throws Exception + { + } + + public void testGetKey() + throws Exception + { + } + + public static BuilderCommon getBuilderCommon() + { + final LifecycleDebugLogger logger = new LifecycleDebugLogger( new LoggerStub() ); + final LifecycleDependencyResolver lifecycleDependencyResolver = + new LifecycleDependencyResolver( new ProjectDependenciesResolverStub(), new LoggerStub() ); + return new BuilderCommon( logger, new LifecycleExecutionPlanCalculatorStub(), lifecycleDependencyResolver ); + } + +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ConcurrencyDependencyGraphTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ConcurrencyDependencyGraphTest.java new file mode 100644 index 0000000000..a15cccac71 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ConcurrencyDependencyGraphTest.java @@ -0,0 +1,92 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.execution.ProjectDependencyGraph; +import org.apache.maven.lifecycle.LifecycleNotFoundException; +import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException; +import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub; +import org.apache.maven.plugin.InvalidPluginDescriptorException; +import org.apache.maven.plugin.MojoNotFoundException; +import org.apache.maven.plugin.PluginDescriptorParsingException; +import org.apache.maven.plugin.PluginNotFoundException; +import org.apache.maven.plugin.PluginResolutionException; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.project.MavenProject; + +import java.util.List; + +import static org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub.*; + +/** + * @author Kristian Rosenvold + */ +public class ConcurrencyDependencyGraphTest + extends junit.framework.TestCase +{ + public void testConcurrencyGraphPrimaryVersion() + throws InvalidPluginDescriptorException, PluginVersionResolutionException, PluginDescriptorParsingException, + NoPluginFoundForPrefixException, MojoNotFoundException, PluginNotFoundException, PluginResolutionException, + LifecyclePhaseNotFoundException, LifecycleNotFoundException + { + ProjectDependencyGraph dependencyGraph = new ProjectDependencyGraphStub(); + final MavenSession session = ProjectDependencyGraphStub.getMavenSession(); + + ConcurrencyDependencyGraph graph = + new ConcurrencyDependencyGraph( getProjectBuildList( session ), dependencyGraph ); + + final List projectBuilds = graph.getRootSchedulableBuilds(); + assertEquals( 1, projectBuilds.size() ); + assertEquals( A, projectBuilds.get( 0 ) ); + + final List subsequent = graph.markAsFinished( A ); + assertEquals( 2, subsequent.size() ); + assertEquals( ProjectDependencyGraphStub.B, subsequent.get( 0 ) ); + assertEquals( C, subsequent.get( 1 ) ); + + final List bDescendants = graph.markAsFinished( B ); + assertEquals( 1, bDescendants.size() ); + assertEquals( Y, bDescendants.get( 0 ) ); + + final List cDescendants = graph.markAsFinished( C ); + assertEquals( 2, cDescendants.size() ); + assertEquals( X, cDescendants.get( 0 ) ); + assertEquals( Z, cDescendants.get( 1 ) ); + } + + public void testConcurrencyGraphDifferentCompletionOrder() + throws InvalidPluginDescriptorException, PluginVersionResolutionException, PluginDescriptorParsingException, + NoPluginFoundForPrefixException, MojoNotFoundException, PluginNotFoundException, PluginResolutionException, + LifecyclePhaseNotFoundException, LifecycleNotFoundException + { + ProjectDependencyGraph dependencyGraph = new ProjectDependencyGraphStub(); + final MavenSession session = ProjectDependencyGraphStub.getMavenSession(); + ConcurrencyDependencyGraph graph = + new ConcurrencyDependencyGraph( getProjectBuildList( session ), dependencyGraph ); + + graph.markAsFinished( A ); + final List cDescendants = graph.markAsFinished( C ); + assertEquals( 1, cDescendants.size() ); + assertEquals( Z, cDescendants.get( 0 ) ); + + final List bDescendants = graph.markAsFinished( B ); + assertEquals( 2, bDescendants.size() ); + assertEquals( X, bDescendants.get( 0 ) ); + assertEquals( Y, bDescendants.get( 1 ) ); + } + +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ExecutionPlanItemTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ExecutionPlanItemTest.java new file mode 100644 index 0000000000..e8333501d1 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ExecutionPlanItemTest.java @@ -0,0 +1,67 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import junit.framework.TestCase; +import org.apache.maven.lifecycle.Schedule; +import org.apache.maven.lifecycle.internal.stub.MojoExecutorStub; +import org.apache.maven.plugin.MojoExecution; + +/** + * @author Kristian Rosenvold + */ +public class ExecutionPlanItemTest + extends TestCase +{ + + public void testSetComplete() + throws Exception + { + ExecutionPlanItem item = createExecutionPlanItem( "testMojo", null ); + item.setComplete(); // This itself is a valid test + assertFalse( item.ensureComplete() ); + } + + public void testWaitUntilDone() + throws Exception + { + + final ExecutionPlanItem item = + createExecutionPlanItem( "testMojo", createExecutionPlanItem( "testMojo2", null ) ); + new Thread( new Runnable() + { + public void run() + { + item.setComplete(); + } + } ).start(); + item.waitUntilDone(); + } + + + public static ExecutionPlanItem createExecutionPlanItem( String mojoDescription, ExecutionPlanItem downStream ) + { + return createExecutionPlanItem( mojoDescription, downStream, null ); + } + + public static ExecutionPlanItem createExecutionPlanItem( String mojoDescription, ExecutionPlanItem downStream, + Schedule schedule ) + { + return new ExecutionPlanItem( new MojoExecution( MojoExecutorStub.createMojoDescriptor( mojoDescription ) ), + schedule ); + } + + +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleExecutionPlanCalculatorTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleExecutionPlanCalculatorTest.java new file mode 100644 index 0000000000..a0c2fac6bb --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleExecutionPlanCalculatorTest.java @@ -0,0 +1,77 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal; + +import junit.framework.TestCase; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.lifecycle.internal.stub.BuildPluginManagerStub; +import org.apache.maven.lifecycle.internal.stub.DefaultLifecyclesStub; +import org.apache.maven.lifecycle.internal.stub.PluginPrefixResolverStub; +import org.apache.maven.lifecycle.internal.stub.PluginVersionResolverStub; +import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub; + +/** + * @author Kristian Rosenvold> + */ + +public class LifecycleExecutionPlanCalculatorTest + extends TestCase +{ + + public void testCalculateExecutionPlanWithGoalTasks() + throws Exception + { + MojoDescriptorCreator mojoDescriptorCreator = createMojoDescriptorCreator(); + LifecycleExecutionPlanCalculator lifecycleExecutionPlanCalculator = + createExecutionPlaceCalculator( mojoDescriptorCreator ); + + final GoalTask goalTask1 = new GoalTask( "compiler:compile" ); + final GoalTask goalTask2 = new GoalTask( "surefire:test" ); + final TaskSegment taskSegment1 = new TaskSegment( false, goalTask1, goalTask2 ); + final MavenSession session1 = ProjectDependencyGraphStub.getMavenSession( ProjectDependencyGraphStub.A ); + + MavenExecutionPlan executionPlan = + lifecycleExecutionPlanCalculator.calculateExecutionPlan( session1, ProjectDependencyGraphStub.A, + taskSegment1.getTasks() ); + assertEquals( 2, executionPlan.size() ); + + final GoalTask goalTask3 = new GoalTask( "surefire:test" ); + final TaskSegment taskSegment2 = new TaskSegment( false, goalTask1, goalTask2, goalTask3 ); + MavenExecutionPlan executionPlan2 = + lifecycleExecutionPlanCalculator.calculateExecutionPlan( session1, ProjectDependencyGraphStub.A, + taskSegment2.getTasks() ); + assertEquals( 3, executionPlan2.size() ); + } + + // Maybe also make one with LifeCycleTasks + + private LifecycleExecutionPlanCalculator createExecutionPlaceCalculator( + MojoDescriptorCreator mojoDescriptorCreator ) + { + LifecyclePluginResolver lifecyclePluginResolver = + new LifecyclePluginResolver( new PluginVersionResolverStub() ); + return new LifecycleExecutionPlanCalculatorImpl( new BuildPluginManagerStub(), + DefaultLifecyclesStub.createDefaultLifeCycles(), + mojoDescriptorCreator, lifecyclePluginResolver ); + } + + private MojoDescriptorCreator createMojoDescriptorCreator() + { + return new MojoDescriptorCreator( new PluginVersionResolverStub(), new BuildPluginManagerStub(), + new PluginPrefixResolverStub() ); + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleWeaveBuilderTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleWeaveBuilderTest.java new file mode 100644 index 0000000000..12c89f489a --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleWeaveBuilderTest.java @@ -0,0 +1,135 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import junit.framework.TestCase; +import org.apache.maven.execution.DefaultMavenExecutionResult; +import org.apache.maven.execution.MavenExecutionResult; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.LifecycleNotFoundException; +import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException; +import org.apache.maven.lifecycle.internal.stub.CompletionServiceStub; +import org.apache.maven.lifecycle.internal.stub.LifecycleExecutionPlanCalculatorStub; +import org.apache.maven.lifecycle.internal.stub.LoggerStub; +import org.apache.maven.lifecycle.internal.stub.MojoExecutorStub; +import org.apache.maven.lifecycle.internal.stub.ProjectDependenciesResolverStub; +import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub; +import org.apache.maven.plugin.InvalidPluginDescriptorException; +import org.apache.maven.plugin.MojoNotFoundException; +import org.apache.maven.plugin.PluginDescriptorParsingException; +import org.apache.maven.plugin.PluginNotFoundException; +import org.apache.maven.plugin.PluginResolutionException; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; + +import java.util.List; +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * @author Kristian Rosenvold> + */ +public class LifecycleWeaveBuilderTest + extends TestCase +{ + + public void testBuildProjectSynchronously() + throws Exception + { + final CompletionService service = new CompletionServiceStub( true ); + final ProjectBuildList projectBuildList = runWithCompletionService( service ); + assertEquals( "Expect all tasks to be scheduled", projectBuildList.size(), + ( (CompletionServiceStub) service ).size() ); + } + + public void testBuildProjectThreaded() + throws Exception + { + ExecutorService executor = Executors.newFixedThreadPool( 10 ); + ExecutorCompletionService service = new ExecutorCompletionService( executor ); + runWithCompletionService( service ); + executor.shutdown(); + } + + public void testBuildProjectThreadedAggressive() + throws Exception + { + ExecutorService executor = Executors.newFixedThreadPool( 10 ); + ExecutorCompletionService service = new ExecutorCompletionService( executor ); + runWithCompletionService( service ); + executor.shutdown(); + } + + private ProjectBuildList runWithCompletionService( CompletionService service ) + throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, + MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, + PluginVersionResolutionException, LifecyclePhaseNotFoundException, LifecycleNotFoundException, + ExecutionException, InterruptedException + { + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + try + { + BuildListCalculator buildListCalculator = BuildListCalculatorTest.createBuildListCalculator(); + final MavenSession session = ProjectDependencyGraphStub.getMavenSession(); + List taskSegments = buildListCalculator.calculateTaskSegments( session ); + ProjectBuildList projectBuildList = buildListCalculator.calculateProjectBuilds( session, taskSegments ); + + final MojoExecutorStub mojoExecutorStub = new MojoExecutorStub(); + final LifecycleWeaveBuilder builder = getWeaveBuilder( mojoExecutorStub ); + final ReactorContext buildContext = createBuildContext( session ); + ReactorBuildStatus reactorBuildStatus = new ReactorBuildStatus( session.getProjectDependencyGraph() ); + builder.build( projectBuildList, buildContext, taskSegments, session, service, reactorBuildStatus ); + + LifecycleExecutionPlanCalculatorStub lifecycleExecutionPlanCalculatorStub = + new LifecycleExecutionPlanCalculatorStub(); + final int expected = lifecycleExecutionPlanCalculatorStub.getNumberOfExceutions( projectBuildList ); + assertEquals( "All executions should be scheduled", expected, mojoExecutorStub.executions.size() ); + return projectBuildList; + } + finally + { + Thread.currentThread().setContextClassLoader( loader ); + } + } + + + private ReactorContext createBuildContext( MavenSession session ) + { + MavenExecutionResult mavenExecutionResult = new DefaultMavenExecutionResult(); + ReactorBuildStatus reactorBuildStatus = new ReactorBuildStatus( session.getProjectDependencyGraph() ); + return new ReactorContext( mavenExecutionResult, null, null, reactorBuildStatus ); + } + + private LifecycleWeaveBuilder getWeaveBuilder( MojoExecutor mojoExecutor ) + { + final BuilderCommon builderCommon = getBuilderCommon(); + final LoggerStub loggerStub = new LoggerStub(); + final LifecycleDependencyResolver lifecycleDependencyResolver = + new LifecycleDependencyResolver( new ProjectDependenciesResolverStub(), loggerStub ); + return new LifecycleWeaveBuilder( mojoExecutor, builderCommon, loggerStub, lifecycleDependencyResolver ); + + } + + private BuilderCommon getBuilderCommon() + { + final LifecycleDebugLogger logger = new LifecycleDebugLogger( new LoggerStub() ); + final LifecycleDependencyResolver lifecycleDependencyResolver = + new LifecycleDependencyResolver( new ProjectDependenciesResolverStub(), new LoggerStub() ); + return new BuilderCommon( logger, new LifecycleExecutionPlanCalculatorStub(), lifecycleDependencyResolver ); + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ProjectBuildListTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ProjectBuildListTest.java new file mode 100644 index 0000000000..8049237e17 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ProjectBuildListTest.java @@ -0,0 +1,41 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import junit.framework.TestCase; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub; + +/** + * @author Kristian Rosenvold + */ +public class ProjectBuildListTest + extends TestCase +{ + + public void testGetByTaskSegment() + throws Exception + { + final MavenSession session = ProjectDependencyGraphStub.getMavenSession(); + ProjectBuildList projectBuildList = ProjectDependencyGraphStub.getProjectBuildList( session ); + TaskSegment taskSegment = projectBuildList.get( 0 ).getTaskSegment(); + assertTrue( "This test assumes there are at least 6 elements in projectBuilds", projectBuildList.size() >= 6 ); + + final ProjectBuildList byTaskSegment = projectBuildList.getByTaskSegment( taskSegment ); + assertEquals( projectBuildList.size(), + byTaskSegment.size() ); // Todo: Make multiple segments on projectBuildList + } + +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ThreadConfigurationServiceTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ThreadConfigurationServiceTest.java new file mode 100644 index 0000000000..4681d1dae8 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ThreadConfigurationServiceTest.java @@ -0,0 +1,21 @@ +package org.apache.maven.lifecycle.internal; + +import junit.framework.Assert; +import junit.framework.TestCase; +import org.apache.maven.lifecycle.internal.stub.LoggerStub; + +/** + * @author Kristian Rosenvold + */ +public class ThreadConfigurationServiceTest + extends TestCase +{ + public void testGetThreadCount() + throws Exception + { + ThreadConfigurationService threadConfigurationService = new ThreadConfigurationService( new LoggerStub(), 3 ); + + Assert.assertEquals( 5, threadConfigurationService.getThreadCount( "1.75", true, 6 ).intValue() ); + Assert.assertEquals( 6, threadConfigurationService.getThreadCount( "1.84", true, 6 ).intValue() ); + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ThreadOutputMuxerTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ThreadOutputMuxerTest.java new file mode 100644 index 0000000000..40fcadfb1b --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ThreadOutputMuxerTest.java @@ -0,0 +1,163 @@ +/* + * 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. + */ +package org.apache.maven.lifecycle.internal; + +import junit.framework.TestCase; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.LifecycleNotFoundException; +import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException; +import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub; +import org.apache.maven.plugin.InvalidPluginDescriptorException; +import org.apache.maven.plugin.MojoNotFoundException; +import org.apache.maven.plugin.PluginDescriptorParsingException; +import org.apache.maven.plugin.PluginNotFoundException; +import org.apache.maven.plugin.PluginResolutionException; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + * @author Kristian Rosenvold + */ +public class ThreadOutputMuxerTest + extends TestCase +{ + + final String paid = "Paid"; + + final String in = "In"; + + final String full = "Full"; + + public void testSingleThreaded() + throws Exception + { + ProjectBuildList src = getProjectBuildList(); + ProjectBuildList projectBuildList = + new ProjectBuildList( Arrays.asList( src.get( 0 ), src.get( 1 ), src.get( 2 ) ) ); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + PrintStream systemOut = new PrintStream( byteArrayOutputStream ); + ThreadOutputMuxer threadOutputMuxer = new ThreadOutputMuxer( projectBuildList, systemOut ); + + threadOutputMuxer.associateThreadWithProjectSegment( projectBuildList.get( 0 ) ); + System.out.print( paid ); // No, this does not print to system.out. It's part of the test + assertEquals( paid.length(), byteArrayOutputStream.size() ); + threadOutputMuxer.associateThreadWithProjectSegment( projectBuildList.get( 1 ) ); + System.out.print( in ); // No, this does not print to system.out. It's part of the test + assertEquals( paid.length(), byteArrayOutputStream.size() ); + threadOutputMuxer.associateThreadWithProjectSegment( projectBuildList.get( 2 ) ); + System.out.print( full ); // No, this does not print to system.out. It's part of the test + assertEquals( paid.length(), byteArrayOutputStream.size() ); + + threadOutputMuxer.setThisModuleComplete( projectBuildList.get( 0 ) ); + threadOutputMuxer.setThisModuleComplete( projectBuildList.get( 1 ) ); + threadOutputMuxer.setThisModuleComplete( projectBuildList.get( 2 ) ); + threadOutputMuxer.close(); + assertEquals( ( paid + in + full ).length(), byteArrayOutputStream.size() ); + } + + public void testMultiThreaded() + throws Exception + { + ProjectBuildList projectBuildList = getProjectBuildList(); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + PrintStream systemOut = new PrintStream( byteArrayOutputStream ); + final ThreadOutputMuxer threadOutputMuxer = new ThreadOutputMuxer( projectBuildList, systemOut ); + + final List stringList = + Arrays.asList( "Thinkin", "of", "a", "master", "plan", "Cuz", "ain’t", "nuthin", "but", "sweat", "inside", + "my", "hand" ); + Iterator lyrics = stringList.iterator(); + List outputters = new ArrayList(); + + ExecutorService executor = Executors.newFixedThreadPool( 10 ); + CompletionService service = new ExecutorCompletionService( executor ); + + List> futures = new ArrayList>(); + for ( ProjectSegment projectBuild : projectBuildList ) + { + final Future buildFuture = + service.submit( new Outputter( threadOutputMuxer, projectBuild, lyrics.next() ) ); + futures.add( buildFuture ); + } + + for ( Future future : futures ) + { + future.get(); + } + int expectedLength = 0; + for ( int i = 0; i < projectBuildList.size(); i++ ) + { + expectedLength += stringList.get( i ).length(); + } + + threadOutputMuxer.close(); + final byte[] bytes = byteArrayOutputStream.toByteArray(); + String result = new String( bytes ); + assertEquals( result, expectedLength, bytes.length ); + + + } + + class Outputter + implements Callable + { + private final ThreadOutputMuxer threadOutputMuxer; + + private final ProjectSegment item; + + private final String response; + + Outputter( ThreadOutputMuxer threadOutputMuxer, ProjectSegment item, String response ) + { + this.threadOutputMuxer = threadOutputMuxer; + this.item = item; + this.response = response; + } + + public ProjectSegment call() + throws Exception + { + threadOutputMuxer.associateThreadWithProjectSegment( item ); + System.out.print( response ); + threadOutputMuxer.setThisModuleComplete( item ); + return item; + } + } + + + private ProjectBuildList getProjectBuildList() + throws InvalidPluginDescriptorException, PluginVersionResolutionException, PluginDescriptorParsingException, + NoPluginFoundForPrefixException, MojoNotFoundException, PluginNotFoundException, PluginResolutionException, + LifecyclePhaseNotFoundException, LifecycleNotFoundException + { + final MavenSession session = ProjectDependencyGraphStub.getMavenSession(); + return ProjectDependencyGraphStub.getProjectBuildList( session ); + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/AboutTheStubs.html b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/AboutTheStubs.html new file mode 100644 index 0000000000..4642e5040c --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/AboutTheStubs.html @@ -0,0 +1,36 @@ + + + + About these stubs + + +

Design

+These stubs can be thought of as hand-coded mock obects. They allow unit tests to test only specific +aspects of a component while ignoring others. + +These stubs form an internally consistent data-set that is not expected to change. They are +used to test the individual components in the lifecycle with data that has expected characteristics +and can be asserted as desired. + +You can change/extend these stubs, and tests should not be breaking too much, since most tests +assert using expected values from the stubs. Normally, when you try to use data from the stubs that +have not been properly populated, you'll get a nullpointer in your test and you then have to +identify which stub creates that specific piece of data. + +The most important stubs are: +LifecycleExecutionPlanCalculatorStub +ProjectDependencyGraphStub + +Since they define the primary structure of the project/build. + +The stubs define three top-level targets that are defined in LifecycleTaskSegmentCalculatorStub; +clean, aggr and install. "aggr" is an aggregating task while clean and install are lifecyclephases. +There will be three items in the task list for this dataset. + +The stubs also exist at different "levels", where one test might wire stubs into a specific live implementation. +In the next test that same "live implementation" will be used in a stub version instead. + +Not all live services have stubs, but can be added as needed. + + \ No newline at end of file diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java new file mode 100644 index 0000000000..4e738f791d --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java @@ -0,0 +1,67 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal.stub; + +import org.apache.maven.artifact.repository.RepositoryRequest; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Plugin; +import org.apache.maven.plugin.BuildPluginManager; +import org.apache.maven.plugin.InvalidPluginDescriptorException; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.MojoNotFoundException; +import org.apache.maven.plugin.PluginConfigurationException; +import org.apache.maven.plugin.PluginDescriptorParsingException; +import org.apache.maven.plugin.PluginManagerException; +import org.apache.maven.plugin.PluginNotFoundException; +import org.apache.maven.plugin.PluginResolutionException; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.codehaus.plexus.classworlds.realm.ClassRealm; + +/** + * @author Kristian Rosenvold + */ +public class BuildPluginManagerStub + implements BuildPluginManager +{ + + public PluginDescriptor loadPlugin( Plugin plugin, RepositoryRequest repositoryRequest ) + throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, + InvalidPluginDescriptorException + { + return null; + } + + public MojoDescriptor getMojoDescriptor( Plugin plugin, String goal, RepositoryRequest repositoryRequest ) + throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, + MojoNotFoundException, InvalidPluginDescriptorException + { + return MojoExecutorStub.createMojoDescriptor( plugin.getKey() ); + } + + public ClassRealm getPluginRealm( MavenSession session, PluginDescriptor pluginDescriptor ) + throws PluginResolutionException, PluginManagerException + { + return null; + } + + public void executeMojo( MavenSession session, MojoExecution execution ) + throws MojoFailureException, MojoExecutionException, PluginConfigurationException, PluginManagerException + { + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/CompletionServiceStub.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/CompletionServiceStub.java new file mode 100644 index 0000000000..10a113e94f --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/CompletionServiceStub.java @@ -0,0 +1,89 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal.stub; + +import org.apache.maven.lifecycle.internal.ProjectSegment; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionService; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; + +/** + * @author Kristian Rosenvold + */ +public class CompletionServiceStub + implements CompletionService +{ + List> projectBuildFutureTasks = + Collections.synchronizedList( new ArrayList>() ); + + final boolean finishImmediately; + + + public int size() + { + return projectBuildFutureTasks.size(); + } + + public CompletionServiceStub( boolean finishImmediately ) + { + this.finishImmediately = finishImmediately; + } + + public Future submit( Callable task ) + { + FutureTask projectBuildFutureTask = new FutureTask( task ); + projectBuildFutureTasks.add( projectBuildFutureTask ); + if ( finishImmediately ) + { + projectBuildFutureTask.run(); + } + return projectBuildFutureTask; + } + + public Future submit( Runnable task, ProjectSegment result ) + { + FutureTask projectBuildFutureTask = new FutureTask( task, result ); + projectBuildFutureTasks.add( projectBuildFutureTask ); + if ( finishImmediately ) + { + projectBuildFutureTask.run(); + } + return projectBuildFutureTask; + } + + public Future take() + throws InterruptedException + { + return null; + } + + public Future poll() + { + return null; + } + + public Future poll( long timeout, TimeUnit unit ) + throws InterruptedException + { + return null; + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/DefaultLifecyclesStub.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/DefaultLifecyclesStub.java new file mode 100644 index 0000000000..e274325a95 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/DefaultLifecyclesStub.java @@ -0,0 +1,56 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal.stub; + +import org.apache.maven.lifecycle.DefaultLifecycles; +import org.apache.maven.lifecycle.Lifecycle; +import org.apache.maven.lifecycle.Schedule; +import org.apache.maven.lifecycle.Scheduling; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Kristian Rosenvold + */ + +public class DefaultLifecyclesStub +{ + public static DefaultLifecycles createDefaultLifeCycles() + { + final Lifecycle lifecycle1 = new Lifecycle( "abc", Arrays.asList( "compile" ), null ); + final Lifecycle lifecycle2 = new Lifecycle( "abc", Arrays.asList( "test" ), null ); + final List lifeCycles = Arrays.asList( lifecycle1, lifecycle2 ); + final List schedulingList = getSchedulingList(); + final DefaultLifecycles defaultLifecycles = new DefaultLifecycles( lifeCycles, schedulingList ); + try + { + defaultLifecycles.initialize(); + } + catch ( InitializationException e ) + { + throw new RuntimeException( e ); + } + return defaultLifecycles; + } + + public static List getSchedulingList() + { + return Arrays.asList( new Scheduling( "default", Arrays.asList( new Schedule( "compile", false, false ), + new Schedule( "test", false, true ) ) ) ); + } +} \ No newline at end of file diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/LifeCyclePluginAnalyzerStub.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/LifeCyclePluginAnalyzerStub.java new file mode 100644 index 0000000000..930f5e9315 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/LifeCyclePluginAnalyzerStub.java @@ -0,0 +1,74 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal.stub; + +import org.apache.maven.lifecycle.LifeCyclePluginAnalyzer; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginExecution; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * @author Kristian Rosenvold + */ +public class LifeCyclePluginAnalyzerStub + implements LifeCyclePluginAnalyzer +{ + public Set getPluginsBoundByDefaultToAllLifecycles( String packaging ) + { + Set plugins; + + // NOTE: The upper-case packaging name is intentional, that's a special hinting mode used for certain tests + if ( "JAR".equals( packaging ) ) + { + plugins = new LinkedHashSet(); + + plugins.add( newPlugin( "maven-compiler-plugin", "compile", "testCompile" ) ); + plugins.add( newPlugin( "maven-resources-plugin", "resources", "testResources" ) ); + plugins.add( newPlugin( "maven-surefire-plugin", "test" ) ); + plugins.add( newPlugin( "maven-jar-plugin", "jar" ) ); + plugins.add( newPlugin( "maven-install-plugin", "install" ) ); + plugins.add( newPlugin( "maven-deploy-plugin", "deploy" ) ); + } + else + { + plugins = Collections.emptySet(); + } + + return plugins; + } + + private Plugin newPlugin( String artifactId, String... goals ) + { + Plugin plugin = new Plugin(); + + plugin.setGroupId( "org.apache.maven.plugins" ); + plugin.setArtifactId( artifactId ); + + for ( String goal : goals ) + { + PluginExecution pluginExecution = new PluginExecution(); + pluginExecution.setId( "default-" + goal ); + pluginExecution.addGoal( goal ); + plugin.addExecution( pluginExecution ); + } + + return plugin; + } + +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/LifecycleExecutionPlanCalculatorStub.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/LifecycleExecutionPlanCalculatorStub.java new file mode 100644 index 0000000000..bdd4aa30a7 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/LifecycleExecutionPlanCalculatorStub.java @@ -0,0 +1,171 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal.stub; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.LifecycleNotFoundException; +import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.lifecycle.internal.ExecutionPlanItem; +import org.apache.maven.lifecycle.internal.LifecycleExecutionPlanCalculator; +import org.apache.maven.lifecycle.internal.ProjectBuildList; +import org.apache.maven.lifecycle.internal.ProjectSegment; +import org.apache.maven.model.Plugin; +import org.apache.maven.plugin.InvalidPluginDescriptorException; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.MojoNotFoundException; +import org.apache.maven.plugin.PluginDescriptorParsingException; +import org.apache.maven.plugin.PluginNotFoundException; +import org.apache.maven.plugin.PluginResolutionException; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Kristian Rosenvold + */ +public class LifecycleExecutionPlanCalculatorStub + implements LifecycleExecutionPlanCalculator +{ + public final static MojoDescriptor CLEAN = createMojoDescriptor( "clean" ); + + public final static MojoDescriptor VALIDATE = createMojoDescriptor( "validate" ); + + public final static MojoDescriptor TEST_COMPILE = createMojoDescriptor( "test-compile" ); + + public final static MojoDescriptor PROCESS_TEST_RESOURCES = createMojoDescriptor( "process-test-resources" ); + + public final static MojoDescriptor PROCESS_RESOURCES = createMojoDescriptor( "process-resources" ); + + public final static MojoDescriptor COMPILE = createMojoDescriptor( "compile" ); + + public final static MojoDescriptor TEST = createMojoDescriptor( "test" ); + + public final static MojoDescriptor PACKAGE = createMojoDescriptor( "package" ); + + public final static MojoDescriptor INSTALL = createMojoDescriptor( "install" ); + + public int getNumberOfExceutions( ProjectBuildList projectBuildList ) + throws InvalidPluginDescriptorException, PluginVersionResolutionException, PluginDescriptorParsingException, + NoPluginFoundForPrefixException, MojoNotFoundException, PluginNotFoundException, PluginResolutionException, + LifecyclePhaseNotFoundException, LifecycleNotFoundException + { + int result = 0; + for ( ProjectSegment projectBuild : projectBuildList ) + { + MavenExecutionPlan plan = calculateExecutionPlan( projectBuild.getSession(), projectBuild.getProject(), + projectBuild.getTaskSegment().getTasks() ); + result += plan.size(); + } + return result; + } + + public MavenExecutionPlan calculateExecutionPlan( MavenSession session, MavenProject project, List tasks ) + throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException, + PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException, + NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException + { + if ( project.equals( ProjectDependencyGraphStub.A ) ) + { + return getProjectAExceutionPlan(); + } + if ( project.equals( ProjectDependencyGraphStub.B ) ) + { + return getProjectBExecutionPlan(); + } + // The remaining are basically "for future expansion" + List me = new ArrayList(); + me.add( createMojoExecution( new Plugin(), "resources", "default-resources", PROCESS_RESOURCES ) ); + me.add( createMojoExecution( new Plugin(), "compile", "default-compile", COMPILE ) ); + return new MavenExecutionPlan( getScopes(), getScopes(), + DefaultLifecyclesStub.createDefaultLifeCycles().createExecutionPlanItem( project, + me ) ); + } + + public static MavenExecutionPlan getProjectAExceutionPlan() + throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException, + PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException, + NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException + { + List me = new ArrayList(); + me.add( createMojoExecution( new Plugin(), "enforce", "enforce-versions", VALIDATE ) ); + me.add( createMojoExecution( new Plugin(), "resources", "default-resources", PROCESS_RESOURCES ) ); + me.add( createMojoExecution( new Plugin(), "compile", "default-compile", COMPILE ) ); + me.add( createMojoExecution( new Plugin(), "testResources", "default-testResources", PROCESS_TEST_RESOURCES ) ); + me.add( createMojoExecution( new Plugin(), "testCompile", "default-testCompile", TEST_COMPILE ) ); + me.add( createMojoExecution( new Plugin(), "test", "default-test", TEST ) ); + me.add( createMojoExecution( new Plugin(), "war", "default-war", PACKAGE ) ); + me.add( createMojoExecution( new Plugin(), "install", "default-install", INSTALL ) ); + final List executionPlanItem = + DefaultLifecyclesStub.createDefaultLifeCycles().createExecutionPlanItem( + ProjectDependencyGraphStub.A.getExecutionProject(), me ); + return new MavenExecutionPlan( getScopes(), getScopes(), executionPlanItem ); + } + + public static MavenExecutionPlan getProjectBExecutionPlan() + throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException, + PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException, + NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException + { + List me = new ArrayList(); + me.add( createMojoExecution( new Plugin(), "enforce", "enforce-versions", VALIDATE ) ); + me.add( createMojoExecution( new Plugin(), "resources", "default-resources", PROCESS_RESOURCES ) ); + me.add( createMojoExecution( new Plugin(), "compile", "default-compile", COMPILE ) ); + me.add( createMojoExecution( new Plugin(), "testResources", "default-testResources", PROCESS_TEST_RESOURCES ) ); + me.add( createMojoExecution( new Plugin(), "testCompile", "default-testCompile", TEST_COMPILE ) ); + me.add( createMojoExecution( new Plugin(), "test", "default-test", TEST ) ); + final List planItem = + DefaultLifecyclesStub.createDefaultLifeCycles().createExecutionPlanItem( + ProjectDependencyGraphStub.B.getExecutionProject(), me ); + return new MavenExecutionPlan( getScopes(), getScopes(), planItem ); + } + + private static MojoExecution createMojoExecution( Plugin plugin, String goal, String executionId, + MojoDescriptor mojoDescriptor ) + { + MojoExecution result = new MojoExecution( plugin, goal, executionId ); + result.setConfiguration( new Xpp3Dom( executionId + "-" + goal ) ); + result.setMojoDescriptor( mojoDescriptor ); + return result; + + } + + public static MojoDescriptor createMojoDescriptor( String phaseName ) + { + final MojoDescriptor mojoDescriptor = new MojoDescriptor(); + mojoDescriptor.setPhase( phaseName ); + final PluginDescriptor descriptor = new PluginDescriptor(); + descriptor.setArtifactId( "artifact." + phaseName ); + mojoDescriptor.setPluginDescriptor( descriptor ); + return mojoDescriptor; + } + + + public static Set getScopes() + { + return new HashSet( Arrays.asList( "compile" ) ); + } + +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/LifecycleTaskSegmentCalculatorStub.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/LifecycleTaskSegmentCalculatorStub.java new file mode 100644 index 0000000000..1a3498dcd5 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/LifecycleTaskSegmentCalculatorStub.java @@ -0,0 +1,89 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal.stub; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.internal.GoalTask; +import org.apache.maven.lifecycle.internal.LifecycleTask; +import org.apache.maven.lifecycle.internal.LifecycleTaskSegmentCalculator; +import org.apache.maven.lifecycle.internal.TaskSegment; +import org.apache.maven.plugin.InvalidPluginDescriptorException; +import org.apache.maven.plugin.MojoNotFoundException; +import org.apache.maven.plugin.PluginDescriptorParsingException; +import org.apache.maven.plugin.PluginNotFoundException; +import org.apache.maven.plugin.PluginResolutionException; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Kristian Rosenvold + */ + +public class LifecycleTaskSegmentCalculatorStub + implements LifecycleTaskSegmentCalculator +{ + public static final String clean = "clean"; + + public static final String aggr = "aggr"; + + public static final String install = "install"; + + public List calculateTaskSegments( MavenSession session, List tasks ) + throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, + MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, + PluginVersionResolutionException + { + List taskSegments = new ArrayList( tasks.size() ); + + TaskSegment currentSegment = null; + + for ( String task : tasks ) + { + if ( aggr.equals( task ) ) + { + boolean aggregating = true; + + if ( currentSegment == null || currentSegment.isAggregating() != aggregating ) + { + currentSegment = new TaskSegment( aggregating ); + taskSegments.add( currentSegment ); + } + + currentSegment.getTasks().add( new GoalTask( task ) ); + } + else + { + // lifecycle phase + if ( currentSegment == null || currentSegment.isAggregating() ) + { + currentSegment = new TaskSegment( false ); + taskSegments.add( currentSegment ); + } + currentSegment.getTasks().add( new LifecycleTask( task ) ); + } + } + + return taskSegments; + } + + public boolean requiresProject( MavenSession session ) + { + return true; + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/LoggerStub.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/LoggerStub.java new file mode 100644 index 0000000000..00bb3cfa9f --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/LoggerStub.java @@ -0,0 +1,110 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal.stub; + +import org.codehaus.plexus.logging.Logger; + +/** + * @author Kristian Rosenvold + */ +public class LoggerStub + implements Logger +{ + public void debug( String s ) + { + } + + public void debug( String s, Throwable throwable ) + { + } + + public boolean isDebugEnabled() + { + return true; + } + + public void info( String s ) + { + } + + public void info( String s, Throwable throwable ) + { + } + + public boolean isInfoEnabled() + { + return true; + } + + public void warn( String s ) + { + } + + public void warn( String s, Throwable throwable ) + { + } + + public boolean isWarnEnabled() + { + return true; + } + + public void error( String s ) + { + } + + public void error( String s, Throwable throwable ) + { + } + + public boolean isErrorEnabled() + { + return true; + } + + public void fatalError( String s ) + { + } + + public void fatalError( String s, Throwable throwable ) + { + } + + public boolean isFatalErrorEnabled() + { + return true; + } + + public Logger getChildLogger( String s ) + { + return null; + } + + public int getThreshold() + { + return 0; + } + + public void setThreshold( int i ) + { + + } + + public String getName() + { + return "StubLogger"; + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/MojoExecutorStub.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/MojoExecutorStub.java new file mode 100644 index 0000000000..64f3e6c206 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/MojoExecutorStub.java @@ -0,0 +1,71 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal.stub; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.LifecycleExecutionException; +import org.apache.maven.lifecycle.internal.DependencyContext; +import org.apache.maven.lifecycle.internal.MojoExecutor; +import org.apache.maven.lifecycle.internal.PhaseRecorder; +import org.apache.maven.lifecycle.internal.ProjectIndex; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.PluginDescriptor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author Kristian Rosenvold + */ +public class MojoExecutorStub + extends MojoExecutor +{ // This is being lazy instead of making interface + + public List executions = Collections.synchronizedList( new ArrayList() ); + + @Override + public void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex, + DependencyContext dependencyContext, PhaseRecorder phaseRecorder ) + throws LifecycleExecutionException + { + executions.add( mojoExecution ); + } + + @Override + public void execute( MavenSession session, List mojoExecutions, ProjectIndex projectIndex, + DependencyContext dependencyContext ) + throws LifecycleExecutionException + { + for ( MojoExecution mojoExecution : mojoExecutions ) + { + executions.add( mojoExecution ); + } + } + + + public static MojoDescriptor createMojoDescriptor( String mojoDescription ) + { + final PluginDescriptor descriptor = new PluginDescriptor(); + descriptor.setArtifactId( mojoDescription ); + final MojoDescriptor mojoDescriptor = new MojoDescriptor(); + mojoDescriptor.setDescription( mojoDescription ); + mojoDescriptor.setPluginDescriptor( descriptor ); + return mojoDescriptor; + } + +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/PluginPrefixResolverStub.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/PluginPrefixResolverStub.java new file mode 100644 index 0000000000..7198fd29a2 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/PluginPrefixResolverStub.java @@ -0,0 +1,52 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal.stub; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.prefix.PluginPrefixRequest; +import org.apache.maven.plugin.prefix.PluginPrefixResolver; +import org.apache.maven.plugin.prefix.PluginPrefixResult; + +/** + * @author Kristian Rosenvold + */ + +public class PluginPrefixResolverStub + implements PluginPrefixResolver +{ + public PluginPrefixResult resolve( PluginPrefixRequest request ) + throws NoPluginFoundForPrefixException + { + return new PluginPrefixResult() + { + public String getGroupId() + { + return "com.foobar"; + } + + public String getArtifactId() + { + return "bazbaz"; + } + + public ArtifactRepository getRepository() + { + return null; + } + }; + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/PluginVersionResolverStub.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/PluginVersionResolverStub.java new file mode 100644 index 0000000000..69f3e0b55f --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/PluginVersionResolverStub.java @@ -0,0 +1,48 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal.stub; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.plugin.version.PluginVersionRequest; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.plugin.version.PluginVersionResolver; +import org.apache.maven.plugin.version.PluginVersionResult; + +/** + * @author Kristian Rosenvold + */ + +public class PluginVersionResolverStub + implements PluginVersionResolver +{ + + public PluginVersionResult resolve( PluginVersionRequest request ) + throws PluginVersionResolutionException + { + return new PluginVersionResult() + { + public String getVersion() + { + return "0.42"; + } + + public ArtifactRepository getRepository() + { + return null; + } + }; + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/ProjectDependenciesResolverStub.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/ProjectDependenciesResolverStub.java new file mode 100644 index 0000000000..2382661488 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/ProjectDependenciesResolverStub.java @@ -0,0 +1,54 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal.stub; + +import org.apache.maven.ProjectDependenciesResolver; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Kristian Rosenvold + */ +public class ProjectDependenciesResolverStub + implements ProjectDependenciesResolver +{ + public Set resolve( MavenProject project, Collection scopesToResolve, MavenSession session ) + throws ArtifactResolutionException, ArtifactNotFoundException + { + return new HashSet(); + } + + public Set resolve( MavenProject project, Collection scopesToCollect, + Collection scopesToResolve, MavenSession session ) + throws ArtifactResolutionException, ArtifactNotFoundException + { + return new HashSet(); + } + + public Set resolve( Collection projects, Collection scopes, + MavenSession session ) + throws ArtifactResolutionException, ArtifactNotFoundException + { + return new HashSet(); + } +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/ProjectDependencyGraphStub.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/ProjectDependencyGraphStub.java new file mode 100644 index 0000000000..6ffcd0438f --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/ProjectDependencyGraphStub.java @@ -0,0 +1,226 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal.stub; + +import org.apache.maven.execution.AbstractExecutionListener; +import org.apache.maven.execution.DefaultMavenExecutionRequest; +import org.apache.maven.execution.DefaultMavenExecutionResult; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.execution.ProjectDependencyGraph; +import org.apache.maven.lifecycle.LifecycleNotFoundException; +import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException; +import org.apache.maven.lifecycle.internal.GoalTask; +import org.apache.maven.lifecycle.internal.ProjectBuildList; +import org.apache.maven.lifecycle.internal.ProjectSegment; +import org.apache.maven.lifecycle.internal.TaskSegment; +import org.apache.maven.plugin.InvalidPluginDescriptorException; +import org.apache.maven.plugin.MojoNotFoundException; +import org.apache.maven.plugin.PluginDescriptorParsingException; +import org.apache.maven.plugin.PluginNotFoundException; +import org.apache.maven.plugin.PluginResolutionException; +import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.project.MavenProject; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A stub dependency graph that is custom made for testing concurrent build graph evaluations. + *

+ * Implements a graph as follows: + * A has no dependencies + * B depends on A + * C depends on A + * X depends on B & C + * Y depends on B + * Z depends on C + * + * @author Kristian Rosenvold + */ +public class ProjectDependencyGraphStub + implements ProjectDependencyGraph +{ + public static final MavenProject A = new MavenProject(); + + public static final MavenProject B = new MavenProject(); + + public static final MavenProject C = new MavenProject(); + + public static final MavenProject X = new MavenProject(); + + public static final MavenProject Y = new MavenProject(); + + public static final MavenProject Z = new MavenProject(); + + public static final MavenProject UNKNOWN = new MavenProject(); + + static + { + A.setArtifactId( "A" ); + B.setArtifactId( "B" ); + C.setArtifactId( "C" ); + X.setArtifactId( "X" ); + Y.setArtifactId( "Y" ); + Z.setArtifactId( "Z" ); + } + + // This should probably be moved to a separate stub + + public static ProjectBuildList getProjectBuildList( MavenSession session ) + throws InvalidPluginDescriptorException, PluginVersionResolutionException, PluginDescriptorParsingException, + NoPluginFoundForPrefixException, MojoNotFoundException, PluginNotFoundException, PluginResolutionException, + LifecyclePhaseNotFoundException, LifecycleNotFoundException + { + final List list = getProjectBuilds( session ); + return new ProjectBuildList( list ); + + } + + public static List getProjectBuilds( MavenSession session ) + throws InvalidPluginDescriptorException, PluginVersionResolutionException, PluginDescriptorParsingException, + NoPluginFoundForPrefixException, PluginNotFoundException, MojoNotFoundException, PluginResolutionException, + LifecyclePhaseNotFoundException, LifecycleNotFoundException + { + List projectBuilds = new ArrayList(); + + TaskSegment segment = createTaskSegment(); + projectBuilds.add( createProjectBuild( A, session, segment ) ); + projectBuilds.add( createProjectBuild( B, session, segment ) ); + projectBuilds.add( createProjectBuild( C, session, segment ) ); + projectBuilds.add( createProjectBuild( X, session, segment ) ); + projectBuilds.add( createProjectBuild( Y, session, segment ) ); + projectBuilds.add( createProjectBuild( Z, session, segment ) ); + return projectBuilds; + } + + private static ProjectSegment createProjectBuild( MavenProject project, MavenSession session, + TaskSegment taskSegment ) + throws InvalidPluginDescriptorException, PluginVersionResolutionException, PluginDescriptorParsingException, + NoPluginFoundForPrefixException, MojoNotFoundException, PluginNotFoundException, PluginResolutionException, + LifecyclePhaseNotFoundException, LifecycleNotFoundException + { + final MavenSession session1 = session.clone(); + return new ProjectSegment( project, taskSegment, session1 ); + } + + + private static TaskSegment createTaskSegment() + { + TaskSegment result = new TaskSegment( false ); + result.getTasks().add( new GoalTask( "t1" ) ); + result.getTasks().add( new GoalTask( "t2" ) ); + return result; + } + + class Dependency + { + MavenProject dependant; + + MavenProject dependency; + + Dependency( MavenProject dependant, MavenProject dependency ) + { + this.dependant = dependant; + this.dependency = dependency; + } + + void addIfDownstream( MavenProject mavenProject, List result ) + { + if ( dependency == mavenProject ) + { + result.add( dependant ); + } + } + + void addIfUpstreamOf( MavenProject mavenProject, List result ) + { + if ( dependant == mavenProject ) + { + result.add( dependency ); // All projects are the statics from this class + } + } + } + + private List getDependencies() + { + List dependencies = new ArrayList(); + dependencies.add( new Dependency( B, A ) ); + dependencies.add( new Dependency( C, A ) ); + dependencies.add( new Dependency( X, B ) ); + dependencies.add( new Dependency( X, C ) ); + dependencies.add( new Dependency( Y, B ) ); + dependencies.add( new Dependency( Z, C ) ); + return dependencies; + } + + public List getSortedProjects() + { + return Arrays.asList( A, B, C, X, Y, Z ); // I'm not entirely sure about the order but this shold do... + } + + public List getDownstreamProjects( MavenProject project, boolean transitive ) + { + if ( transitive ) + { + throw new RuntimeException( "Not implemented yet" ); + } + List result = new ArrayList(); + for ( Dependency dependency : getDependencies() ) + { + dependency.addIfDownstream( project, result ); + } + return result; + } + + public List getUpstreamProjects( MavenProject project, boolean transitive ) + { + if ( transitive ) + { + throw new RuntimeException( "Not implemented yet" ); + } + List result = new ArrayList(); + final List dependencies = getDependencies(); + for ( Dependency dependency : dependencies ) + { + dependency.addIfUpstreamOf( project, result ); + } + return result; + } + + public static MavenSession getMavenSession( MavenProject mavenProject ) + { + final MavenSession session = getMavenSession(); + session.setCurrentProject( mavenProject ); + return session; + } + + public static MavenSession getMavenSession() + { + final DefaultMavenExecutionResult defaultMavenExecutionResult = new DefaultMavenExecutionResult(); + MavenExecutionRequest mavenExecutionRequest = new DefaultMavenExecutionRequest(); + mavenExecutionRequest.setExecutionListener( new AbstractExecutionListener() ); + mavenExecutionRequest.setGoals( Arrays.asList( "clean", "aggr", "install" ) ); + final MavenSession session = new MavenSession( null, mavenExecutionRequest, defaultMavenExecutionResult ); + final ProjectDependencyGraphStub dependencyGraphStub = new ProjectDependencyGraphStub(); + session.setProjectDependencyGraph( dependencyGraphStub ); + session.setProjects( dependencyGraphStub.getSortedProjects() ); + return session; + } + +} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/ProjectDependencyGraphStubTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/ProjectDependencyGraphStubTest.java new file mode 100644 index 0000000000..8c60572343 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/ProjectDependencyGraphStubTest.java @@ -0,0 +1,61 @@ +/* + * 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. + */ + +package org.apache.maven.lifecycle.internal.stub; + +import junit.framework.TestCase; +import org.apache.maven.project.MavenProject; + +import java.util.List; + + +/** + * Tests the stub. Yeah, I know. + * + * @author Kristian Rosenvold + */ + +public class ProjectDependencyGraphStubTest + extends TestCase +{ + public void testADependencies() + { + ProjectDependencyGraphStub stub = new ProjectDependencyGraphStub(); + final List mavenProjects = stub.getUpstreamProjects( ProjectDependencyGraphStub.A, false ); + assertEquals( 0, mavenProjects.size() ); + } + + public void testBDepenencies( ProjectDependencyGraphStub stub ) + { + final List bProjects = stub.getUpstreamProjects( ProjectDependencyGraphStub.B, false ); + assertEquals( 1, bProjects.size() ); + assertTrue( bProjects.contains( ProjectDependencyGraphStub.A ) ); + } + + public void testCDepenencies( ProjectDependencyGraphStub stub ) + { + final List cProjects = stub.getUpstreamProjects( ProjectDependencyGraphStub.C, false ); + assertEquals( 1, cProjects.size() ); + assertTrue( cProjects.contains( ProjectDependencyGraphStub.C ) ); + } + + public void testXDepenencies( ProjectDependencyGraphStub stub ) + { + final List cProjects = stub.getUpstreamProjects( ProjectDependencyGraphStub.X, false ); + assertEquals( 2, cProjects.size() ); + assertTrue( cProjects.contains( ProjectDependencyGraphStub.C ) ); + assertTrue( cProjects.contains( ProjectDependencyGraphStub.B ) ); + } +} diff --git a/maven-core/src/test/java/org/apache/maven/project/EmptyLifecycleExecutor.java b/maven-core/src/test/java/org/apache/maven/project/EmptyLifecycleExecutor.java index 135b3e868e..70ab6c9274 100644 --- a/maven-core/src/test/java/org/apache/maven/project/EmptyLifecycleExecutor.java +++ b/maven-core/src/test/java/org/apache/maven/project/EmptyLifecycleExecutor.java @@ -55,7 +55,7 @@ public class EmptyLifecycleExecutor throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, MojoNotFoundException { - return new MavenExecutionPlan( Collections. emptyList(), null, null ); + return new MavenExecutionPlan(null, null, null ); } public void execute( MavenSession session ) diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java b/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java index f80564041e..61971b5066 100644 --- a/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java +++ b/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java @@ -98,6 +98,8 @@ public class CLIManager public static final String ENCRYPT_PASSWORD = "ep"; + public static final String THREADS = "T"; + private Options options; @SuppressWarnings("static-access") @@ -136,7 +138,8 @@ public class CLIManager options.addOption( OptionBuilder.withLongOpt( "show-version" ).withDescription( "Display version information WITHOUT stopping build" ).create( SHOW_VERSION ) ); options.addOption( OptionBuilder.withLongOpt( "encrypt-master-password" ).hasArg().withDescription( "Encrypt master security password" ).create( ENCRYPT_MASTER_PASSWORD ) ); options.addOption( OptionBuilder.withLongOpt( "encrypt-password" ).hasArg().withDescription( "Encrypt server password" ).create( ENCRYPT_PASSWORD ) ); - + options.addOption( OptionBuilder.withLongOpt( "threads" ).hasArg().withDescription( "Thread count, for instance 2.0C where C is core multiplied" ).create( THREADS ) ); + // Adding this back in for compatibility with the verifier that hard codes this option. options.addOption( OptionBuilder.withLongOpt( "no-plugin-registry" ).withDescription( "Ineffective, only kept for backward compatibility" ).create( "npr" ) ); diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/ExecutionEventLogger.java b/maven-embedder/src/main/java/org/apache/maven/cli/ExecutionEventLogger.java index a517e166c8..817b7080ba 100644 --- a/maven-embedder/src/main/java/org/apache/maven/cli/ExecutionEventLogger.java +++ b/maven-embedder/src/main/java/org/apache/maven/cli/ExecutionEventLogger.java @@ -202,7 +202,9 @@ class ExecutionEventLogger long time = finish.getTime() - session.getRequest().getStartTime().getTime(); - logger.info( "Total time: " + getFormattedTime( time ) ); + String wallClock = session.getRequest().isThreadConfigurationPresent() ? " (Wall Clock)" : ""; + + logger.info( "Total time: " + getFormattedTime( time ) + wallClock); logger.info( "Finished at: " + finish ); diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java index c8577e1d7f..d8825869c0 100644 --- a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java +++ b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java @@ -37,6 +37,7 @@ import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenExecutionRequestPopulator; import org.apache.maven.execution.MavenExecutionResult; import org.apache.maven.lifecycle.LifecycleExecutionException; +import org.apache.maven.lifecycle.internal.LifecycleWeaveBuilder; import org.apache.maven.model.building.ModelProcessor; import org.apache.maven.project.MavenProject; import org.apache.maven.properties.internal.EnvironmentUtils; @@ -69,6 +70,8 @@ public class MavenCli { public static final String LOCAL_REPO_PROPERTY = "maven.repo.local"; + public static final String THREADS_DEPRECATED = "maven.threads.experimental"; + public static final String userHome = System.getProperty( "user.home" ); public static final File userMavenConfigurationHome = new File( userHome, ".m2" ); @@ -863,6 +866,21 @@ public class MavenCli request.setLocalRepositoryPath( localRepoProperty ); } + + final String threadConfiguration = commandLine.hasOption( CLIManager.THREADS ) ? + commandLine.getOptionValue( CLIManager.THREADS) : + request.getSystemProperties().getProperty(MavenCli.THREADS_DEPRECATED); // TODO: Remove this setting. Note that the int-tests use it + + if (threadConfiguration != null){ + request.setPerCoreThreadCount( threadConfiguration.contains("C")); + if (threadConfiguration.contains("W")) + { + LifecycleWeaveBuilder.setWeaveMode(request.getUserProperties()); + } + request.setThreadCount(threadConfiguration.replace("C", "").replace("W", "").replace("auto", "")); + } + + return request; }