blob: 064bdea312f663c8b6b5a5f16ef7caa71730e76c [file] [log] [blame]
package org.apache.continuum.builder.distributed.executor;
/*
* 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.builder.distributed.manager.DistributedBuildManager;
import org.apache.continuum.builder.utils.ContinuumBuildConstant;
import org.apache.continuum.dao.BuildDefinitionDao;
import org.apache.continuum.dao.BuildResultDao;
import org.apache.continuum.dao.ProjectDao;
import org.apache.continuum.dao.ProjectScmRootDao;
import org.apache.continuum.distributed.transport.slave.SlaveBuildAgentTransportClient;
import org.apache.continuum.model.project.ProjectScmRoot;
import org.apache.continuum.model.repository.LocalRepository;
import org.apache.continuum.taskqueue.PrepareBuildProjectsTask;
import org.apache.continuum.utils.ContinuumUtils;
import org.apache.continuum.utils.ProjectSorter;
import org.apache.continuum.utils.build.BuildTrigger;
import org.apache.maven.continuum.ContinuumException;
import org.apache.maven.continuum.configuration.ConfigurationService;
import org.apache.maven.continuum.model.project.BuildDefinition;
import org.apache.maven.continuum.model.project.BuildResult;
import org.apache.maven.continuum.model.project.Project;
import org.apache.maven.continuum.model.scm.ChangeFile;
import org.apache.maven.continuum.model.scm.ChangeSet;
import org.apache.maven.continuum.model.scm.ScmResult;
import org.apache.maven.continuum.project.ContinuumProjectState;
import org.apache.maven.continuum.store.ContinuumStoreException;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.taskqueue.Task;
import org.codehaus.plexus.taskqueue.execution.TaskExecutionException;
import org.codehaus.plexus.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DistributedBuildProjectTaskExecutor
implements DistributedBuildTaskExecutor
{
private static final Logger log = LoggerFactory.getLogger( DistributedBuildProjectTaskExecutor.class );
private String buildAgentUrl;
private long startTime;
private long endTime;
@Requirement
private ProjectDao projectDao;
@Requirement
private ProjectScmRootDao projectScmRootDao;
@Requirement
private BuildDefinitionDao buildDefinitionDao;
@Requirement
private BuildResultDao buildResultDao;
@Requirement
private ConfigurationService configurationService;
@Requirement
private DistributedBuildManager distributedBuildManager;
public void setBuildAgentUrl( String buildAgentUrl )
{
this.buildAgentUrl = buildAgentUrl;
}
public String getBuildAgentUrl()
{
return buildAgentUrl;
}
public void executeTask( Task task )
throws TaskExecutionException
{
PrepareBuildProjectsTask prepareBuildTask = (PrepareBuildProjectsTask) task;
try
{
SlaveBuildAgentTransportClient client = new SlaveBuildAgentTransportClient( new URL( buildAgentUrl ), "",
configurationService.getSharedSecretPassword() );
log.info( "initializing buildContext for projectGroupId=" + prepareBuildTask.getProjectGroupId() );
List<Map<String, Object>> buildContext = initializeBuildContext(
prepareBuildTask.getProjectsBuildDefinitionsMap(), prepareBuildTask.getBuildTrigger(),
prepareBuildTask.getScmRootAddress(), prepareBuildTask.getProjectScmRootId() );
startTime = System.currentTimeMillis();
client.buildProjects( buildContext );
endTime = System.currentTimeMillis();
}
catch ( MalformedURLException e )
{
log.error( "Invalid URL " + buildAgentUrl + ", not building" );
throw new TaskExecutionException( "Invalid URL " + buildAgentUrl, e );
}
catch ( Exception e )
{
log.error( "Error occurred while building task", e );
endTime = System.currentTimeMillis();
createResult( prepareBuildTask, ContinuumUtils.throwableToString( e ) );
}
}
private List<Map<String, Object>> initializeBuildContext( Map<Integer, Integer> projectsAndBuildDefinitions,
BuildTrigger buildTrigger, String scmRootAddress,
int scmRootId )
throws ContinuumException
{
List<Map<String, Object>> buildContext = new ArrayList<Map<String, Object>>();
try
{
ProjectScmRoot scmRoot = projectScmRootDao.getProjectScmRoot( scmRootId );
List<Project> projects = projectDao.getProjectsWithDependenciesByGroupId(
scmRoot.getProjectGroup().getId() );
List<Project> sortedProjects = ProjectSorter.getSortedProjects( projects, null );
for ( Project project : sortedProjects )
{
if ( !projectsAndBuildDefinitions.containsKey( project.getId() ) )
{
continue;
}
int buildDefinitionId = projectsAndBuildDefinitions.get( project.getId() );
BuildDefinition buildDef = buildDefinitionDao.getBuildDefinition( buildDefinitionId );
BuildResult buildResult = buildResultDao.getLatestBuildResultForProject( project.getId() );
Map<String, Object> context = new HashMap<String, Object>();
context.put( ContinuumBuildConstant.KEY_PROJECT_GROUP_ID, project.getProjectGroup().getId() );
context.put( ContinuumBuildConstant.KEY_PROJECT_GROUP_NAME, project.getProjectGroup().getName() );
context.put( ContinuumBuildConstant.KEY_SCM_ROOT_ID, scmRootId );
context.put( ContinuumBuildConstant.KEY_SCM_ROOT_ADDRESS, scmRootAddress );
context.put( ContinuumBuildConstant.KEY_PROJECT_ID, project.getId() );
context.put( ContinuumBuildConstant.KEY_PROJECT_NAME, project.getName() );
context.put( ContinuumBuildConstant.KEY_PROJECT_VERSION, project.getVersion() );
context.put( ContinuumBuildConstant.KEY_EXECUTOR_ID, project.getExecutorId() );
context.put( ContinuumBuildConstant.KEY_PROJECT_BUILD_NUMBER, project.getBuildNumber() );
context.put( ContinuumBuildConstant.KEY_SCM_URL, project.getScmUrl() );
context.put( ContinuumBuildConstant.KEY_PROJECT_STATE, project.getState() );
if ( buildResult != null )
{
context.put( ContinuumBuildConstant.KEY_LATEST_UPDATE_DATE, new Date(
buildResult.getStartTime() ) );
}
LocalRepository localRepo = project.getProjectGroup().getLocalRepository();
if ( localRepo != null )
{
// CONTINUUM-2391
context.put( ContinuumBuildConstant.KEY_LOCAL_REPOSITORY, localRepo.getName() );
}
else
{
context.put( ContinuumBuildConstant.KEY_LOCAL_REPOSITORY, "" );
}
if ( project.getScmUsername() == null )
{
context.put( ContinuumBuildConstant.KEY_SCM_USERNAME, "" );
}
else
{
context.put( ContinuumBuildConstant.KEY_SCM_USERNAME, project.getScmUsername() );
}
if ( project.getScmPassword() == null )
{
context.put( ContinuumBuildConstant.KEY_SCM_PASSWORD, "" );
}
else
{
context.put( ContinuumBuildConstant.KEY_SCM_PASSWORD, project.getScmPassword() );
}
if ( project.getScmTag() != null )
{
context.put( ContinuumBuildConstant.KEY_SCM_TAG, project.getScmTag() );
}
else
{
context.put( ContinuumBuildConstant.KEY_SCM_TAG, "" );
}
context.put( ContinuumBuildConstant.KEY_BUILD_DEFINITION_ID, buildDefinitionId );
String buildDefinitionLabel = buildDef.getDescription();
if ( StringUtils.isEmpty( buildDefinitionLabel ) )
{
buildDefinitionLabel = buildDef.getGoals();
}
context.put( ContinuumBuildConstant.KEY_BUILD_DEFINITION_LABEL, buildDefinitionLabel );
context.put( ContinuumBuildConstant.KEY_BUILD_FILE, buildDef.getBuildFile() );
if ( buildDef.getGoals() == null )
{
context.put( ContinuumBuildConstant.KEY_GOALS, "" );
}
else
{
context.put( ContinuumBuildConstant.KEY_GOALS, buildDef.getGoals() );
}
if ( buildDef.getArguments() == null )
{
context.put( ContinuumBuildConstant.KEY_ARGUMENTS, "" );
}
else
{
context.put( ContinuumBuildConstant.KEY_ARGUMENTS, buildDef.getArguments() );
}
context.put( ContinuumBuildConstant.KEY_TRIGGER, buildTrigger.getTrigger() );
if ( buildTrigger.getTrigger() == ContinuumProjectState.TRIGGER_FORCED )
{
if ( buildTrigger.getTriggeredBy() == null )
{
context.put( ContinuumBuildConstant.KEY_USERNAME, "" );
}
else
{
context.put( ContinuumBuildConstant.KEY_USERNAME, buildTrigger.getTriggeredBy() );
}
}
else
{
context.put( ContinuumBuildConstant.KEY_USERNAME, buildDef.getSchedule().getName() );
}
context.put( ContinuumBuildConstant.KEY_BUILD_FRESH, buildDef.isBuildFresh() );
context.put( ContinuumBuildConstant.KEY_ALWAYS_BUILD, buildDef.isAlwaysBuild() );
context.put( ContinuumBuildConstant.KEY_OLD_SCM_CHANGES, getOldScmChanges( project.getId(),
buildDefinitionId ) );
context.put( ContinuumBuildConstant.KEY_BUILD_AGENT_URL, buildAgentUrl );
context.put( ContinuumBuildConstant.KEY_MAX_JOB_EXEC_TIME,
buildDef.getSchedule().getMaxJobExecutionTime() );
buildContext.add( context );
}
return buildContext;
}
catch ( ContinuumStoreException e )
{
throw new ContinuumException( "Error while initializing build context", e );
}
}
private void createResult( PrepareBuildProjectsTask task, String error )
throws TaskExecutionException
{
try
{
ProjectScmRoot scmRoot = projectScmRootDao.getProjectScmRootByProjectGroupAndScmRootAddress(
task.getProjectGroupId(), task.getScmRootAddress() );
if ( scmRoot.getState() == ContinuumProjectState.UPDATING )
{
scmRoot.setState( ContinuumProjectState.ERROR );
scmRoot.setError( error );
projectScmRootDao.updateProjectScmRoot( scmRoot );
}
else
{
Map<Integer, Integer> map = task.getProjectsBuildDefinitionsMap();
for ( Map.Entry<Integer, Integer> build : map.entrySet() )
{
int projectId = build.getKey(), buildDefinitionId = build.getValue();
Project project = projectDao.getProject( projectId );
BuildDefinition buildDef = buildDefinitionDao.getBuildDefinition( buildDefinitionId );
BuildResult latestBuildResult = buildResultDao
.getLatestBuildResultForBuildDefinition( projectId, buildDefinitionId );
if ( latestBuildResult == null ||
( latestBuildResult.getStartTime() >= startTime && latestBuildResult.getEndTime() > 0 &&
latestBuildResult.getEndTime() < endTime ) || latestBuildResult.getStartTime() < startTime )
{
BuildResult buildResult = new BuildResult();
buildResult.setBuildDefinition( buildDef );
buildResult.setError( error );
buildResult.setState( ContinuumProjectState.ERROR );
buildResult.setTrigger( task.getBuildTrigger().getTrigger() );
buildResult.setUsername( task.getBuildTrigger().getTriggeredBy() );
buildResult.setStartTime( startTime );
buildResult.setEndTime( endTime );
buildResultDao.addBuildResult( project, buildResult );
}
}
}
}
catch ( ContinuumStoreException e )
{
throw new TaskExecutionException( "Error while creating result", e );
}
}
private List<Map<String, Object>> getOldScmChanges( int projectId, int buildDefinitionId )
throws ContinuumStoreException
{
List<Map<String, Object>> scmChanges = new ArrayList<Map<String, Object>>();
BuildResult oldBuildResult = buildResultDao.getLatestBuildResultForBuildDefinition( projectId,
buildDefinitionId );
if ( oldBuildResult != null )
{
ScmResult scmResult = getOldScmResults( projectId, oldBuildResult.getBuildNumber(),
oldBuildResult.getEndTime() );
scmChanges = getScmChanges( scmResult );
}
return scmChanges;
}
private List<Map<String, Object>> getScmChanges( ScmResult scmResult )
{
List<Map<String, Object>> scmChanges = new ArrayList<Map<String, Object>>();
if ( scmResult != null && scmResult.getChanges() != null )
{
for ( Object obj : scmResult.getChanges() )
{
ChangeSet changeSet = (ChangeSet) obj;
Map<String, Object> map = new HashMap<String, Object>();
if ( StringUtils.isNotEmpty( changeSet.getAuthor() ) )
{
map.put( ContinuumBuildConstant.KEY_CHANGESET_AUTHOR, changeSet.getAuthor() );
}
else
{
map.put( ContinuumBuildConstant.KEY_CHANGESET_AUTHOR, "" );
}
if ( StringUtils.isNotEmpty( changeSet.getComment() ) )
{
map.put( ContinuumBuildConstant.KEY_CHANGESET_COMMENT, changeSet.getComment() );
}
else
{
map.put( ContinuumBuildConstant.KEY_CHANGESET_COMMENT, "" );
}
if ( changeSet.getDateAsDate() != null )
{
map.put( ContinuumBuildConstant.KEY_CHANGESET_DATE, changeSet.getDateAsDate() );
}
map.put( ContinuumBuildConstant.KEY_CHANGESET_FILES, getScmChangeFiles( changeSet.getFiles() ) );
scmChanges.add( map );
}
}
return scmChanges;
}
private List<Map<String, String>> getScmChangeFiles( List<ChangeFile> files )
{
List<Map<String, String>> scmChangeFiles = new ArrayList<Map<String, String>>();
if ( files != null )
{
for ( ChangeFile changeFile : files )
{
Map<String, String> map = new HashMap<String, String>();
if ( StringUtils.isNotEmpty( changeFile.getName() ) )
{
map.put( ContinuumBuildConstant.KEY_CHANGEFILE_NAME, changeFile.getName() );
}
else
{
map.put( ContinuumBuildConstant.KEY_CHANGEFILE_NAME, "" );
}
if ( StringUtils.isNotEmpty( changeFile.getRevision() ) )
{
map.put( ContinuumBuildConstant.KEY_CHANGEFILE_REVISION, changeFile.getRevision() );
}
else
{
map.put( ContinuumBuildConstant.KEY_CHANGEFILE_REVISION, "" );
}
if ( StringUtils.isNotEmpty( changeFile.getStatus() ) )
{
map.put( ContinuumBuildConstant.KEY_CHANGEFILE_STATUS, changeFile.getStatus() );
}
else
{
map.put( ContinuumBuildConstant.KEY_CHANGEFILE_STATUS, "" );
}
scmChangeFiles.add( map );
}
}
return scmChangeFiles;
}
private ScmResult getOldScmResults( int projectId, long startId, long fromDate )
throws ContinuumStoreException
{
List<BuildResult> results = buildResultDao.getBuildResultsForProjectFromId( projectId, startId );
ScmResult res = new ScmResult();
if ( results != null && results.size() > 0 )
{
for ( BuildResult result : results )
{
ScmResult scmResult = result.getScmResult();
if ( scmResult != null )
{
List<ChangeSet> changes = scmResult.getChanges();
if ( changes != null )
{
for ( ChangeSet changeSet : changes )
{
if ( changeSet.getDate() < fromDate )
{
continue;
}
if ( !res.getChanges().contains( changeSet ) )
{
res.addChange( changeSet );
}
}
}
}
}
}
return res;
}
}