blob: eeabad71c33f050c1716bfdd3870fd54af2930a9 [file] [log] [blame]
package org.apache.maven.continuum.execution.maven.m2;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.continuum.model.repository.LocalRepository;
import org.apache.maven.artifact.manager.WagonManager;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.continuum.model.project.Project;
import org.apache.maven.continuum.model.project.ProjectDependency;
import org.apache.maven.continuum.model.project.ProjectDeveloper;
import org.apache.maven.continuum.model.project.ProjectNotifier;
import org.apache.maven.continuum.project.builder.ContinuumProjectBuildingResult;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Developer;
import org.apache.maven.model.Extension;
import org.apache.maven.model.Model;
import org.apache.maven.model.Notifier;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.Profile;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.model.Scm;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.apache.maven.profiles.DefaultProfileManager;
import org.apache.maven.profiles.ProfileManager;
import org.apache.maven.project.InvalidProjectModelException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.validation.ModelValidationResult;
import org.apache.maven.settings.MavenSettingsBuilder;
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.io.xpp3.SettingsXpp3Writer;
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.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;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
* @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
* @version $Id$
* @plexus.component role="org.apache.maven.continuum.execution.maven.m2.MavenBuilderHelper" role-hint="default"
*/
public class DefaultMavenBuilderHelper
extends AbstractLogEnabled
implements MavenBuilderHelper, Contextualizable, Initializable
{
/**
* @plexus.requirement
*/
private MavenProjectBuilder projectBuilder;
/**
* @plexus.requirement
*/
private ArtifactRepositoryFactory artifactRepositoryFactory;
/**
* @plexus.requirement
*/
private ArtifactRepositoryLayout repositoryLayout;
/**
* @plexus.requirement
*/
private MavenSettingsBuilder mavenSettingsBuilder;
/**
* @plexus.configuration default-value="${plexus.home}/local-repository"
*/
private String localRepository;
private PlexusContainer container;
private LocalRepository repository;
// ----------------------------------------------------------------------
// MavenBuilderHelper Implementation
// ----------------------------------------------------------------------
public void mapMetadataToProject( ContinuumProjectBuildingResult result, File metadata, Project continuumProject )
{
MavenProject mavenProject = getMavenProject( result, metadata );
if ( mavenProject == null )
{
result.addError( ContinuumProjectBuildingResult.ERROR_UNKNOWN,
"Can't load the maven project. Verify that your scm url is correct and remove/readd the project." );
return;
}
mapMavenProjectToContinuumProject( result, mavenProject, continuumProject, false );
}
public void mapMavenProjectToContinuumProject( ContinuumProjectBuildingResult result, MavenProject mavenProject,
Project continuumProject, boolean groupPom )
{
if ( mavenProject == null )
{
result.addError( ContinuumProjectBuildingResult.ERROR_UNKNOWN, "The maven project is null." );
return;
}
// ----------------------------------------------------------------------
// Name
// ----------------------------------------------------------------------
continuumProject.setName( getProjectName( mavenProject ) );
// ----------------------------------------------------------------------
// SCM Url
// ----------------------------------------------------------------------
// TODO: Remove this: scm url shouldn't be null there
if ( StringUtils.isEmpty( continuumProject.getScmUrl() ) )
{
String scmUrl = getScmUrl( mavenProject );
continuumProject.setScmUrl( scmUrl );
//TODO: This code is duplicated in MavenTwoContinuumProjectBuilder
if ( mavenProject.getScm() != null && "HEAD".equals( mavenProject.getScm().getTag() ) )
{
continuumProject.setScmTag( mavenProject.getScm().getTag() );
}
}
// ----------------------------------------------------------------------
// Version
// ----------------------------------------------------------------------
continuumProject.setVersion( getVersion( mavenProject ) );
// ----------------------------------------------------------------------
// GroupId
// ----------------------------------------------------------------------
if ( !StringUtils.isEmpty( mavenProject.getGroupId() ) )
{
continuumProject.setGroupId( mavenProject.getGroupId() );
}
else
{
result.addError( ContinuumProjectBuildingResult.ERROR_MISSING_GROUPID );
return;
}
// ----------------------------------------------------------------------
// artifactId
// ----------------------------------------------------------------------
if ( !StringUtils.isEmpty( mavenProject.getArtifactId() ) )
{
continuumProject.setArtifactId( mavenProject.getArtifactId() );
}
else
{
result.addError( ContinuumProjectBuildingResult.ERROR_MISSING_ARTIFACTID );
return;
}
// ----------------------------------------------------------------------
// Project Url
// ----------------------------------------------------------------------
if ( !StringUtils.isEmpty( mavenProject.getUrl() ) )
{
continuumProject.setUrl( mavenProject.getUrl() );
}
// ----------------------------------------------------------------------
// Developers
// ----------------------------------------------------------------------
if ( mavenProject.getDevelopers() != null )
{
List<ProjectDeveloper> developers = new ArrayList<ProjectDeveloper>();
for ( Developer d : (List<Developer>)mavenProject.getDevelopers() )
{
ProjectDeveloper cd = new ProjectDeveloper();
cd.setScmId( d.getId() );
cd.setName( d.getName() );
cd.setEmail( d.getEmail() );
developers.add( cd );
}
continuumProject.setDevelopers( developers );
}
// ----------------------------------------------------------------------
// Parent
// ----------------------------------------------------------------------
if ( mavenProject.getParent() != null )
{
MavenProject parentProject = mavenProject.getParent();
ProjectDependency parent = new ProjectDependency();
parent.setGroupId( parentProject.getGroupId() );
parent.setArtifactId( parentProject.getArtifactId() );
parent.setVersion( parentProject.getVersion() );
continuumProject.setParent( parent );
}
// ----------------------------------------------------------------------
// Dependencies
// ----------------------------------------------------------------------
List<ProjectDependency> dependencies = new ArrayList<ProjectDependency>();
for ( Dependency dependency : (List<Dependency>)mavenProject.getDependencies() )
{
ProjectDependency cd = new ProjectDependency();
cd.setGroupId( dependency.getGroupId() );
cd.setArtifactId( dependency.getArtifactId() );
cd.setVersion( dependency.getVersion() );
dependencies.add( cd );
}
for ( Plugin dependency : (List<Plugin>)mavenProject.getBuildPlugins() )
{
ProjectDependency cd = new ProjectDependency();
cd.setGroupId( dependency.getGroupId() );
cd.setArtifactId( dependency.getArtifactId() );
cd.setVersion( dependency.getVersion() );
dependencies.add( cd );
}
for ( ReportPlugin dependency : (List<ReportPlugin>)mavenProject.getReportPlugins() )
{
ProjectDependency cd = new ProjectDependency();
cd.setGroupId( dependency.getGroupId() );
cd.setArtifactId( dependency.getArtifactId() );
cd.setVersion( dependency.getVersion() );
dependencies.add( cd );
}
for ( Extension dependency : (List<Extension>)mavenProject.getBuildExtensions() )
{
ProjectDependency cd = new ProjectDependency();
cd.setGroupId( dependency.getGroupId() );
cd.setArtifactId( dependency.getArtifactId() );
cd.setVersion( dependency.getVersion() );
dependencies.add( cd );
}
continuumProject.setDependencies( dependencies );
// ----------------------------------------------------------------------
// Notifiers
// ----------------------------------------------------------------------
List<ProjectNotifier> userNotifiers = new ArrayList<ProjectNotifier>();
if ( continuumProject.getNotifiers() != null )
{
for ( int i = 0; i < continuumProject.getNotifiers().size(); i++ )
{
ProjectNotifier notifier = (ProjectNotifier) continuumProject.getNotifiers().get( i );
if ( notifier.isFromUser() )
{
ProjectNotifier userNotifier = new ProjectNotifier();
userNotifier.setType( notifier.getType() );
userNotifier.setEnabled( notifier.isEnabled() );
userNotifier.setConfiguration( notifier.getConfiguration() );
userNotifier.setFrom( notifier.getFrom() );
userNotifier.setRecipientType( notifier.getRecipientType() );
userNotifier.setSendOnError( notifier.isSendOnError() );
userNotifier.setSendOnFailure( notifier.isSendOnFailure() );
userNotifier.setSendOnSuccess( notifier.isSendOnSuccess() );
userNotifier.setSendOnWarning( notifier.isSendOnWarning() );
userNotifiers.add( userNotifier );
}
}
}
List<ProjectNotifier> notifiers = getNotifiers( result, mavenProject, continuumProject );
if ( notifiers != null )
{
continuumProject.setNotifiers( notifiers );
}
for ( ProjectNotifier notifier : userNotifiers )
{
continuumProject.addNotifier( notifier );
}
}
public MavenProject getMavenProject( ContinuumProjectBuildingResult result, File file )
{
MavenProject project;
try
{
// TODO: This seems like code that is shared with DefaultMaven, so it should be moved to the project
// builder perhaps
Settings settings = getSettings();
if ( getLogger().isDebugEnabled() )
{
writeSettings( settings );
}
ProfileManager profileManager = new DefaultProfileManager( container, settings );
project = projectBuilder.build( file, getLocalRepository(), profileManager, false );
if ( getLogger().isDebugEnabled() )
{
writePom( project );
writeActiveProfileStatement( project );
}
}
catch ( ProjectBuildingException e )
{
StringBuffer messages = new StringBuffer();
Throwable cause = e.getCause();
if ( cause != null )
{
while ( ( cause.getCause() != null ) && ( cause instanceof ProjectBuildingException ) )
{
cause = cause.getCause();
}
}
if ( e instanceof InvalidProjectModelException )
{
InvalidProjectModelException ex = (InvalidProjectModelException) e;
ModelValidationResult validationResult = ex.getValidationResult();
if ( validationResult != null && validationResult.getMessageCount() > 0 )
{
for ( Iterator<String> i = validationResult.getMessages().iterator(); i.hasNext(); )
{
String valmsg = i.next();
result.addError( ContinuumProjectBuildingResult.ERROR_VALIDATION, valmsg );
messages.append( valmsg );
messages.append( "\n" );
}
}
}
if ( cause instanceof ArtifactNotFoundException )
{
result.addError( ContinuumProjectBuildingResult.ERROR_ARTIFACT_NOT_FOUND,
( (ArtifactNotFoundException) cause ).toString() );
return null;
}
result.addError( ContinuumProjectBuildingResult.ERROR_PROJECT_BUILDING, e.getMessage() );
String msg = "Cannot build maven project from " + file + " (" + e.getMessage() + ").\n" + messages;
file.delete();
getLogger().error( msg );
return null;
}
// TODO catch all exceptions is bad
catch ( Exception e )
{
result.addError( ContinuumProjectBuildingResult.ERROR_PROJECT_BUILDING, e.getMessage() );
String msg = "Cannot build maven project from " + file + " (" + e.getMessage() + ").";
file.delete();
getLogger().error( msg );
return null;
}
// ----------------------------------------------------------------------
// Validate the MavenProject using some Continuum rules
// ----------------------------------------------------------------------
// SCM connection should be optional -- we can get it from the pom url in many cases (CONTINUUM-1721)
return project;
}
public ArtifactRepository getLocalRepository()
throws SettingsConfigurationException
{
return getRepository( getSettings() );
}
// ----------------------------------------------------------------------
//
// ----------------------------------------------------------------------
public String getProjectName( MavenProject project )
{
String name = project.getName();
if ( StringUtils.isEmpty( name ) )
{
return project.getId();
}
return name;
}
/**
* @return the scm connection url from the project, or null if none is present
*/
private String getScmUrl( MavenProject project )
{
if ( project != null && project.getScm() != null )
{
return project.getScm().getConnection();
}
return null;
//TODO: What if the project has only a developerConnection (no anonymous access?)
//TODO: Should this return an empty string instead of null if there is no url?
}
private List<ProjectNotifier> getNotifiers( ContinuumProjectBuildingResult result, MavenProject mavenProject,
Project continuumProject )
{
List<ProjectNotifier> notifiers = new ArrayList<ProjectNotifier>();
if ( mavenProject.getCiManagement() != null && mavenProject.getCiManagement().getNotifiers() != null )
{
for ( Notifier projectNotifier : (List<Notifier>)mavenProject.getCiManagement().getNotifiers() )
{
ProjectNotifier notifier = new ProjectNotifier();
if ( StringUtils.isEmpty( projectNotifier.getType() ) )
{
result.addError( ContinuumProjectBuildingResult.ERROR_MISSING_NOTIFIER_TYPE );
return null;
}
notifier.setType( projectNotifier.getType() );
if ( projectNotifier.getConfiguration() == null )
{
result.addError( ContinuumProjectBuildingResult.ERROR_MISSING_NOTIFIER_CONFIGURATION );
return null;
}
notifier.setConfiguration( projectNotifier.getConfiguration() );
notifier.setFrom( ProjectNotifier.FROM_PROJECT );
notifier.setSendOnSuccess( projectNotifier.isSendOnSuccess() );
notifier.setSendOnFailure( projectNotifier.isSendOnFailure() );
notifier.setSendOnError( projectNotifier.isSendOnError() );
notifier.setSendOnWarning( projectNotifier.isSendOnWarning() );
notifiers.add( notifier );
}
}
return notifiers;
}
private String getVersion( MavenProject project )
{
return project.getVersion();
}
// ----------------------------------------------------------------------
//
// ----------------------------------------------------------------------
private Settings getSettings()
throws SettingsConfigurationException
{
try
{
return mavenSettingsBuilder.buildSettings( false );
}
catch ( IOException e )
{
throw new SettingsConfigurationException( "Error reading settings file", e );
}
catch ( XmlPullParserException e )
{
throw new SettingsConfigurationException( e.getMessage(), e.getDetail(), e.getLineNumber(),
e.getColumnNumber() );
}
}
private ArtifactRepository getRepository( Settings settings )
{
// ----------------------------------------------------------------------
// Set our configured location as the default but try to use the defaults
// as returned by the MavenSettings component.
// ----------------------------------------------------------------------
String localRepo = localRepository;
if ( repository != null )
{
return artifactRepositoryFactory.createArtifactRepository( repository.getName(),
"file://" + repository.getLocation(),
repositoryLayout,
null,
null );
}
else if ( !( StringUtils.isEmpty( settings.getLocalRepository() ) ) )
{
localRepo = settings.getLocalRepository();
}
return artifactRepositoryFactory.createArtifactRepository( "local", "file://" + localRepo, repositoryLayout,
null, null );
}
private void writeSettings( Settings settings )
{
StringWriter sWriter = new StringWriter();
SettingsXpp3Writer settingsWriter = new SettingsXpp3Writer();
try
{
settingsWriter.write( sWriter, settings );
StringBuffer message = new StringBuffer();
message.append( "\n************************************************************************************" );
message.append( "\nEffective Settings" );
message.append( "\n************************************************************************************" );
message.append( "\n" );
message.append( sWriter.toString() );
message.append( "\n************************************************************************************" );
message.append( "\n\n" );
getLogger().debug( message.toString() );
}
catch ( IOException e )
{
getLogger().warn( "Cannot serialize Settings to XML.", e );
}
}
private void writePom( MavenProject project )
{
StringBuffer message = new StringBuffer();
Model pom = project.getModel();
StringWriter sWriter = new StringWriter();
MavenXpp3Writer pomWriter = new MavenXpp3Writer();
try
{
pomWriter.write( sWriter, pom );
message.append( "\n************************************************************************************" );
message.append( "\nEffective POM for project \'" ).append( project.getId() ).append( "\'" );
message.append( "\n************************************************************************************" );
message.append( "\n" );
message.append( sWriter.toString() );
message.append( "\n************************************************************************************" );
message.append( "\n\n" );
getLogger().debug( message.toString() );
}
catch ( IOException e )
{
getLogger().warn( "Cannot serialize POM to XML.", e );
}
}
private void writeActiveProfileStatement( MavenProject project )
{
List<Profile> profiles = project.getActiveProfiles();
StringBuffer message = new StringBuffer();
message.append( "\n" );
message.append( "\n************************************************************************************" );
message.append( "\nActive Profiles for Project \'" ).append( project.getId() ).append( "\'" );
message.append( "\n************************************************************************************" );
message.append( "\n" );
if ( profiles == null || profiles.isEmpty() )
{
message.append( "There are no active profiles." );
}
else
{
message.append( "The following profiles are active:\n" );
for ( Profile profile : profiles )
{
message.append( "\n - " ).append( profile.getId() ).append( " (source: " )
.append( profile.getSource() ).append( ")" );
}
}
message.append( "\n************************************************************************************" );
message.append( "\n\n" );
getLogger().debug( message.toString() );
}
/**
* @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, SettingsConfigurationException
{
WagonManager wagonManager = (WagonManager) container.lookup( WagonManager.ROLE );
try
{
Proxy proxy = settings.getActiveProxy();
if ( proxy != null )
{
if ( proxy.getHost() == null )
{
throw new SettingsConfigurationException( "Proxy in settings.xml has no host" );
}
wagonManager.addProxy( proxy.getProtocol(), proxy.getHost(), proxy.getPort(), proxy.getUsername(),
proxy.getPassword(), proxy.getNonProxyHosts() );
}
for ( Iterator<Server> i = settings.getServers().iterator(); i.hasNext(); )
{
Server server = i.next();
wagonManager.addAuthenticationInfo( server.getId(), server.getUsername(), server.getPassword(),
server.getPrivateKey(), server.getPassphrase() );
wagonManager.addPermissionInfo( server.getId(), server.getFilePermissions(),
server.getDirectoryPermissions() );
if ( server.getConfiguration() != null )
{
wagonManager.addConfiguration( server.getId(), (Xpp3Dom) server.getConfiguration() );
}
}
for ( Iterator<Mirror> i = settings.getMirrors().iterator(); i.hasNext(); )
{
Mirror mirror = i.next();
wagonManager.addMirror( mirror.getId(), mirror.getMirrorOf(), mirror.getUrl() );
}
}
finally
{
container.release( wagonManager );
}
}
// ----------------------------------------------------------------------
//
// ----------------------------------------------------------------------
public void contextualize( Context context )
throws ContextException
{
container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
}
public void initialize()
throws InitializationException
{
try
{
Settings settings = getSettings();
resolveParameters( settings );
}
catch ( Exception e )
{
throw new InitializationException( "Can't initialize '" + getClass().getName() + "'", e );
}
}
public void setLocalRepository( LocalRepository repository )
{
this.repository = repository;
}
}