org.apache.maven.DefaultMaven Maven / Gradle / Ivy
package org.apache.maven;
/*
* Copyright 2001-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.apache.maven.artifact.manager.WagonManager;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionResponse;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ReactorManager;
import org.apache.maven.execution.RuntimeInformation;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.lifecycle.LifecycleExecutor;
import org.apache.maven.model.Profile;
import org.apache.maven.monitor.event.EventDispatcher;
import org.apache.maven.monitor.event.MavenEvents;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.profiles.ProfileManager;
import org.apache.maven.profiles.activation.ProfileActivationException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.reactor.ReactorException;
import org.apache.maven.settings.Mirror;
import org.apache.maven.settings.Proxy;
import org.apache.maven.settings.Server;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.SettingsUtils;
import org.apache.maven.usability.DiagnosisUtils;
import org.apache.maven.usability.ErrorDiagnoser;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.dag.CycleDetectedException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @author Jason van Zyl
* @version $Id: DefaultMaven.java 293504 2005-10-04 03:31:35Z jvanzyl $
* @todo unify error reporting. We should return one response, always - and let the CLI decide how to render it. The reactor response should contain individual project responses
*/
public class DefaultMaven
extends AbstractLogEnabled
implements Maven, Contextualizable
{
public static File userDir = new File( System.getProperty( "user.dir" ) );
// ----------------------------------------------------------------------
// Components
// ----------------------------------------------------------------------
protected MavenProjectBuilder projectBuilder;
protected LifecycleExecutor lifecycleExecutor;
protected PlexusContainer container;
protected Map errorDiagnosers;
protected RuntimeInformation runtimeInformation;
private static final long MB = 1024 * 1024;
private static final int MS_PER_SEC = 1000;
private static final int SEC_PER_MIN = 60;
// ----------------------------------------------------------------------
// Project execution
// ----------------------------------------------------------------------
public MavenExecutionResponse execute( MavenExecutionRequest request )
throws ReactorException
{
if ( request.getSettings().isOffline() )
{
getLogger().info( DiagnosisUtils.getOfflineWarning() );
WagonManager wagonManager = null;
try
{
wagonManager = (WagonManager) container.lookup( WagonManager.ROLE );
wagonManager.setOnline( false );
}
catch ( ComponentLookupException e )
{
throw new ReactorException( "Cannot retrieve WagonManager in order to set offline mode.", e );
}
finally
{
try
{
container.release( wagonManager );
}
catch ( ComponentLifecycleException e )
{
getLogger().warn( "Cannot release WagonManager.", e );
}
}
}
try
{
resolveParameters( request.getSettings() );
}
catch ( ComponentLookupException e )
{
throw new ReactorException( "Unable to configure Maven for execution", e );
}
catch ( ComponentLifecycleException e )
{
throw new ReactorException( "Unable to configure Maven for execution", e );
}
EventDispatcher dispatcher = request.getEventDispatcher();
String event = MavenEvents.REACTOR_EXECUTION;
dispatcher.dispatchStart( event, request.getBaseDirectory() );
ReactorManager rm;
ProfileManager globalProfileManager = request.getGlobalProfileManager();
boolean foundProjects = true;
try
{
loadSettingsProfiles( globalProfileManager, request.getSettings() );
List files = getProjectFiles( request );
List projects = collectProjects( files, request.getLocalRepository(), request.isRecursive(),
request.getSettings(), globalProfileManager, !request.isReactorActive() );
// the reasoning here is that the list is still unsorted according to dependency, so the first project
// SHOULD BE the top-level, or the one we want to start with if we're doing an aggregated build.
if ( projects.isEmpty() )
{
MavenProject superProject = projectBuilder.buildStandaloneSuperProject( request.getLocalRepository() );
projects.add( superProject );
foundProjects = false;
}
rm = new ReactorManager( projects );
String requestFailureBehavior = request.getFailureBehavior();
if ( requestFailureBehavior != null )
{
rm.setFailureBehavior( requestFailureBehavior );
}
}
catch ( IOException e )
{
throw new ReactorException( "Error processing projects for the reactor: ", e );
}
catch ( ArtifactResolutionException e )
{
return dispatchErrorResponse( dispatcher, event, request.getBaseDirectory(), e );
}
catch ( ProjectBuildingException e )
{
return dispatchErrorResponse( dispatcher, event, request.getBaseDirectory(), e );
}
catch ( CycleDetectedException e )
{
return dispatchErrorResponse( dispatcher, event, request.getBaseDirectory(), e );
}
catch ( ProfileActivationException e )
{
return dispatchErrorResponse( dispatcher, event, request.getBaseDirectory(), e );
}
try
{
MavenSession session = createSession( request, rm );
session.setUsingPOMsFromFilesystem( foundProjects );
try
{
MavenExecutionResponse response = lifecycleExecutor.execute( session, rm, dispatcher );
// TODO: is this perhaps more appropriate in the CLI?
if ( response.isExecutionFailure() )
{
dispatcher.dispatchError( event, request.getBaseDirectory(), response.getException() );
// TODO: yuck! Revisit when cleaning up the exception handling from the top down
Throwable exception = response.getException();
if ( ReactorManager.FAIL_AT_END.equals( rm.getFailureBehavior() ) &&
exception instanceof ReactorException )
{
logFailure( response, exception, null );
if ( rm.hasMultipleProjects() && response.executedMultipleProjects() )
{
writeReactorSummary( rm );
}
}
else if ( exception instanceof MojoFailureException )
{
MojoFailureException e = (MojoFailureException) exception;
logFailure( response, e, e.getLongMessage() );
}
else if ( exception instanceof MojoExecutionException )
{
// TODO: replace by above
if ( exception.getCause() == null )
{
MojoExecutionException e = (MojoExecutionException) exception;
logFailure( response, e, e.getLongMessage() );
}
else
{
// TODO: throw exceptions like this, so "failures" are just that
logError( response );
}
}
else if ( exception instanceof ArtifactResolutionException )
{
logFailure( response, exception, null );
}
else
{
// TODO: this should be a "FATAL" exception, reported to the
// developers - however currently a LOT of
// "user" errors fall through the cracks (like invalid POMs, as
// one example)
logError( response );
}
return response;
}
else
{
logSuccess( response, rm );
}
}
catch ( LifecycleExecutionException e )
{
logFatal( e );
throw new ReactorException( "Error executing project within the reactor", e );
}
dispatcher.dispatchEnd( event, request.getBaseDirectory() );
// TODO: not really satisfactory
return null;
}
catch ( ReactorException e )
{
dispatcher.dispatchError( event, request.getBaseDirectory(), e );
throw e;
}
}
private void writeReactorSummary( ReactorManager rm )
{
// -------------------------
// Reactor Summary:
// -------------------------
// o project-name...........FAILED
// o project2-name..........SKIPPED (dependency build failed or was skipped)
// o project-3-name.........SUCCESS
line();
getLogger().info( "Reactor Summary:" );
line();
for ( Iterator it = rm.getSortedProjects().iterator(); it.hasNext(); )
{
MavenProject project = (MavenProject) it.next();
if ( rm.hasBuildFailure( project ) )
{
logReactorSummaryLine( project.getName(), "FAILED" );
}
else if ( rm.isBlackListed( project ) )
{
logReactorSummaryLine( project.getName(), "SKIPPED (dependency build failed or was skipped)" );
}
else
{
logReactorSummaryLine( project.getName(), "SUCCESS" );
}
}
getLogger().info( "" );
getLogger().info( "" );
}
private void logReactorSummaryLine( String name, String status )
{
StringBuffer messageBuffer = new StringBuffer();
messageBuffer.append( name );
int dotCount = 65;
dotCount -= name.length();
for ( int i = 0; i < dotCount; i++ )
{
messageBuffer.append( '.' );
}
messageBuffer.append( status );
getLogger().info( messageBuffer.toString() );
}
private MavenExecutionResponse dispatchErrorResponse( EventDispatcher dispatcher, String event,
String baseDirectory, Exception e )
{
dispatcher.dispatchError( event, baseDirectory, e );
MavenExecutionResponse response = new MavenExecutionResponse();
response.setStart( new Date() );
response.setFinish( new Date() );
response.setException( e );
logFailure( response, e, null );
return response;
}
private List collectProjects( List files, ArtifactRepository localRepository, boolean recursive, Settings settings,
ProfileManager globalProfileManager, boolean isRoot )
throws ProjectBuildingException, ReactorException, IOException, ArtifactResolutionException,
ProfileActivationException
{
List projects = new ArrayList( files.size() );
for ( Iterator iterator = files.iterator(); iterator.hasNext(); )
{
File file = (File) iterator.next();
boolean usingReleasePom = false;
if ( RELEASE_POMv4.equals( file.getName() ) )
{
getLogger().info( "NOTE: Using release-pom: " + file + " in reactor build." );
usingReleasePom = true;
}
MavenProject project = getProject( file, localRepository, settings, globalProfileManager );
if ( isRoot )
{
project.setExecutionRoot( true );
}
if ( project.getPrerequisites() != null && project.getPrerequisites().getMaven() != null )
{
DefaultArtifactVersion version = new DefaultArtifactVersion( project.getPrerequisites().getMaven() );
if ( runtimeInformation.getApplicationVersion().compareTo( version ) < 0 )
{
throw new ProjectBuildingException( "Unable to build project '" + project.getFile() +
"; it requires Maven version " + version.toString() );
}
}
if ( project.getModules() != null && !project.getModules().isEmpty() && recursive )
{
// TODO: Really should fail if it was not? What if it is aggregating - eg "ear"?
project.setPackaging( "pom" );
File basedir = file.getParentFile();
// Initial ordering is as declared in the modules section
List moduleFiles = new ArrayList( project.getModules().size() );
for ( Iterator i = project.getModules().iterator(); i.hasNext(); )
{
String name = (String) i.next();
File moduleFile;
if ( usingReleasePom )
{
moduleFile = new File( basedir, name + "/" + Maven.RELEASE_POMv4 );
}
else
{
moduleFile = new File( basedir, name + "/" + Maven.POMv4 );
}
moduleFiles.add( moduleFile );
}
List collectedProjects = collectProjects( moduleFiles, localRepository, recursive, settings,
globalProfileManager, false );
projects.addAll( collectedProjects );
project.setCollectedProjects( collectedProjects );
}
projects.add( project );
}
return projects;
}
public MavenProject getProject( File pom, ArtifactRepository localRepository, Settings settings,
ProfileManager globalProfileManager )
throws ProjectBuildingException, ArtifactResolutionException, ProfileActivationException
{
if ( pom.exists() )
{
if ( pom.length() == 0 )
{
throw new ProjectBuildingException(
"The file " + pom.getAbsolutePath() + " you specified has zero length." );
}
}
return projectBuilder.build( pom, localRepository, globalProfileManager );
}
private void loadSettingsProfiles( ProfileManager profileManager, Settings settings )
{
List settingsProfiles = settings.getProfiles();
if ( settingsProfiles != null && !settingsProfiles.isEmpty() )
{
List settingsActiveProfileIds = settings.getActiveProfiles();
profileManager.explicitlyActivate( settingsActiveProfileIds );
for ( Iterator it = settings.getProfiles().iterator(); it.hasNext(); )
{
org.apache.maven.settings.Profile rawProfile = (org.apache.maven.settings.Profile) it.next();
Profile profile = SettingsUtils.convertFromSettingsProfile( rawProfile );
profileManager.addProfile( profile );
}
}
}
// ----------------------------------------------------------------------
// Methods used by all execution request handlers
// ----------------------------------------------------------------------
//!! We should probably have the execution request handler create the
// session as
// the session type would be specific to the request i.e. having a project
// or not.
protected MavenSession createSession( MavenExecutionRequest request, ReactorManager rpm )
{
return new MavenSession( container,
request.getSettings(),
request.getLocalRepository(),
request.getEventDispatcher(),
rpm,
request.getGoals(),
request.getBaseDirectory(),
request.getExecutionProperties() );
}
/**
* @todo [BP] this might not be required if there is a better way to pass
* them in. It doesn't feel quite right.
* @todo [JC] we should at least provide a mapping of protocol-to-proxy for
* the wagons, shouldn't we?
*/
private void resolveParameters( Settings settings )
throws ComponentLookupException, ComponentLifecycleException
{
WagonManager wagonManager = (WagonManager) container.lookup( WagonManager.ROLE );
try
{
Proxy proxy = settings.getActiveProxy();
if ( proxy != null )
{
wagonManager.addProxy( proxy.getProtocol(), proxy.getHost(), proxy.getPort(), proxy.getUsername(),
proxy.getPassword(), proxy.getNonProxyHosts() );
}
for ( Iterator i = settings.getServers().iterator(); i.hasNext(); )
{
Server server = (Server) i.next();
wagonManager.addAuthenticationInfo( server.getId(), server.getUsername(), server.getPassword(),
server.getPrivateKey(), server.getPassphrase() );
wagonManager.addPermissionInfo( server.getId(), server.getFilePermissions(),
server.getDirectoryPermissions() );
}
for ( Iterator i = settings.getMirrors().iterator(); i.hasNext(); )
{
Mirror mirror = (Mirror) i.next();
wagonManager.addMirror( mirror.getId(), mirror.getMirrorOf(), mirror.getUrl() );
}
}
finally
{
container.release( wagonManager );
}
}
// ----------------------------------------------------------------------
// Lifecylce Management
// ----------------------------------------------------------------------
public void contextualize( Context context )
throws ContextException
{
container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
}
// ----------------------------------------------------------------------
// Reporting / Logging
// ----------------------------------------------------------------------
protected void logFatal( Throwable error )
{
line();
getLogger().error( "FATAL ERROR" );
line();
diagnoseError( error );
line();
getLogger().error( "FATAL ERROR" );
line();
}
protected void logError( MavenExecutionResponse r )
{
line();
getLogger().error( "BUILD ERROR" );
line();
diagnoseError( r.getException() );
line();
getLogger().error( "BUILD ERROR" );
line();
stats( r.getStart(), r.getFinish() );
line();
}
private void diagnoseError( Throwable error )
{
String message = null;
if ( errorDiagnosers != null )
{
for ( Iterator it = errorDiagnosers.values().iterator(); it.hasNext(); )
{
ErrorDiagnoser diagnoser = (ErrorDiagnoser) it.next();
if ( diagnoser.canDiagnose( error ) )
{
message = diagnoser.diagnose( error );
// first one wins.
break;
}
}
}
if ( message == null )
{
message = error.getMessage();
}
getLogger().info( "Diagnosis: " + message );
line();
// TODO: needs to honour -e
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "Trace:\n", error );
line();
}
}
protected void logFailure( MavenExecutionResponse r, Throwable error, String longMessage )
{
line();
getLogger().info( "BUILD FAILURE" );
line();
String message = null;
if ( errorDiagnosers != null )
{
for ( Iterator it = errorDiagnosers.values().iterator(); it.hasNext(); )
{
ErrorDiagnoser diagnoser = (ErrorDiagnoser) it.next();
if ( diagnoser.canDiagnose( error ) )
{
message = diagnoser.diagnose( error );
// first one wins.
break;
}
}
}
if ( message == null )
{
message = "Reason: " + error.getMessage();
}
getLogger().info( message );
line();
if ( longMessage != null )
{
getLogger().info( longMessage );
line();
}
// TODO: needs to honour -e
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "Trace", error );
line();
}
stats( r.getStart(), r.getFinish() );
line();
}
protected void logSuccess( MavenExecutionResponse r, ReactorManager rm )
{
if ( rm.hasMultipleProjects() && r.executedMultipleProjects() )
{
writeReactorSummary( rm );
}
line();
getLogger().info( "BUILD SUCCESSFUL" );
line();
stats( r.getStart(), r.getFinish() );
line();
}
protected void stats( Date start, Date finish )
{
long time = finish.getTime() - start.getTime();
getLogger().info( "Total time: " + formatTime( time ) );
getLogger().info( "Finished at: " + finish );
//noinspection CallToSystemGC
System.gc();
Runtime r = Runtime.getRuntime();
getLogger().info(
"Final Memory: " + ( r.totalMemory() - r.freeMemory() ) / MB + "M/" + r.totalMemory() / MB + "M" );
}
protected void line()
{
getLogger().info( "----------------------------------------------------------------------------" );
}
protected static String formatTime( long ms )
{
long secs = ms / MS_PER_SEC;
long min = secs / SEC_PER_MIN;
secs = secs % SEC_PER_MIN;
String msg = "";
if ( min > 1 )
{
msg = min + " minutes ";
}
else if ( min == 1 )
{
msg = "1 minute ";
}
if ( secs > 1 )
{
msg += secs + " seconds";
}
else if ( secs == 1 )
{
msg += "1 second";
}
else if ( min == 0 )
{
msg += "< 1 second";
}
return msg;
}
private List getProjectFiles( MavenExecutionRequest request )
throws IOException
{
List files = Collections.EMPTY_LIST;
if ( request.isReactorActive() )
{
// TODO: should we now include the pom.xml in the current directory?
// String includes = System.getProperty( "maven.reactor.includes", "**/" + POMv4 );
// String excludes = System.getProperty( "maven.reactor.excludes", POMv4 );
String includes = System.getProperty( "maven.reactor.includes", "**/" + POMv4 + ",**/" + RELEASE_POMv4 );
String excludes = System.getProperty( "maven.reactor.excludes", POMv4 + "," + RELEASE_POMv4 );
files = FileUtils.getFiles( userDir, includes, excludes );
filterOneProjectFilePerDirectory( files );
// make sure there is consistent ordering on all platforms, rather than using the filesystem ordering
Collections.sort( files );
}
else if ( request.getPomFile() != null )
{
File projectFile = new File( request.getPomFile() ).getAbsoluteFile();
if ( projectFile.exists() )
{
files = Collections.singletonList( projectFile );
}
}
else
{
File projectFile = new File( userDir, RELEASE_POMv4 );
if ( !projectFile.exists() )
{
projectFile = new File( userDir, POMv4 );
}
if ( projectFile.exists() )
{
files = Collections.singletonList( projectFile );
}
}
return files;
}
private void filterOneProjectFilePerDirectory( List files )
{
List releaseDirs = new ArrayList();
for ( Iterator it = files.iterator(); it.hasNext(); )
{
File projectFile = (File) it.next();
if ( RELEASE_POMv4.equals( projectFile.getName() ) )
{
releaseDirs.add( projectFile.getParentFile() );
}
}
for ( Iterator it = files.iterator(); it.hasNext(); )
{
File projectFile = (File) it.next();
// remove pom.xml files where there is a sibling release-pom.xml file...
if ( !RELEASE_POMv4.equals( projectFile.getName() ) && releaseDirs.contains( projectFile.getParentFile() ) )
{
it.remove();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy