View Javadoc

1   package org.apache.maven.continuum.buildcontroller;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.continuum.dao.BuildDefinitionDao;
23  import org.apache.continuum.dao.BuildResultDao;
24  import org.apache.continuum.dao.ProjectDao;
25  import org.apache.continuum.dao.ProjectGroupDao;
26  import org.apache.continuum.dao.ProjectScmRootDao;
27  import org.apache.continuum.model.project.ProjectScmRoot;
28  import org.apache.continuum.utils.ContinuumUtils;
29  import org.apache.continuum.utils.build.BuildTrigger;
30  import org.apache.maven.continuum.core.action.AbstractContinuumAction;
31  import org.apache.maven.continuum.core.action.ExecuteBuilderContinuumAction;
32  import org.apache.maven.continuum.execution.ContinuumBuildExecutor;
33  import org.apache.maven.continuum.execution.ContinuumBuildExecutorConstants;
34  import org.apache.maven.continuum.execution.manager.BuildExecutorManager;
35  import org.apache.maven.continuum.model.project.BuildDefinition;
36  import org.apache.maven.continuum.model.project.BuildResult;
37  import org.apache.maven.continuum.model.project.Project;
38  import org.apache.maven.continuum.model.project.ProjectDependency;
39  import org.apache.maven.continuum.model.project.ProjectGroup;
40  import org.apache.maven.continuum.model.scm.ChangeFile;
41  import org.apache.maven.continuum.model.scm.ChangeSet;
42  import org.apache.maven.continuum.model.scm.ScmResult;
43  import org.apache.maven.continuum.notification.ContinuumNotificationDispatcher;
44  import org.apache.maven.continuum.project.ContinuumProjectState;
45  import org.apache.maven.continuum.store.ContinuumObjectNotFoundException;
46  import org.apache.maven.continuum.store.ContinuumStoreException;
47  import org.apache.maven.continuum.utils.WorkingDirectoryService;
48  import org.apache.maven.scm.ScmException;
49  import org.apache.maven.scm.repository.ScmRepositoryException;
50  import org.codehaus.plexus.action.ActionManager;
51  import org.codehaus.plexus.action.ActionNotFoundException;
52  import org.codehaus.plexus.taskqueue.execution.TaskExecutionException;
53  import org.slf4j.Logger;
54  import org.slf4j.LoggerFactory;
55  
56  import java.util.ArrayList;
57  import java.util.Iterator;
58  import java.util.List;
59  import java.util.Map;
60  
61  /**
62   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
63   * @version $Id: DefaultBuildController.java 1372260 2012-08-13 04:29:09Z brett $
64   * @plexus.component role="org.apache.maven.continuum.buildcontroller.BuildController" role-hint="default"
65   */
66  public class DefaultBuildController
67      implements BuildController
68  {
69      private static final Logger log = LoggerFactory.getLogger( DefaultBuildController.class );
70  
71      /**
72       * @plexus.requirement
73       */
74      private BuildDefinitionDao buildDefinitionDao;
75  
76      /**
77       * @plexus.requirement
78       */
79      private BuildResultDao buildResultDao;
80  
81      /**
82       * @plexus.requirement
83       */
84      private ProjectDao projectDao;
85  
86      /**
87       * @plexus.requirement
88       */
89      private ProjectGroupDao projectGroupDao;
90  
91      /**
92       * @plexus.requirement
93       */
94      private ProjectScmRootDao projectScmRootDao;
95  
96      /**
97       * @plexus.requirement
98       */
99      private ContinuumNotificationDispatcher notifierDispatcher;
100 
101     /**
102      * @plexus.requirement
103      */
104     private ActionManager actionManager;
105 
106     /**
107      * @plexus.requirement
108      */
109     private WorkingDirectoryService workingDirectoryService;
110 
111     /**
112      * @plexus.requirement
113      */
114     private BuildExecutorManager buildExecutorManager;
115 
116     // ----------------------------------------------------------------------
117     // BuildController Implementation
118     // ----------------------------------------------------------------------
119 
120     /**
121      * @param projectId
122      * @param buildDefinitionId
123      * @param buildTrigger
124      * @param scmResult
125      * @throws TaskExecutionException
126      */
127     public void build( int projectId, int buildDefinitionId, BuildTrigger buildTrigger, ScmResult scmResult )
128         throws TaskExecutionException
129     {
130         log.info( "Initializing build" );
131         BuildContext context = initializeBuildContext( projectId, buildDefinitionId, buildTrigger, scmResult );
132 
133         // ignore this if AlwaysBuild ?
134         if ( !checkScmResult( context ) )
135         {
136             log.info( "Error updating from SCM, not building" );
137             return;
138         }
139 
140         log.info( "Starting build of " + context.getProject().getName() );
141         startBuild( context );
142 
143         try
144         {
145             checkProjectDependencies( context );
146 
147             if ( !shouldBuild( context ) )
148             {
149                 return;
150             }
151 
152             Map<String, Object> actionContext = context.getActionContext();
153 
154             try
155             {
156                 performAction( "update-project-from-working-directory", context );
157             }
158             catch ( TaskExecutionException e )
159             {
160                 updateBuildResult( context, ContinuumUtils.throwableToString( e ) );
161 
162                 //just log the error but don't stop the build from progressing in order not to suppress any build result messages there
163                 log.error( "Error executing action update-project-from-working-directory '", e );
164             }
165 
166             performAction( "execute-builder", context );
167 
168             performAction( "deploy-artifact", context );
169 
170             context.setCancelled( ExecuteBuilderContinuumAction.isCancelled( actionContext ) );
171 
172             String s = AbstractContinuumAction.getBuildId( actionContext, null );
173 
174             if ( s != null && !context.isCancelled() )
175             {
176                 try
177                 {
178                     context.setBuildResult( buildResultDao.getBuildResult( Integer.valueOf( s ) ) );
179                 }
180                 catch ( NumberFormatException e )
181                 {
182                     throw new TaskExecutionException( "Internal error: build id not an integer", e );
183                 }
184                 catch ( ContinuumObjectNotFoundException e )
185                 {
186                     throw new TaskExecutionException( "Internal error: Cannot find build result", e );
187                 }
188                 catch ( ContinuumStoreException e )
189                 {
190                     throw new TaskExecutionException( "Error loading build result", e );
191                 }
192             }
193         }
194         finally
195         {
196             endBuild( context );
197         }
198     }
199 
200     /**
201      * Checks if the build should be marked as ERROR and notifies the end of the build.
202      *
203      * @param context
204      * @throws TaskExecutionException
205      */
206     private void endBuild( BuildContext context )
207         throws TaskExecutionException
208     {
209         Project project = context.getProject();
210 
211         try
212         {
213             if ( project.getState() != ContinuumProjectState.NEW &&
214                 project.getState() != ContinuumProjectState.CHECKEDOUT &&
215                 project.getState() != ContinuumProjectState.OK && project.getState() != ContinuumProjectState.FAILED &&
216                 project.getState() != ContinuumProjectState.ERROR && !context.isCancelled() )
217             {
218                 try
219                 {
220                     String s = AbstractContinuumAction.getBuildId( context.getActionContext(), null );
221 
222                     if ( s != null )
223                     {
224                         BuildResult buildResult = buildResultDao.getBuildResult( Integer.valueOf( s ) );
225                         project.setState( buildResult.getState() );
226                         projectDao.updateProject( project );
227                     }
228                 }
229                 catch ( ContinuumStoreException e )
230                 {
231                     throw new TaskExecutionException( "Error storing the project", e );
232                 }
233             }
234         }
235         finally
236         {
237             if ( !context.isCancelled() )
238             {
239                 notifierDispatcher.buildComplete( project, context.getBuildDefinition(), context.getBuildResult() );
240             }
241         }
242     }
243 
244     private void updateBuildResult( BuildContext context, String error )
245         throws TaskExecutionException
246     {
247         BuildResult build = context.getBuildResult();
248 
249         if ( build == null )
250         {
251             build = makeAndStoreBuildResult( context, error );
252         }
253         else
254         {
255             updateBuildResult( build, context );
256 
257             build.setError( error );
258 
259             try
260             {
261                 buildResultDao.updateBuildResult( build );
262 
263                 build = buildResultDao.getBuildResult( build.getId() );
264 
265                 context.setBuildResult( build );
266             }
267             catch ( ContinuumStoreException e )
268             {
269                 throw new TaskExecutionException( "Error updating build result", e );
270             }
271         }
272 
273         context.getProject().setState( build.getState() );
274 
275         try
276         {
277             projectDao.updateProject( context.getProject() );
278         }
279         catch ( ContinuumStoreException e )
280         {
281             throw new TaskExecutionException( "Error updating project", e );
282         }
283     }
284 
285     private void updateBuildResult( BuildResult build, BuildContext context )
286     {
287         if ( build.getScmResult() == null && context.getScmResult() != null )
288         {
289             build.setScmResult( context.getScmResult() );
290         }
291 
292         if ( build.getModifiedDependencies() == null && context.getModifiedDependencies() != null )
293         {
294             build.setModifiedDependencies( context.getModifiedDependencies() );
295         }
296     }
297 
298     private void startBuild( BuildContext context )
299         throws TaskExecutionException
300     {
301 
302         Project project = context.getProject();
303 
304         project.setOldState( project.getState() );
305 
306         project.setState( ContinuumProjectState.BUILDING );
307 
308         try
309         {
310             projectDao.updateProject( project );
311         }
312         catch ( ContinuumStoreException e )
313         {
314             throw new TaskExecutionException( "Error persisting project", e );
315         }
316 
317         notifierDispatcher.buildStarted( project, context.getBuildDefinition() );
318 
319     }
320 
321     /**
322      * Initializes a BuildContext for the build.
323      *
324      * @param projectId
325      * @param buildDefinitionId
326      * @param buildTrigger
327      * @param scmResult
328      * @return
329      * @throws TaskExecutionException
330      */
331     @SuppressWarnings( "unchecked" )
332     protected BuildContext initializeBuildContext( int projectId, int buildDefinitionId, BuildTrigger buildTrigger,
333                                                    ScmResult scmResult )
334         throws TaskExecutionException
335     {
336         BuildContext context = new BuildContext();
337 
338         context.setStartTime( System.currentTimeMillis() );
339 
340         Map actionContext = context.getActionContext();
341 
342         try
343         {
344             Project project = projectDao.getProject( projectId );
345 
346             context.setProject( project );
347 
348             BuildDefinition buildDefinition = buildDefinitionDao.getBuildDefinition( buildDefinitionId );
349 
350             BuildTrigger newBuildTrigger = buildTrigger;
351 
352             if ( newBuildTrigger.getTrigger() == ContinuumProjectState.TRIGGER_SCHEDULED )
353             {
354                 newBuildTrigger.setTriggeredBy( buildDefinition.getSchedule().getName() );
355             }
356 
357             context.setBuildTrigger( newBuildTrigger );
358 
359             context.setBuildDefinition( buildDefinition );
360 
361             BuildResult oldBuildResult = buildResultDao.getLatestBuildResultForBuildDefinition( projectId,
362                                                                                                 buildDefinitionId );
363 
364             context.setOldBuildResult( oldBuildResult );
365 
366             context.setScmResult( scmResult );
367 
368             // CONTINUUM-2193
369             ProjectGroup projectGroup = project.getProjectGroup();
370             List<ProjectScmRoot> scmRoots = projectScmRootDao.getProjectScmRootByProjectGroup( projectGroup.getId() );
371             String projectScmUrl = project.getScmUrl();
372             String projectScmRootAddress = "";
373 
374             for ( ProjectScmRoot projectScmRoot : scmRoots )
375             {
376                 projectScmRootAddress = projectScmRoot.getScmRootAddress();
377                 if ( projectScmUrl.startsWith( projectScmRoot.getScmRootAddress() ) )
378                 {
379                     AbstractContinuumAction.setProjectScmRootUrl( actionContext, projectScmRoot.getScmRootAddress() );
380                     break;
381                 }
382             }
383 
384             if ( project.isCheckedOutInSingleDirectory() )
385             {
386                 List<Project> projectsInGroup = projectGroupDao.getProjectGroupWithProjects(
387                     projectGroup.getId() ).getProjects();
388                 List<Project> projectsWithCommonScmRoot = new ArrayList<Project>();
389                 for ( Project projectInGroup : projectsInGroup )
390                 {
391                     if ( projectInGroup.getScmUrl().startsWith( projectScmRootAddress ) )
392                     {
393                         projectsWithCommonScmRoot.add( projectInGroup );
394                     }
395                 }
396                 AbstractContinuumAction.setListOfProjectsInGroupWithCommonScmRoot( actionContext,
397                                                                                    projectsWithCommonScmRoot );
398             }
399 
400             // CONTINUUM-1871 olamy if continuum is killed during building oldBuildResult will have a endTime 0
401             // this means all changes since the project has been loaded in continuum will be in memory
402             // now we will load all BuildResult with an Id bigger or equals than the oldBuildResult one
403             //if ( oldBuildResult != null )
404             //{
405             //    context.setOldScmResult(
406             //        getOldScmResults( projectId, oldBuildResult.getBuildNumber(), oldBuildResult.getEndTime() ) );
407             //}
408         }
409         catch ( ContinuumStoreException e )
410         {
411             throw new TaskExecutionException( "Error initializing the build context", e );
412         }
413 
414         // Map<String, Object> actionContext = context.getActionContext();
415 
416         AbstractContinuumAction.setProjectId( actionContext, projectId );
417 
418         AbstractContinuumAction.setProject( actionContext, context.getProject() );
419 
420         AbstractContinuumAction.setBuildDefinitionId( actionContext, buildDefinitionId );
421 
422         AbstractContinuumAction.setBuildDefinition( actionContext, context.getBuildDefinition() );
423 
424         AbstractContinuumAction.setBuildTrigger( actionContext, buildTrigger );
425 
426         AbstractContinuumAction.setScmResult( actionContext, context.getScmResult() );
427 
428         if ( context.getOldBuildResult() != null )
429         {
430             AbstractContinuumAction.setOldBuildId( actionContext, context.getOldBuildResult().getId() );
431         }
432 
433         return context;
434     }
435 
436     private void performAction( String actionName, BuildContext context )
437         throws TaskExecutionException
438     {
439         String error;
440         TaskExecutionException exception;
441 
442         try
443         {
444             log.info( "Performing action " + actionName );
445             actionManager.lookup( actionName ).execute( context.getActionContext() );
446             return;
447         }
448         catch ( ActionNotFoundException e )
449         {
450             error = ContinuumUtils.throwableToString( e );
451             exception = new TaskExecutionException( "Error looking up action '" + actionName + "'", e );
452         }
453         catch ( ScmRepositoryException e )
454         {
455             error = getValidationMessages( e ) + "\n" + ContinuumUtils.throwableToString( e );
456 
457             exception = new TaskExecutionException( "SCM error while executing '" + actionName + "'", e );
458         }
459         catch ( ScmException e )
460         {
461             error = ContinuumUtils.throwableToString( e );
462 
463             exception = new TaskExecutionException( "SCM error while executing '" + actionName + "'", e );
464         }
465         catch ( Exception e )
466         {
467             exception = new TaskExecutionException( "Error executing action '" + actionName + "'", e );
468             error = ContinuumUtils.throwableToString( exception );
469         }
470 
471         // TODO: clean this up. We catch the original exception from the action, and then update the buildresult
472         // for it - we need to because of the specialized error message for SCM.
473         // If updating the buildresult fails, log the previous error and throw the new one.
474         // If updating the buildresult succeeds, throw the original exception. The build result should NOT
475         // be updated again - a TaskExecutionException is final, no further action should be taken upon it.
476 
477         try
478         {
479             updateBuildResult( context, error );
480         }
481         catch ( TaskExecutionException e )
482         {
483             log.error( "Error updating build result after receiving the following exception: ", exception );
484             throw e;
485         }
486 
487         throw exception;
488     }
489 
490     protected boolean shouldBuild( BuildContext context )
491         throws TaskExecutionException
492     {
493         BuildDefinition buildDefinition = context.getBuildDefinition();
494         if ( buildDefinition.isAlwaysBuild() )
495         {
496             log.info( "AlwaysBuild configured, building" );
497             return true;
498         }
499         if ( context.getOldBuildResult() == null )
500         {
501             log.info( "The project has never been built with the current build definition, building" );
502             return true;
503         }
504 
505         Project project = context.getProject();
506 
507         //CONTINUUM-1428
508         if ( project.getOldState() == ContinuumProjectState.ERROR ||
509             context.getOldBuildResult().getState() == ContinuumProjectState.ERROR )
510         {
511             log.info( "Latest state was 'ERROR', building" );
512             return true;
513         }
514 
515         if ( context.getBuildTrigger().getTrigger() == ContinuumProjectState.TRIGGER_FORCED )
516         {
517             log.info( "The project build is forced, building" );
518             return true;
519         }
520 
521         boolean shouldBuild = false;
522 
523         boolean allChangesUnknown = true;
524 
525         if ( project.getOldState() != ContinuumProjectState.NEW &&
526             project.getOldState() != ContinuumProjectState.CHECKEDOUT &&
527             context.getBuildTrigger().getTrigger() != ContinuumProjectState.TRIGGER_FORCED &&
528             project.getState() != ContinuumProjectState.NEW && project.getState() != ContinuumProjectState.CHECKEDOUT )
529         {
530             // Check SCM changes
531             if ( context.getScmResult() != null )
532             {
533                 allChangesUnknown = checkAllChangesUnknown( context.getScmResult().getChanges() );
534             }
535 
536             if ( allChangesUnknown )
537             {
538                 if ( context.getScmResult() != null && !context.getScmResult().getChanges().isEmpty() )
539                 {
540                     log.info(
541                         "The project was not built because all changes are unknown (maybe local modifications or ignored files not defined in your SCM tool." );
542                 }
543                 else
544                 {
545                     log.info(
546                         "The project was not built because no changes were detected in sources since the last build." );
547                 }
548             }
549 
550             // Check dependencies changes
551             if ( context.getModifiedDependencies() != null && !context.getModifiedDependencies().isEmpty() )
552             {
553                 log.info( "Found dependencies changes, building" );
554                 shouldBuild = true;
555             }
556         }
557 
558         // Check changes
559         if ( !shouldBuild && ( ( !allChangesUnknown && context.getScmResult() != null &&
560             !context.getScmResult().getChanges().isEmpty() ) || project.getExecutorId().equals(
561             ContinuumBuildExecutorConstants.MAVEN_TWO_BUILD_EXECUTOR ) ) )
562         {
563             try
564             {
565                 ContinuumBuildExecutor executor = buildExecutorManager.getBuildExecutor( project.getExecutorId() );
566 
567                 Map<String, Object> actionContext = context.getActionContext();
568                 List<Project> projectsWithCommonScmRoot =
569                     AbstractContinuumAction.getListOfProjectsInGroupWithCommonScmRoot( actionContext );
570                 String projectScmRootUrl = AbstractContinuumAction.getProjectScmRootUrl( actionContext,
571                                                                                          project.getScmUrl() );
572 
573                 if ( executor == null )
574                 {
575                     log.warn( "No continuum build executor found for project " + project.getId() +
576                                   " with executor '" + project.getExecutorId() + "'" );
577                 }
578                 else if ( context.getScmResult() != null )
579                 {
580                     shouldBuild = executor.shouldBuild( context.getScmResult().getChanges(), project,
581                                                         workingDirectoryService.getWorkingDirectory( project,
582                                                                                                      projectScmRootUrl,
583                                                                                                      projectsWithCommonScmRoot ),
584                                                         context.getBuildDefinition() );
585                 }
586             }
587             catch ( Exception e )
588             {
589                 updateBuildResult( context, ContinuumUtils.throwableToString( e ) );
590                 throw new TaskExecutionException( "Can't determine if the project should build or not", e );
591             }
592         }
593 
594         if ( shouldBuild )
595         {
596             log.info( "Changes found in the current project, building" );
597         }
598         else
599         {
600             project.setState( project.getOldState() );
601 
602             project.setOldState( 0 );
603 
604             try
605             {
606                 projectDao.updateProject( project );
607             }
608             catch ( ContinuumStoreException e )
609             {
610                 throw new TaskExecutionException( "Error storing project", e );
611             }
612             log.info( "No changes in the current project, not building" );
613 
614         }
615 
616         return shouldBuild;
617     }
618 
619     private boolean checkAllChangesUnknown( List<ChangeSet> changes )
620     {
621         for ( ChangeSet changeSet : changes )
622         {
623             List<ChangeFile> changeFiles = changeSet.getFiles();
624 
625             for ( ChangeFile changeFile : changeFiles )
626             {
627                 if ( !"unknown".equalsIgnoreCase( changeFile.getStatus() ) )
628                 {
629                     return false;
630                 }
631             }
632         }
633 
634         return true;
635     }
636 
637     private String getValidationMessages( ScmRepositoryException ex )
638     {
639         List<String> messages = ex.getValidationMessages();
640 
641         StringBuffer message = new StringBuffer();
642 
643         if ( messages != null && !messages.isEmpty() )
644         {
645             for ( Iterator<String> i = messages.iterator(); i.hasNext(); )
646             {
647                 message.append( i.next() );
648 
649                 if ( i.hasNext() )
650                 {
651                     message.append( System.getProperty( "line.separator" ) );
652                 }
653             }
654         }
655         return message.toString();
656     }
657 
658     protected void checkProjectDependencies( BuildContext context )
659     {
660         if ( context.getOldBuildResult() == null )
661         {
662             return;
663         }
664 
665         try
666         {
667             Project project = projectDao.getProjectWithDependencies( context.getProject().getId() );
668             List<ProjectDependency> dependencies = project.getDependencies();
669 
670             if ( dependencies == null )
671             {
672                 dependencies = new ArrayList<ProjectDependency>();
673             }
674 
675             if ( project.getParent() != null )
676             {
677                 dependencies.add( project.getParent() );
678             }
679 
680             if ( dependencies.isEmpty() )
681             {
682                 return;
683             }
684 
685             List<ProjectDependency> modifiedDependencies = new ArrayList<ProjectDependency>();
686 
687             for ( ProjectDependency dep : dependencies )
688             {
689                 Project dependencyProject = projectDao.getProject( dep.getGroupId(), dep.getArtifactId(),
690                                                                    dep.getVersion() );
691 
692                 if ( dependencyProject != null )
693                 {
694                     long nbBuild = buildResultDao.getNbBuildResultsInSuccessForProject( dependencyProject.getId(),
695                                                                                         context.getOldBuildResult().getEndTime() );
696                     if ( nbBuild > 0 )
697                     {
698                         log.debug( "Dependency changed: " + dep.getGroupId() + ":" + dep.getArtifactId() + ":" +
699                                        dep.getVersion() );
700                         modifiedDependencies.add( dep );
701                     }
702                     else
703                     {
704                         log.debug( "Dependency not changed: " + dep.getGroupId() + ":" + dep.getArtifactId() + ":" +
705                                        dep.getVersion() );
706                     }
707                 }
708                 else
709                 {
710                     log.debug( "Skip non Continuum project: " + dep.getGroupId() + ":" + dep.getArtifactId() + ":" +
711                                    dep.getVersion() );
712                 }
713             }
714 
715             context.setModifiedDependencies( modifiedDependencies );
716             AbstractContinuumAction.setUpdatedDependencies( context.getActionContext(), modifiedDependencies );
717         }
718         catch ( ContinuumStoreException e )
719         {
720             log.warn( "Can't get the project dependencies", e );
721         }
722     }
723 
724     // ----------------------------------------------------------------------
725     //
726     // ----------------------------------------------------------------------
727 
728     private BuildResult makeAndStoreBuildResult( BuildContext context, String error )
729         throws TaskExecutionException
730     {
731         // Project project, ScmResult scmResult, long startTime, int trigger )
732         // project, scmResult, startTime, trigger );
733 
734         BuildResult build = new BuildResult();
735 
736         build.setState( ContinuumProjectState.ERROR );
737 
738         build.setTrigger( context.getBuildTrigger().getTrigger() );
739 
740         build.setUsername( context.getBuildTrigger().getTriggeredBy() );
741 
742         build.setStartTime( context.getStartTime() );
743 
744         build.setEndTime( System.currentTimeMillis() );
745 
746         updateBuildResult( build, context );
747 
748         build.setScmResult( context.getScmResult() );
749 
750         build.setBuildDefinition( context.getBuildDefinition() );
751 
752         if ( error != null )
753         {
754             build.setError( error );
755         }
756 
757         try
758         {
759             buildResultDao.addBuildResult( context.getProject(), build );
760 
761             build = buildResultDao.getBuildResult( build.getId() );
762 
763             context.setBuildResult( build );
764 
765             return build;
766         }
767         catch ( ContinuumStoreException e )
768         {
769             throw new TaskExecutionException( "Error storing build result", e );
770         }
771     }
772 
773     /**
774      * Check to see if there was a error while checking out/updating the project
775      *
776      * @param context The build context
777      * @return true if scm result is ok
778      * @throws TaskExecutionException
779      */
780     private boolean checkScmResult( BuildContext context )
781         throws TaskExecutionException
782     {
783         Project project = context.getProject();
784 
785         int projectGroupId = project.getProjectGroup().getId();
786 
787         List<ProjectScmRoot> scmRoots = projectScmRootDao.getProjectScmRootByProjectGroup( projectGroupId );
788 
789         for ( ProjectScmRoot projectScmRoot : scmRoots )
790         {
791             if ( project.getScmUrl().startsWith( projectScmRoot.getScmRootAddress() ) )
792             {
793                 if ( projectScmRoot.getState() == ContinuumProjectState.UPDATED )
794                 {
795                     return true;
796                 }
797 
798                 break;
799             }
800         }
801 
802         return false;
803     }
804 }