| package org.apache.maven.continuum.project.builder.maven; |
| |
| /* |
| * 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.dao.LocalRepositoryDao; |
| import org.apache.continuum.dao.ProjectGroupDao; |
| import org.apache.continuum.dao.ScheduleDao; |
| import org.apache.continuum.model.repository.LocalRepository; |
| import org.apache.maven.continuum.builddefinition.BuildDefinitionService; |
| import org.apache.maven.continuum.builddefinition.BuildDefinitionServiceException; |
| import org.apache.maven.continuum.configuration.ConfigurationService; |
| import org.apache.maven.continuum.execution.maven.m2.MavenBuilderHelper; |
| import org.apache.maven.continuum.execution.maven.m2.MavenTwoBuildExecutor; |
| import org.apache.maven.continuum.model.project.BuildDefinition; |
| import org.apache.maven.continuum.model.project.BuildDefinitionTemplate; |
| import org.apache.maven.continuum.model.project.Project; |
| import org.apache.maven.continuum.model.project.ProjectGroup; |
| import org.apache.maven.continuum.model.project.Schedule; |
| import org.apache.maven.continuum.project.builder.AbstractContinuumProjectBuilder; |
| import org.apache.maven.continuum.project.builder.ContinuumProjectBuilder; |
| import org.apache.maven.continuum.project.builder.ContinuumProjectBuilderException; |
| import org.apache.maven.continuum.project.builder.ContinuumProjectBuildingResult; |
| import org.apache.maven.continuum.store.ContinuumStoreException; |
| import org.apache.maven.project.MavenProject; |
| import org.codehaus.plexus.util.StringUtils; |
| |
| import java.io.File; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * @author <a href="mailto:trygvis@inamo.no">Trygve Laugstøl</a> |
| * @version $Id$ |
| * @plexus.component role="org.apache.maven.continuum.project.builder.ContinuumProjectBuilder" role-hint="maven-two-builder" |
| */ |
| public class MavenTwoContinuumProjectBuilder |
| extends AbstractContinuumProjectBuilder |
| implements ContinuumProjectBuilder |
| { |
| public static final String ID = "maven-two-builder"; |
| |
| private static final String POM_PART = "/pom.xml"; |
| |
| /** |
| * @plexus.requirement |
| */ |
| private LocalRepositoryDao localRepositoryDao; |
| |
| /** |
| * @plexus.requirement |
| */ |
| private MavenBuilderHelper builderHelper; |
| |
| /** |
| * @plexus.requirement |
| */ |
| private ScheduleDao scheduleDao; |
| |
| /** |
| * @plexus.requirement |
| */ |
| private BuildDefinitionService buildDefinitionService; |
| |
| /** |
| * @plexus.configuration |
| */ |
| private List<String> excludedPackagingTypes = new ArrayList<String>(); |
| |
| private Project rootProject; |
| |
| /** |
| * @plexus.requirement |
| */ |
| private ProjectGroupDao projectGroupDao; |
| |
| // ---------------------------------------------------------------------- |
| // AbstractContinuumProjectBuilder Implementation |
| // ---------------------------------------------------------------------- |
| public ContinuumProjectBuildingResult buildProjectsFromMetadata( URL url, String username, String password ) |
| throws ContinuumProjectBuilderException |
| { |
| return buildProjectsFromMetadata( url, username, password, true, false ); |
| } |
| |
| public ContinuumProjectBuildingResult buildProjectsFromMetadata( URL url, String username, String password, |
| boolean loadRecursiveProjects, |
| boolean checkoutInSingleDirectory ) |
| throws ContinuumProjectBuilderException |
| { |
| try |
| { |
| return buildProjectsFromMetadata( url, username, password, loadRecursiveProjects, |
| buildDefinitionService.getDefaultMavenTwoBuildDefinitionTemplate(), |
| checkoutInSingleDirectory ); |
| } |
| catch ( BuildDefinitionServiceException e ) |
| { |
| throw new ContinuumProjectBuilderException( e.getMessage(), e ); |
| } |
| } |
| |
| public ContinuumProjectBuildingResult buildProjectsFromMetadata( URL url, String username, String password, |
| boolean loadRecursiveProjects, |
| BuildDefinitionTemplate buildDefinitionTemplate, |
| boolean checkoutInSingleDirectory ) |
| throws ContinuumProjectBuilderException |
| { |
| return buildProjectsFromMetadata( url, username, password, loadRecursiveProjects, buildDefinitionTemplate, checkoutInSingleDirectory, -1 ); |
| } |
| |
| public ContinuumProjectBuildingResult buildProjectsFromMetadata( URL url, String username, String password, |
| boolean loadRecursiveProjects, |
| BuildDefinitionTemplate buildDefinitionTemplate, |
| boolean checkoutInSingleDirectory, |
| int projectGroupId ) |
| throws ContinuumProjectBuilderException |
| { |
| // ---------------------------------------------------------------------- |
| // We need to roll the project data into a file so that we can use it |
| // ---------------------------------------------------------------------- |
| |
| ContinuumProjectBuildingResult result = new ContinuumProjectBuildingResult(); |
| |
| try |
| { |
| ProjectGroup projectGroup = null; |
| if ( projectGroupId > 0 ) |
| { |
| projectGroup = projectGroupDao.getProjectGroupWithBuildDetailsByProjectGroupId( projectGroupId ); |
| } |
| |
| readModules( url, result, projectGroup, username, password, null, loadRecursiveProjects, buildDefinitionTemplate, |
| checkoutInSingleDirectory ); |
| } |
| catch ( BuildDefinitionServiceException e ) |
| { |
| throw new ContinuumProjectBuilderException( e.getMessage(), e ); |
| } |
| catch ( ContinuumStoreException e ) |
| { |
| throw new ContinuumProjectBuilderException( e.getMessage(), e ); |
| } |
| return result; |
| } |
| |
| // ---------------------------------------------------------------------- |
| // |
| // ---------------------------------------------------------------------- |
| |
| private void readModules( URL url, ContinuumProjectBuildingResult result, ProjectGroup projectGroup, String username, |
| String password, String scmUrl, boolean loadRecursiveProjects, |
| BuildDefinitionTemplate buildDefinitionTemplate, boolean checkoutInSingleDirectory ) |
| throws ContinuumProjectBuilderException, BuildDefinitionServiceException |
| { |
| |
| MavenProject mavenProject; |
| |
| File pomFile = null; |
| |
| try |
| { |
| pomFile = createMetadataFile( result, url, username, password ); |
| |
| if ( result.hasErrors() ) |
| { |
| return; |
| } |
| |
| mavenProject = builderHelper.getMavenProject( result, pomFile ); |
| |
| if ( result.hasErrors() ) |
| { |
| return; |
| } |
| } |
| finally |
| { |
| if ( pomFile != null && pomFile.exists() ) |
| { |
| pomFile.delete(); |
| } |
| } |
| log.debug( "projectGroup " + projectGroup ); |
| |
| if ( projectGroup == null ) |
| { |
| projectGroup = buildProjectGroup( mavenProject, result ); |
| |
| // project groups have the top lvl build definition which is the default build defintion for the sub |
| // projects |
| log.debug( "projectGroup != null" + ( projectGroup != null ) ); |
| if ( projectGroup != null ) |
| { |
| List<BuildDefinition> buildDefinitions = getBuildDefinitions( buildDefinitionTemplate, |
| loadRecursiveProjects, |
| mavenProject.getBuild().getDefaultGoal() ); |
| for ( BuildDefinition buildDefinition : buildDefinitions ) |
| { |
| buildDefinition = persistBuildDefinition( buildDefinition ); |
| |
| projectGroup.addBuildDefinition( buildDefinition ); |
| } |
| } |
| } |
| if ( result.getProjectGroups().isEmpty() ) |
| { |
| result.addProjectGroup( projectGroup ); |
| } |
| |
| if ( !excludedPackagingTypes.contains( mavenProject.getPackaging() ) ) |
| { |
| Project continuumProject = new Project(); |
| |
| /* |
| We are interested in having the scm username and password being passed into this method be taken into |
| account during project mapping so make sure we set it to the continuum project instance. |
| */ |
| if ( username != null && StringUtils.isNotEmpty( username ) ) |
| { |
| continuumProject.setScmUsername( username ); |
| |
| if ( password != null && StringUtils.isNotEmpty( password ) ) |
| { |
| continuumProject.setScmPassword( password ); |
| } |
| } |
| |
| continuumProject.setCheckedOutInSingleDirectory( checkoutInSingleDirectory ); |
| |
| // New project |
| builderHelper.mapMavenProjectToContinuumProject( result, mavenProject, continuumProject, true ); |
| |
| if ( result.hasErrors() ) |
| { |
| log.info( |
| "Error adding project: Unknown error mapping project " + url + ": " + result.getErrorsAsString() ); |
| return; |
| } |
| |
| // Rewrite scmurl from the one found in added project due to a bug in scm url resolution |
| // for projects that doesn't have module name != artifactId |
| if ( StringUtils.isNotEmpty( scmUrl ) ) |
| { |
| continuumProject.setScmUrl( scmUrl ); |
| } |
| else |
| { |
| scmUrl = continuumProject.getScmUrl(); |
| } |
| |
| if ( !"HEAD".equals( mavenProject.getScm().getTag() ) ) |
| { |
| continuumProject.setScmTag( mavenProject.getScm().getTag() ); |
| } |
| |
| // CONTINUUM-2563 |
| // Don't create if the project has a build definition template assigned to it already |
| if ( !loadRecursiveProjects && buildDefinitionTemplate.equals( getDefaultBuildDefinitionTemplate() ) ) |
| { |
| List<BuildDefinition> buildDefinitions = projectGroup.getBuildDefinitions(); |
| for ( BuildDefinition buildDefinition : buildDefinitions ) |
| { |
| if ( buildDefinition.isDefaultForProject() ) |
| { |
| // create a default build definition at the project level |
| BuildDefinition projectBuildDef = buildDefinitionService.cloneBuildDefinition( buildDefinition ); |
| projectBuildDef.setDefaultForProject( true ); |
| |
| String arguments = projectBuildDef.getArguments().replace( "--non-recursive", "" ); |
| arguments = arguments.replace( "-N", "" ); |
| arguments = arguments.trim(); |
| |
| // add build definition only if it differs |
| if ( !projectBuildDef.getArguments().equals( arguments ) ) |
| { |
| log.info( "Adding default build definition for project '" + continuumProject.getName() + |
| "' without '--non-recursive' flag." ); |
| |
| projectBuildDef.setArguments( arguments ); |
| continuumProject.addBuildDefinition( projectBuildDef ); |
| } |
| |
| break; |
| } |
| } |
| } |
| |
| result.addProject( continuumProject, MavenTwoBuildExecutor.ID ); |
| |
| if ( checkoutInSingleDirectory && rootProject == null ) |
| { |
| rootProject = continuumProject; |
| result.setRootProject( rootProject ); |
| } |
| } |
| |
| List<String> modules = mavenProject.getModules(); |
| |
| String prefix = url.toExternalForm(); |
| |
| String suffix = ""; |
| |
| int i = prefix.indexOf( '?' ); |
| |
| int lastSlash; |
| |
| if ( i != -1 ) |
| { |
| suffix = prefix.substring( i ); |
| |
| lastSlash = prefix.lastIndexOf( "/", i ); |
| } |
| else |
| { |
| lastSlash = prefix.lastIndexOf( "/" ); |
| } |
| |
| prefix = prefix.substring( 0, lastSlash ); |
| if ( loadRecursiveProjects ) |
| { |
| for ( String module : modules ) |
| { |
| if ( StringUtils.isNotEmpty( module ) ) |
| { |
| String urlString = prefix + "/" + module + POM_PART + suffix; |
| |
| URL moduleUrl; |
| |
| try |
| { |
| urlString = StringUtils.replace( urlString, '\\', '/' ); |
| moduleUrl = new URL( urlString ); |
| } |
| catch ( MalformedURLException e ) |
| { |
| log.debug( "Error adding project module: Malformed URL " + urlString, e ); |
| result.addError( ContinuumProjectBuildingResult.ERROR_MALFORMED_URL, urlString ); |
| continue; |
| } |
| |
| String moduleScmUrl = ""; |
| |
| String modulePath = StringUtils.replace( new String( module ), '\\', '/' ); |
| |
| // check if module is relative |
| if ( modulePath.indexOf( "../" ) != -1 ) |
| { |
| int depth = StringUtils.countMatches( StringUtils.substring( modulePath, 0, |
| modulePath.lastIndexOf( '/' ) + |
| 1 ), "/" ); |
| |
| String baseUrl = ""; |
| for ( int j = 1; j <= depth; j++ ) |
| { |
| scmUrl = StringUtils.chompLast( new String( scmUrl ), "/" ); |
| baseUrl = StringUtils.substring( scmUrl, 0, scmUrl.lastIndexOf( '/' ) ); |
| } |
| moduleScmUrl = baseUrl + "/" + StringUtils.substring( modulePath, modulePath.lastIndexOf( |
| "../" ) + 3 ); |
| } |
| else |
| { |
| scmUrl = StringUtils.chompLast( scmUrl, "/" ); |
| moduleScmUrl = scmUrl + "/" + modulePath; |
| } |
| // we are in recursive loading mode |
| readModules( moduleUrl, result, projectGroup, username, password, moduleScmUrl, true, |
| buildDefinitionTemplate, checkoutInSingleDirectory ); |
| } |
| } |
| } |
| } |
| |
| private BuildDefinition persistBuildDefinition( BuildDefinition buildDefinition ) |
| throws BuildDefinitionServiceException |
| { |
| buildDefinition = buildDefinitionService.addBuildDefinition( buildDefinition ); |
| if ( buildDefinition.getSchedule() == null ) |
| { |
| try |
| { |
| Schedule schedule = scheduleDao.getScheduleByName( |
| ConfigurationService.DEFAULT_SCHEDULE_NAME ); |
| |
| buildDefinition.setSchedule( schedule ); |
| } |
| catch ( ContinuumStoreException e ) |
| { |
| log.warn( "Can't get default schedule.", e ); |
| } |
| } |
| return buildDefinition; |
| } |
| |
| private List<BuildDefinition> getBuildDefinitions( BuildDefinitionTemplate template, boolean loadRecursiveProjects, |
| String defaultGoal ) |
| throws ContinuumProjectBuilderException, BuildDefinitionServiceException |
| { |
| List<BuildDefinition> buildDefinitions = new ArrayList<BuildDefinition>(); |
| boolean defaultSet = false; |
| for ( BuildDefinition buildDefinition : template.getBuildDefinitions() ) |
| { |
| buildDefinition = buildDefinitionService.cloneBuildDefinition( buildDefinition ); |
| |
| if ( !defaultSet && buildDefinition.isDefaultForProject() ) |
| { |
| defaultSet = true; |
| |
| //CONTINUUM-1296 |
| if ( StringUtils.isNotEmpty( defaultGoal ) ) |
| { |
| buildDefinition.setGoals( defaultGoal ); |
| } |
| } |
| else |
| { |
| buildDefinition.setDefaultForProject( false ); |
| } |
| |
| // due to CONTINUUM-1207 CONTINUUM-1436 user can do what they want with arguments |
| // we must remove if exists --non-recursive or -N |
| if ( !loadRecursiveProjects ) |
| { |
| if ( StringUtils.isEmpty( buildDefinition.getArguments() ) ) |
| { |
| // strange for a mvn build |
| log.info( "build definition '" + buildDefinition.getId() + "' has empty args" ); |
| } |
| else |
| { |
| String arguments = buildDefinition.getArguments().replace( "--non-recursive", "" ); |
| arguments = arguments.replace( "-N", "" ); |
| arguments = arguments.trim(); |
| buildDefinition.setArguments( arguments ); |
| } |
| } |
| buildDefinition.setTemplate( false ); |
| buildDefinitions.add( buildDefinition ); |
| } |
| return buildDefinitions; |
| |
| } |
| |
| private ProjectGroup buildProjectGroup( MavenProject mavenProject, ContinuumProjectBuildingResult result ) |
| { |
| ProjectGroup projectGroup = new ProjectGroup(); |
| |
| // ---------------------------------------------------------------------- |
| // Group id |
| // ---------------------------------------------------------------------- |
| |
| if ( StringUtils.isEmpty( mavenProject.getGroupId() ) ) |
| { |
| result.addError( ContinuumProjectBuildingResult.ERROR_MISSING_GROUPID ); |
| |
| return null; |
| } |
| |
| projectGroup.setGroupId( mavenProject.getGroupId() ); |
| |
| // ---------------------------------------------------------------------- |
| // Name |
| // ---------------------------------------------------------------------- |
| |
| String name = mavenProject.getName(); |
| |
| if ( StringUtils.isEmpty( name ) ) |
| { |
| name = mavenProject.getGroupId(); |
| } |
| |
| projectGroup.setName( name ); |
| |
| // ---------------------------------------------------------------------- |
| // Description |
| // ---------------------------------------------------------------------- |
| |
| projectGroup.setDescription( mavenProject.getDescription() ); |
| |
| // ---------------------------------------------------------------------- |
| // Local Repository |
| // ---------------------------------------------------------------------- |
| |
| try |
| { |
| LocalRepository repository = localRepositoryDao.getLocalRepositoryByName( "DEFAULT" ); |
| |
| projectGroup.setLocalRepository( repository ); |
| } |
| catch ( ContinuumStoreException e ) |
| { |
| log.warn( "Can't get default repository.", e ); |
| } |
| |
| return projectGroup; |
| } |
| |
| |
| public BuildDefinitionTemplate getDefaultBuildDefinitionTemplate() |
| throws ContinuumProjectBuilderException |
| { |
| try |
| { |
| return buildDefinitionService.getDefaultMavenTwoBuildDefinitionTemplate(); |
| } |
| catch ( BuildDefinitionServiceException e ) |
| { |
| throw new ContinuumProjectBuilderException( e.getMessage(), e ); |
| } |
| } |
| |
| } |