View Javadoc

1   package org.apache.maven.continuum;
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.buildagent.NoBuildAgentException;
23  import org.apache.continuum.buildagent.NoBuildAgentInGroupException;
24  import org.apache.continuum.builder.distributed.manager.DistributedBuildManager;
25  import org.apache.continuum.buildmanager.BuildManagerException;
26  import org.apache.continuum.buildmanager.BuildsManager;
27  import org.apache.continuum.buildqueue.BuildQueueService;
28  import org.apache.continuum.buildqueue.BuildQueueServiceException;
29  import org.apache.continuum.configuration.BuildAgentConfigurationException;
30  import org.apache.continuum.configuration.ContinuumConfigurationException;
31  import org.apache.continuum.dao.BuildDefinitionDao;
32  import org.apache.continuum.dao.BuildResultDao;
33  import org.apache.continuum.dao.ContinuumReleaseResultDao;
34  import org.apache.continuum.dao.DaoUtils;
35  import org.apache.continuum.dao.NotifierDao;
36  import org.apache.continuum.dao.ProjectDao;
37  import org.apache.continuum.dao.ProjectGroupDao;
38  import org.apache.continuum.dao.ProjectScmRootDao;
39  import org.apache.continuum.dao.ScheduleDao;
40  import org.apache.continuum.model.project.ProjectGroupSummary;
41  import org.apache.continuum.model.project.ProjectScmRoot;
42  import org.apache.continuum.model.release.ContinuumReleaseResult;
43  import org.apache.continuum.purge.ContinuumPurgeManager;
44  import org.apache.continuum.purge.PurgeConfigurationService;
45  import org.apache.continuum.release.config.ContinuumReleaseDescriptor;
46  import org.apache.continuum.release.distributed.manager.DistributedReleaseManager;
47  import org.apache.continuum.release.model.PreparedRelease;
48  import org.apache.continuum.repository.RepositoryService;
49  import org.apache.continuum.taskqueue.manager.TaskQueueManager;
50  import org.apache.continuum.taskqueue.manager.TaskQueueManagerException;
51  import org.apache.continuum.utils.ProjectSorter;
52  import org.apache.continuum.utils.build.BuildTrigger;
53  import org.apache.maven.continuum.build.settings.SchedulesActivationException;
54  import org.apache.maven.continuum.build.settings.SchedulesActivator;
55  import org.apache.maven.continuum.builddefinition.BuildDefinitionService;
56  import org.apache.maven.continuum.builddefinition.BuildDefinitionServiceException;
57  import org.apache.maven.continuum.configuration.ConfigurationException;
58  import org.apache.maven.continuum.configuration.ConfigurationLoadingException;
59  import org.apache.maven.continuum.configuration.ConfigurationService;
60  import org.apache.maven.continuum.core.action.AbstractContinuumAction;
61  import org.apache.maven.continuum.core.action.CheckoutProjectContinuumAction;
62  import org.apache.maven.continuum.core.action.CreateProjectsFromMetadataAction;
63  import org.apache.maven.continuum.core.action.StoreProjectAction;
64  import org.apache.maven.continuum.execution.ContinuumBuildExecutorConstants;
65  import org.apache.maven.continuum.execution.manager.BuildExecutorManager;
66  import org.apache.maven.continuum.initialization.ContinuumInitializationException;
67  import org.apache.maven.continuum.initialization.ContinuumInitializer;
68  import org.apache.maven.continuum.installation.InstallationService;
69  import org.apache.maven.continuum.model.project.BuildDefinition;
70  import org.apache.maven.continuum.model.project.BuildDefinitionTemplate;
71  import org.apache.maven.continuum.model.project.BuildQueue;
72  import org.apache.maven.continuum.model.project.BuildResult;
73  import org.apache.maven.continuum.model.project.Project;
74  import org.apache.maven.continuum.model.project.ProjectGroup;
75  import org.apache.maven.continuum.model.project.ProjectNotifier;
76  import org.apache.maven.continuum.model.project.Schedule;
77  import org.apache.maven.continuum.model.scm.ChangeSet;
78  import org.apache.maven.continuum.model.scm.ScmResult;
79  import org.apache.maven.continuum.profile.ProfileService;
80  import org.apache.maven.continuum.project.ContinuumProjectState;
81  import org.apache.maven.continuum.project.builder.ContinuumProjectBuildingResult;
82  import org.apache.maven.continuum.project.builder.maven.MavenOneContinuumProjectBuilder;
83  import org.apache.maven.continuum.project.builder.maven.MavenTwoContinuumProjectBuilder;
84  import org.apache.maven.continuum.release.ContinuumReleaseException;
85  import org.apache.maven.continuum.release.ContinuumReleaseManager;
86  import org.apache.maven.continuum.store.ContinuumObjectNotFoundException;
87  import org.apache.maven.continuum.store.ContinuumStoreException;
88  import org.apache.maven.continuum.utils.ContinuumUrlValidator;
89  import org.apache.maven.continuum.utils.WorkingDirectoryService;
90  import org.apache.maven.shared.release.ReleaseResult;
91  import org.codehaus.plexus.action.Action;
92  import org.codehaus.plexus.action.ActionManager;
93  import org.codehaus.plexus.action.ActionNotFoundException;
94  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
95  import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
96  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Startable;
97  import org.codehaus.plexus.personality.plexus.lifecycle.phase.StartingException;
98  import org.codehaus.plexus.personality.plexus.lifecycle.phase.StoppingException;
99  import org.codehaus.plexus.util.FileUtils;
100 import org.codehaus.plexus.util.IOUtil;
101 import org.codehaus.plexus.util.StringUtils;
102 import org.slf4j.Logger;
103 import org.slf4j.LoggerFactory;
104 import org.springframework.beans.BeanUtils;
105 
106 import java.io.File;
107 import java.io.FileWriter;
108 import java.io.IOException;
109 import java.io.InputStream;
110 import java.io.PrintWriter;
111 import java.util.ArrayList;
112 import java.util.Arrays;
113 import java.util.Collection;
114 import java.util.Collections;
115 import java.util.Date;
116 import java.util.HashMap;
117 import java.util.Iterator;
118 import java.util.List;
119 import java.util.Map;
120 import java.util.Properties;
121 import java.util.regex.Matcher;
122 import java.util.regex.Pattern;
123 
124 /**
125  * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
126  * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l </a>
127  * @version $Id: DefaultContinuum.java 1391353 2012-09-28 07:50:49Z brett $
128  * @plexus.component role="org.apache.maven.continuum.Continuum" role-hint="default"
129  */
130 public class DefaultContinuum
131     implements Continuum, Initializable, Startable
132 {
133     private static final Logger log = LoggerFactory.getLogger( DefaultContinuum.class );
134 
135     /**
136      * @plexus.requirement
137      */
138     private ActionManager actionManager;
139 
140     /**
141      * @plexus.requirement
142      */
143     private ConfigurationService configurationService;
144 
145     /**
146      * @plexus.requirement
147      */
148     private DaoUtils daoUtils;
149 
150     /**
151      * @plexus.requirement
152      */
153     private BuildDefinitionDao buildDefinitionDao;
154 
155     /**
156      * @plexus.requirement
157      */
158     private BuildResultDao buildResultDao;
159 
160     /**
161      * @plexus.requirement
162      */
163     private NotifierDao notifierDao;
164 
165     /**
166      * @plexus.requirement
167      */
168     private ProjectDao projectDao;
169 
170     /**
171      * @plexus.requirement
172      */
173     private ProjectGroupDao projectGroupDao;
174 
175     /**
176      * @plexus.requirement
177      */
178     private ScheduleDao scheduleDao;
179 
180     /**
181      * @plexus.requirement
182      */
183     private ContinuumReleaseResultDao releaseResultDao;
184 
185     /**
186      * @plexus.requirement
187      */
188     private ProjectScmRootDao projectScmRootDao;
189 
190     /**
191      * @plexus.requirement
192      */
193     private ContinuumInitializer initializer;
194 
195     /**
196      * @plexus.requirement
197      */
198     private SchedulesActivator schedulesActivator;
199 
200     /**
201      * @plexus.requirement
202      */
203     private InstallationService installationService;
204 
205     /**
206      * @plexus.requirement
207      */
208     private ProfileService profileService;
209 
210     /**
211      * @plexus.requirement
212      */
213     private BuildDefinitionService buildDefinitionService;
214 
215     // ----------------------------------------------------------------------
216     // Moved from core
217     // ----------------------------------------------------------------------
218 
219     /**
220      * @plexus.requirement
221      */
222     private ContinuumReleaseManager releaseManager;
223 
224     /**
225      * @plexus.requirement
226      */
227     private WorkingDirectoryService workingDirectoryService;
228 
229     /**
230      * @plexus.requirement
231      */
232     private BuildExecutorManager executorManager;
233 
234     /**
235      * @plexus.requirement role-hint="continuumUrl"
236      */
237     private ContinuumUrlValidator urlValidator;
238 
239     private boolean stopped = false;
240 
241     /**
242      * @plexus.requirement
243      */
244     private ContinuumPurgeManager purgeManager;
245 
246     /**
247      * @plexus.requirement
248      */
249     private RepositoryService repositoryService;
250 
251     /**
252      * @plexus.requirement
253      */
254     private PurgeConfigurationService purgeConfigurationService;
255 
256     /**
257      * @plexus.requirement
258      */
259     private TaskQueueManager taskQueueManager;
260 
261     /**
262      * @plexus.requirement role-hint="parallel"
263      */
264     private BuildsManager parallelBuildsManager;
265 
266     /**
267      * @plexus.requirement
268      */
269     private BuildQueueService buildQueueService;
270 
271     /**
272      * @plexus.requirement
273      */
274     private DistributedBuildManager distributedBuildManager;
275 
276     /**
277      * @plexus.requirement
278      */
279     private DistributedReleaseManager distributedReleaseManager;
280 
281     public DefaultContinuum()
282     {
283         Runtime.getRuntime().addShutdownHook( new Thread()
284         {
285             @Override
286             public void run()
287             {
288                 stopContinuum();
289             }
290         } );
291     }
292 
293     public ContinuumReleaseManager getReleaseManager()
294     {
295         return releaseManager;
296     }
297 
298     public ContinuumPurgeManager getPurgeManager()
299     {
300         return purgeManager;
301     }
302 
303     public RepositoryService getRepositoryService()
304     {
305         return repositoryService;
306     }
307 
308     public TaskQueueManager getTaskQueueManager()
309     {
310         return taskQueueManager;
311     }
312 
313     public PurgeConfigurationService getPurgeConfigurationService()
314     {
315         return purgeConfigurationService;
316     }
317 
318     public BuildsManager getBuildsManager()
319     {
320         return parallelBuildsManager;
321     }
322 
323     public DistributedReleaseManager getDistributedReleaseManager()
324     {
325         return distributedReleaseManager;
326     }
327 
328     // ----------------------------------------------------------------------
329     // Project Groups
330     // ----------------------------------------------------------------------
331     public ProjectGroup getProjectGroup( int projectGroupId )
332         throws ContinuumException
333     {
334         try
335         {
336             return projectGroupDao.getProjectGroup( projectGroupId );
337         }
338         catch ( ContinuumObjectNotFoundException e )
339         {
340             throw new ContinuumException( "invalid group id", e );
341         }
342         catch ( ContinuumStoreException e )
343         {
344             throw new ContinuumException( "Error while querying for project group.", e );
345         }
346     }
347 
348     public ProjectGroup getProjectGroupWithProjects( int projectGroupId )
349         throws ContinuumException
350     {
351         try
352         {
353             return projectGroupDao.getProjectGroupWithProjects( projectGroupId );
354         }
355         catch ( ContinuumStoreException e )
356         {
357             throw new ContinuumException( "could not find project group containing " + projectGroupId );
358         }
359     }
360 
361     public ProjectGroup getProjectGroupByProjectId( int projectId )
362         throws ContinuumException
363     {
364         try
365         {
366             return projectGroupDao.getProjectGroupByProjectId( projectId );
367         }
368         catch ( ContinuumObjectNotFoundException e )
369         {
370             throw new ContinuumException( "could not find project group containing " + projectId );
371         }
372     }
373 
374     public void removeProjectGroup( int projectGroupId )
375         throws ContinuumException
376     {
377         ProjectGroup projectGroup = getProjectGroupWithProjects( projectGroupId );
378 
379         if ( projectGroup != null )
380         {
381             List<Project> projects = projectGroup.getProjects();
382             int[] projectIds = new int[projects.size()];
383 
384             int idx = 0;
385             for ( Project project : projects )
386             {
387                 projectIds[idx] = project.getId();
388                 idx++;
389             }
390 
391             // check if any project is still being checked out
392             // canceling the checkout and proceeding with the delete results to a cannot delete directory error!
393             try
394             {
395                 if ( parallelBuildsManager.isAnyProjectCurrentlyBeingCheckedOut( projectIds ) )
396                 {
397                     throw new ContinuumException(
398                         "Unable to delete group. At least one project in group is still being checked out." );
399                 }
400 
401                 if ( parallelBuildsManager.isAnyProjectCurrentlyPreparingBuild( projectIds ) )
402                 {
403                     throw new ContinuumException(
404                         "Unable to delete group. The project group is still preparing build." );
405                 }
406 
407                 if ( parallelBuildsManager.isAnyProjectCurrentlyBuilding( projectIds ) )
408                 {
409                     throw new ContinuumException(
410                         "Unable to delete group. At least one project in group is still building." );
411                 }
412 
413                 if ( isAnyProjectsInReleaseStage( projects ) )
414                 {
415                     throw new ContinuumException(
416                         "Unable to delete group. At least one project in group is in release stage" );
417                 }
418             }
419             catch ( BuildManagerException e )
420             {
421                 throw new ContinuumException( "Unable to delete group.", e );
422             }
423 
424             for ( int projectId : projectIds )
425             {
426                 removeProject( projectId );
427             }
428 
429             // check if there are any project scm root left
430             List<ProjectScmRoot> scmRoots = getProjectScmRootByProjectGroup( projectGroupId );
431 
432             for ( ProjectScmRoot scmRoot : scmRoots )
433             {
434                 removeProjectScmRoot( scmRoot );
435             }
436 
437             log.info( "Remove project group " + projectGroup.getName() + "(" + projectGroup.getId() + ")" );
438 
439             Map<String, Object> context = new HashMap<String, Object>();
440             AbstractContinuumAction.setProjectGroupId( context, projectGroup.getId() );
441             executeAction( "remove-assignable-roles", context );
442 
443             projectGroupDao.removeProjectGroup( projectGroup );
444         }
445     }
446 
447     public void addProjectGroup( ProjectGroup projectGroup )
448         throws ContinuumException
449     {
450         ProjectGroup pg = null;
451 
452         try
453         {
454             pg = projectGroupDao.getProjectGroupByGroupId( projectGroup.getGroupId() );
455         }
456         catch ( ContinuumObjectNotFoundException e )
457         {
458             //since we want to add a new project group, we should be getting
459             //this exception
460         }
461         catch ( ContinuumStoreException e )
462         {
463             throw new ContinuumException( "Unable to add the requested project group", e );
464         }
465 
466         if ( pg == null )
467         {
468             //CONTINUUM-1502
469             projectGroup.setName( projectGroup.getName().trim() );
470             try
471             {
472                 ProjectGroup new_pg = projectGroupDao.addProjectGroup( projectGroup );
473 
474                 buildDefinitionService.addBuildDefinitionTemplateToProjectGroup( new_pg.getId(),
475                                                                                  buildDefinitionService.getDefaultMavenTwoBuildDefinitionTemplate() );
476 
477                 Map<String, Object> context = new HashMap<String, Object>();
478                 AbstractContinuumAction.setProjectGroupId( context, new_pg.getId() );
479                 executeAction( "add-assignable-roles", context );
480 
481                 log.info( "Added new project group: " + new_pg.getName() );
482             }
483             catch ( BuildDefinitionServiceException e )
484             {
485                 throw new ContinuumException( e.getMessage(), e );
486             }
487             catch ( ContinuumObjectNotFoundException e )
488             {
489                 throw new ContinuumException( e.getMessage(), e );
490             }
491 
492         }
493         else
494         {
495             throw new ContinuumException( "Unable to add the requested project group: groupId already exists." );
496         }
497 
498     }
499 
500     public List<ProjectGroup> getAllProjectGroups()
501     {
502         return new ArrayList<ProjectGroup>( projectGroupDao.getAllProjectGroups() );
503     }
504 
505     public ProjectGroup getProjectGroupByGroupId( String groupId )
506         throws ContinuumException
507     {
508         try
509         {
510             return projectGroupDao.getProjectGroupByGroupId( groupId );
511         }
512         catch ( ContinuumObjectNotFoundException e )
513         {
514             throw new ContinuumException( "Unable to find project group", e );
515         }
516         catch ( ContinuumStoreException e )
517         {
518             throw new ContinuumException( "Error retrieving", e );
519         }
520     }
521 
522     public ProjectGroup getProjectGroupByGroupIdWithBuildDetails( String groupId )
523         throws ContinuumException
524     {
525         try
526         {
527             return projectGroupDao.getProjectGroupByGroupIdWithBuildDetails( groupId );
528         }
529         catch ( ContinuumObjectNotFoundException e )
530         {
531             throw new ContinuumException( "Unable to find project group", e );
532         }
533         catch ( ContinuumStoreException e )
534         {
535             throw new ContinuumException( "Error retrieving", e );
536         }
537     }
538 
539     public List<ProjectGroup> getAllProjectGroupsWithRepository( int repositoryId )
540     {
541         return projectGroupDao.getProjectGroupByRepository( repositoryId );
542     }
543 
544     // ----------------------------------------------------------------------
545     // Projects
546     // ----------------------------------------------------------------------
547 
548     /**
549      * TODO: Remove this method
550      */
551     public Collection<Project> getProjects()
552         throws ContinuumException
553     {
554         return projectDao.getAllProjectsByName();
555     }
556 
557     /**
558      * TODO: Remove this method
559      */
560     public Collection<Project> getProjectsWithDependencies()
561         throws ContinuumException
562     {
563         return projectDao.getAllProjectsByNameWithDependencies();
564     }
565 
566     public Map<Integer, BuildResult> getLatestBuildResults( int projectGroupId )
567     {
568         Map<Integer, BuildResult> result = buildResultDao.getLatestBuildResultsByProjectGroupId( projectGroupId );
569 
570         if ( result == null )
571         {
572             result = new HashMap<Integer, BuildResult>();
573         }
574 
575         return result;
576     }
577 
578     public Map<Integer, BuildResult> getBuildResultsInSuccess( int projectGroupId )
579     {
580         Map<Integer, BuildResult> result = buildResultDao.getBuildResultsInSuccessByProjectGroupId( projectGroupId );
581 
582         if ( result == null )
583         {
584             result = new HashMap<Integer, BuildResult>();
585         }
586 
587         return result;
588     }
589 
590     public BuildResult getLatestBuildResultForProject( int projectId )
591     {
592         return buildResultDao.getLatestBuildResultForProject( projectId );
593     }
594 
595     public BuildResult getBuildResultByBuildNumber( int projectId, int buildNumber )
596         throws ContinuumException
597     {
598         List<BuildResult> builds = buildResultDao.getBuildResultByBuildNumber( projectId, buildNumber );
599 
600         return ( builds.isEmpty() ? null : builds.get( 0 ) );
601     }
602 
603     public List<BuildResult> getBuildResultsInRange( int projectGroupId, Date fromDate, Date toDate, int state,
604                                                      String triggeredBy )
605     {
606         return buildResultDao.getBuildResultsInRange( fromDate, toDate, state, triggeredBy, projectGroupId );
607     }
608 
609     // ----------------------------------------------------------------------
610     //
611     // ----------------------------------------------------------------------
612 
613     public void removeProject( int projectId )
614         throws ContinuumException
615     {
616         try
617         {
618             Project project = getProject( projectId );
619 
620             try
621             {
622                 if ( parallelBuildsManager.isProjectCurrentlyBeingCheckedOut( projectId ) )
623                 {
624                     throw new ContinuumException(
625                         "Unable to remove project " + projectId + " because it is currently being checked out" );
626                 }
627 
628                 if ( parallelBuildsManager.isProjectInAnyCurrentBuild( projectId ) )
629                 {
630                     throw new ContinuumException(
631                         "Unable to remove project " + projectId + " because it is currently building" );
632                 }
633             }
634             catch ( BuildManagerException e )
635             {
636                 throw new ContinuumException( e.getMessage(), e );
637             }
638 
639             if ( isProjectInReleaseStage( project ) )
640             {
641                 throw new ContinuumException(
642                     "Unable to remove project " + projectId + " because it is in release stage" );
643             }
644 
645             try
646             {
647                 parallelBuildsManager.removeProjectFromCheckoutQueue( projectId );
648 
649                 parallelBuildsManager.removeProjectFromBuildQueue( projectId );
650             }
651             catch ( BuildManagerException e )
652             {
653                 throw new ContinuumException( e.getMessage(), e );
654             }
655 
656             List<ContinuumReleaseResult> releaseResults = releaseResultDao.getContinuumReleaseResultsByProject(
657                 projectId );
658 
659             ProjectScmRoot scmRoot = getProjectScmRootByProject( projectId );
660 
661             try
662             {
663                 for ( ContinuumReleaseResult releaseResult : releaseResults )
664                 {
665                     releaseResultDao.removeContinuumReleaseResult( releaseResult );
666                 }
667 
668                 File releaseOutputDirectory = configurationService.getReleaseOutputDirectory(
669                     project.getProjectGroup().getId() );
670 
671                 if ( releaseOutputDirectory != null )
672                 {
673                     FileUtils.deleteDirectory( releaseOutputDirectory );
674                 }
675             }
676             catch ( ContinuumStoreException e )
677             {
678                 throw new ContinuumException( "Error while deleting continuum release result of project group", e );
679             }
680             catch ( IOException e )
681             {
682                 throw logAndCreateException( "Error while deleting project group release output directory.", e );
683             }
684 
685             log.info( "Remove project " + project.getName() + "(" + projectId + ")" );
686 
687             // remove dependencies first to avoid key clash with build results
688             project = projectDao.getProjectWithDependencies( projectId );
689             project.setParent( null );
690             project.getDependencies().clear();
691             projectDao.updateProject( project );
692 
693             Collection<BuildResult> buildResults = getBuildResultsForProject( projectId );
694 
695             for ( BuildResult br : buildResults )
696             {
697                 br.setBuildDefinition( null );
698                 //Remove all modified dependencies to prevent SQL errors
699                 br.getModifiedDependencies().clear();
700                 buildResultDao.updateBuildResult( br );
701                 removeBuildResult( br );
702             }
703 
704             File workingDirectory = getWorkingDirectory( projectId );
705 
706             FileUtils.deleteDirectory( workingDirectory );
707 
708             File buildOutputDirectory = configurationService.getBuildOutputDirectory( projectId );
709 
710             FileUtils.deleteDirectory( buildOutputDirectory );
711 
712             projectDao.removeProject( projectDao.getProject( projectId ) );
713 
714             removeProjectScmRoot( scmRoot );
715         }
716         catch ( ContinuumStoreException ex )
717         {
718             throw logAndCreateException( "Error while removing project in database.", ex );
719         }
720         catch ( IOException e )
721         {
722             throw logAndCreateException( "Error while deleting project working directory.", e );
723         }
724     }
725 
726     /**
727      * @see org.apache.maven.continuum.Continuum#checkoutProject(int)
728      */
729     public void checkoutProject( int projectId )
730         throws ContinuumException
731     {
732         Map<String, Object> context = new HashMap<String, Object>();
733 
734         AbstractContinuumAction.setProjectId( context, projectId );
735 
736         try
737         {
738             BuildDefinition buildDefinition = buildDefinitionDao.getDefaultBuildDefinition( projectId );
739             AbstractContinuumAction.setBuildDefinition( context, buildDefinition );
740 
741             executeAction( "add-project-to-checkout-queue", context );
742         }
743         catch ( ContinuumStoreException e )
744         {
745             throw new ContinuumException( e.getMessage(), e );
746         }
747     }
748 
749     public Project getProject( int projectId )
750         throws ContinuumException
751     {
752         try
753         {
754             return projectDao.getProject( projectId );
755         }
756         catch ( ContinuumStoreException ex )
757         {
758             throw logAndCreateException( "Exception while getting project '" + projectId + "'.", ex );
759         }
760     }
761 
762     public Project getProjectWithBuildDetails( int projectId )
763         throws ContinuumException
764     {
765         try
766         {
767             return projectDao.getProjectWithBuildDetails( projectId );
768         }
769         catch ( ContinuumStoreException ex )
770         {
771             throw logAndCreateException( "Exception while getting project '" + projectId + "'.", ex );
772         }
773     }
774 
775     public Map<Integer, ProjectGroupSummary> getProjectsSummaryByGroups()
776     {
777         return projectDao.getProjectsSummary();
778     }
779 
780     // ----------------------------------------------------------------------
781     // Building
782     // ----------------------------------------------------------------------
783 
784     public void buildProjects( String username )
785         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
786     {
787         buildProjects( new BuildTrigger( ContinuumProjectState.TRIGGER_FORCED, username ) );
788     }
789 
790     public void buildProjectsWithBuildDefinition( List<Project> projects, List<BuildDefinition> bds )
791         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
792     {
793         Collection<Project> filteredProjectsList = getProjectsNotInReleaseStage( projects );
794 
795         prepareBuildProjects( filteredProjectsList, bds, true, new BuildTrigger( ContinuumProjectState.TRIGGER_FORCED,
796                                                                                  "" ) );
797     }
798 
799     public void buildProjectsWithBuildDefinition( List<Project> projects, int buildDefinitionId )
800         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
801     {
802         Collection<Project> filteredProjectsList = getProjectsNotInReleaseStage( projects );
803 
804         prepareBuildProjects( filteredProjectsList, buildDefinitionId, new BuildTrigger(
805             ContinuumProjectState.TRIGGER_FORCED, "" ) );
806     }
807 
808     /**
809      * fire of the builds of all projects across all project groups using their default build definitions
810      * TODO:Remove this method
811      *
812      * @param buildTrigger
813      * @throws ContinuumException
814      */
815     public void buildProjects( BuildTrigger buildTrigger )
816         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
817     {
818         Collection<Project> projectsList = getProjectsInBuildOrder();
819 
820         Collection<Project> filteredProjectsList = getProjectsNotInReleaseStage( projectsList );
821 
822         prepareBuildProjects( filteredProjectsList, null, true, buildTrigger );
823     }
824 
825     /**
826      * fire off a build for all of the projects in a project group using their default builds
827      *
828      * @param projectGroupId
829      * @param buildTrigger
830      * @throws ContinuumException
831      */
832     public void buildProjectGroup( int projectGroupId, BuildTrigger buildTrigger )
833         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
834     {
835         List<BuildDefinition> groupDefaultBDs;
836 
837         if ( !isAnyProjectInGroupInReleaseStage( projectGroupId ) )
838         {
839             groupDefaultBDs = getDefaultBuildDefinitionsForProjectGroup( projectGroupId );
840 
841             buildProjectGroupWithBuildDefinition( projectGroupId, groupDefaultBDs, true, buildTrigger );
842         }
843     }
844 
845     /**
846      * fire off a build for all of the projects in a project group using their default builds.
847      *
848      * @param projectGroupId    the project group id
849      * @param buildDefinitionId the build definition id to use
850      * @param buildTrigger      the trigger state and the username
851      * @throws ContinuumException
852      */
853     public void buildProjectGroupWithBuildDefinition( int projectGroupId, int buildDefinitionId,
854                                                       BuildTrigger buildTrigger )
855         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
856     {
857         if ( !isAnyProjectInGroupInReleaseStage( projectGroupId ) )
858         {
859             List<BuildDefinition> bds = new ArrayList<BuildDefinition>();
860             BuildDefinition bd = getBuildDefinition( buildDefinitionId );
861             if ( bd != null )
862             {
863                 bds.add( bd );
864             }
865             buildProjectGroupWithBuildDefinition( projectGroupId, bds, false, buildTrigger );
866         }
867     }
868 
869     /**
870      * fire off a build for all of the projects in a project group using their default builds
871      *
872      * @param projectGroupId
873      * @param bds
874      * @param checkDefaultBuildDefinitionForProject
875      *
876      * @param buildTrigger
877      * @throws ContinuumException
878      */
879     private void buildProjectGroupWithBuildDefinition( int projectGroupId, List<BuildDefinition> bds,
880                                                        boolean checkDefaultBuildDefinitionForProject,
881                                                        BuildTrigger buildTrigger )
882         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
883     {
884         if ( !isAnyProjectInGroupInReleaseStage( projectGroupId ) )
885         {
886             Collection<Project> projectsList;
887 
888             projectsList = getProjectsInBuildOrder( projectDao.getProjectsWithDependenciesByGroupId( projectGroupId ) );
889 
890             buildTrigger.setTrigger( ContinuumProjectState.TRIGGER_FORCED );
891 
892             prepareBuildProjects( projectsList, bds, checkDefaultBuildDefinitionForProject, buildTrigger );
893         }
894     }
895 
896     /**
897      * takes a given schedule and determines which projects need to build
898      * <p/>
899      * The build order is determined by the dependencies
900      *
901      * @param schedule The schedule
902      * @throws ContinuumException
903      */
904     public void buildProjects( Schedule schedule )
905         throws ContinuumException
906     {
907         Collection<Project> projectsList;
908 
909         Map<Integer, Object> projectsMap;
910 
911         try
912         {
913             projectsMap = daoUtils.getAggregatedProjectIdsAndBuildDefinitionIdsBySchedule( schedule.getId() );
914 
915             if ( projectsMap == null || projectsMap.size() == 0 )
916             {
917                 log.debug( "no builds attached to schedule" );
918                 try
919                 {
920                     schedulesActivator.unactivateOrphanBuildSchedule( schedule );
921                 }
922                 catch ( SchedulesActivationException e )
923                 {
924                     log.debug( "Can't unactivate orphan shcedule for buildDefinitions" );
925                 }
926                 // We don't have projects attached to this schedule. This is because it's only is setting for a
927                 // templateBuildDefinition
928                 return;
929             }
930 
931             //TODO: As all projects are built in the same queue for a project group, it would be better to get them by
932             // project group and add them in queues in parallel to save few seconds
933             projectsList = getProjectsInBuildOrder();
934         }
935         catch ( ContinuumStoreException e )
936         {
937             throw new ContinuumException( "Can't get project list for schedule " + schedule.getName(), e );
938         }
939 
940         Map<ProjectScmRoot, Map<Integer, Integer>> map = new HashMap<ProjectScmRoot, Map<Integer, Integer>>();
941         List<ProjectScmRoot> sortedScmRoot = new ArrayList<ProjectScmRoot>();
942 
943         for ( Project project : projectsList )
944         {
945             List<Integer> buildDefIds = (List<Integer>) projectsMap.get( project.getId() );
946             int projectId = project.getId();
947 
948             if ( buildDefIds != null && !buildDefIds.isEmpty() )
949             {
950                 for ( Integer buildDefId : buildDefIds )
951                 {
952                     if ( isProjectOkToBuild( project.getId(), buildDefId ) )
953                     {
954                         ProjectScmRoot scmRoot = getProjectScmRootByProject( project.getId() );
955 
956                         Map<Integer, Integer> projectsAndBuildDefinitionsMap = map.get( scmRoot );
957 
958                         if ( projectsAndBuildDefinitionsMap == null )
959                         {
960                             projectsAndBuildDefinitionsMap = new HashMap<Integer, Integer>();
961                         }
962 
963                         projectsAndBuildDefinitionsMap.put( projectId, buildDefId );
964 
965                         map.put( scmRoot, projectsAndBuildDefinitionsMap );
966 
967                         if ( !sortedScmRoot.contains( scmRoot ) )
968                         {
969                             sortedScmRoot.add( scmRoot );
970                         }
971                     }
972                     else
973                     {
974                         log.info(
975                             "Not queueing the build with projectId={} and buildDefinitionId={} because it is already building",
976                             projectId, buildDefId );
977                     }
978                 }
979             }
980         }
981 
982         BuildTrigger buildTrigger = new BuildTrigger( ContinuumProjectState.TRIGGER_SCHEDULED, schedule.getName() );
983 
984         for ( ProjectScmRoot scmRoot : sortedScmRoot )
985         {
986             try
987             {
988                 prepareBuildProjects( map.get( scmRoot ), buildTrigger, scmRoot.getScmRootAddress(),
989                                       scmRoot.getProjectGroup().getId(), scmRoot.getId(), sortedScmRoot );
990             }
991             catch ( NoBuildAgentException e )
992             {
993                 log.error( "Unable to build projects in project group " + scmRoot.getProjectGroup().getName() +
994                                " because there is no build agent configured" );
995             }
996             catch ( NoBuildAgentInGroupException e )
997             {
998                 log.error( "Unable to build projects in project group " + scmRoot.getProjectGroup().getName() +
999                                " because there is no build agent configured in build agent group" );
1000             }
1001         }
1002     }
1003 
1004     public void buildProject( int projectId, String username )
1005         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
1006     {
1007         buildProject( projectId, new BuildTrigger( ContinuumProjectState.TRIGGER_FORCED, username ) );
1008     }
1009 
1010     public void buildProjectWithBuildDefinition( int projectId, int buildDefinitionId, BuildTrigger buildTrigger )
1011         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
1012     {
1013         buildTrigger.setTrigger( ContinuumProjectState.TRIGGER_FORCED );
1014         buildProject( projectId, buildDefinitionId, buildTrigger );
1015     }
1016 
1017     public void buildProject( int projectId, BuildTrigger buildTrigger )
1018         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
1019     {
1020         Project project = getProject( projectId );
1021         if ( isProjectInReleaseStage( project ) )
1022         {
1023             throw new ContinuumException( "Project (id=" + projectId + ") is currently in release stage." );
1024         }
1025 
1026         BuildDefinition buildDef = getDefaultBuildDefinition( projectId );
1027 
1028         if ( buildDef == null )
1029         {
1030             throw new ContinuumException( "Project (id=" + projectId + ") doesn't have a default build definition." );
1031         }
1032 
1033         if ( !isProjectOkToBuild( projectId, buildDef.getId() ) )
1034         {
1035             log.info(
1036                 "Not queueing the build with projectId={} and buildDefinitionId={} because it is already building",
1037                 projectId, buildDef.getId() );
1038             return;
1039         }
1040 
1041         Map<Integer, Integer> projectsBuildDefinitionsMap = new HashMap<Integer, Integer>();
1042         projectsBuildDefinitionsMap.put( projectId, buildDef.getId() );
1043 
1044         ProjectScmRoot scmRoot = getProjectScmRootByProject( projectId );
1045         List<ProjectScmRoot> sortedScmRoot = new ArrayList<ProjectScmRoot>();
1046         sortedScmRoot.add( scmRoot );
1047 
1048         prepareBuildProjects( projectsBuildDefinitionsMap, buildTrigger, scmRoot.getScmRootAddress(),
1049                               scmRoot.getProjectGroup().getId(), scmRoot.getId(), sortedScmRoot );
1050     }
1051 
1052     public void buildProject( int projectId, int buildDefinitionId, BuildTrigger buildTrigger )
1053         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
1054     {
1055         Project project = getProject( projectId );
1056         if ( isProjectInReleaseStage( project ) )
1057         {
1058             throw new ContinuumException( "Project (id=" + projectId + ") is currently in release stage." );
1059         }
1060 
1061         if ( !isProjectOkToBuild( projectId, buildDefinitionId ) )
1062         {
1063             log.info(
1064                 "Not queueing the build with projectId={} and buildDefinitionId={} because it is already building",
1065                 projectId, buildDefinitionId );
1066             return;
1067         }
1068 
1069         Map<Integer, Integer> projectsBuildDefinitionsMap = new HashMap<Integer, Integer>();
1070         projectsBuildDefinitionsMap.put( projectId, buildDefinitionId );
1071 
1072         ProjectScmRoot scmRoot = getProjectScmRootByProject( projectId );
1073         List<ProjectScmRoot> sortedScmRoot = new ArrayList<ProjectScmRoot>();
1074         sortedScmRoot.add( scmRoot );
1075 
1076         prepareBuildProjects( projectsBuildDefinitionsMap, buildTrigger, scmRoot.getScmRootAddress(),
1077                               scmRoot.getProjectGroup().getId(), scmRoot.getId(), sortedScmRoot );
1078     }
1079 
1080     public BuildResult getBuildResult( int buildId )
1081         throws ContinuumException
1082     {
1083         try
1084         {
1085             return buildResultDao.getBuildResult( buildId );
1086         }
1087         catch ( ContinuumStoreException e )
1088         {
1089             throw logAndCreateException( "Exception while getting build result for project.", e );
1090         }
1091     }
1092 
1093     public void removeBuildResult( int buildId )
1094         throws ContinuumException
1095     {
1096         BuildResult buildResult = getBuildResult( buildId );
1097 
1098         // check first if build result is currently being used by a building project
1099         Project project = buildResult.getProject();
1100         BuildResult bResult = getLatestBuildResultForProject( project.getId() );
1101 
1102         try
1103         {
1104             if ( bResult != null && buildResult.getId() == bResult.getId() &&
1105                 parallelBuildsManager.isProjectInAnyCurrentBuild( project.getId() ) )
1106             {
1107                 throw new ContinuumException(
1108                     "Unable to remove build result because it is currently being used by" + "a building project " +
1109                         project.getId() );
1110             }
1111         }
1112         catch ( BuildManagerException e )
1113         {
1114             throw new ContinuumException( e.getMessage(), e );
1115         }
1116 
1117         buildResult.getModifiedDependencies().clear();
1118         buildResult.setBuildDefinition( null );
1119 
1120         try
1121         {
1122             buildResultDao.updateBuildResult( buildResult );
1123         }
1124         catch ( ContinuumStoreException e )
1125         {
1126             throw logAndCreateException( "Error while removing build result in database.", e );
1127         }
1128         removeBuildResult( buildResult );
1129     }
1130 
1131 
1132     private void removeBuildResult( BuildResult buildResult )
1133     {
1134         buildResultDao.removeBuildResult( buildResult );
1135 
1136         // cleanup some files
1137         try
1138         {
1139             File buildOutputDirectory = getConfiguration().getBuildOutputDirectory( buildResult.getProject().getId() );
1140             File buildDirectory = new File( buildOutputDirectory, Integer.toString( buildResult.getId() ) );
1141 
1142             if ( buildDirectory.exists() )
1143             {
1144                 FileUtils.deleteDirectory( buildDirectory );
1145             }
1146             File buildOutputFile = getConfiguration().getBuildOutputFile( buildResult.getId(),
1147                                                                           buildResult.getProject().getId() );
1148             if ( buildOutputFile.exists() )
1149             {
1150                 FileUtils.forceDelete( buildOutputFile );
1151             }
1152         }
1153         catch ( ConfigurationException e )
1154         {
1155             log.info( "skip error during cleanup build files " + e.getMessage(), e );
1156         }
1157         catch ( IOException e )
1158         {
1159             log.info( "skip IOException during cleanup build files " + e.getMessage(), e );
1160         }
1161 
1162     }
1163 
1164     public String getBuildOutput( int projectId, int buildId )
1165         throws ContinuumException
1166     {
1167         try
1168         {
1169             return configurationService.getBuildOutput( buildId, projectId );
1170         }
1171         catch ( ConfigurationException e )
1172         {
1173             throw logAndCreateException( "Exception while getting build result for project.", e );
1174         }
1175     }
1176 
1177     /**
1178      * TODO: Must be done by build definition
1179      */
1180     public List<ChangeSet> getChangesSinceLastSuccess( int projectId, int buildResultId )
1181         throws ContinuumException
1182     {
1183         BuildResult previousBuildResult = null;
1184         try
1185         {
1186             previousBuildResult = buildResultDao.getPreviousBuildResultInSuccess( projectId, buildResultId );
1187         }
1188         catch ( ContinuumStoreException e )
1189         {
1190             //No previous build in success, Nothing to do
1191         }
1192         long startTime = previousBuildResult == null ? 0 : previousBuildResult.getStartTime();
1193         ArrayList<BuildResult> buildResults = new ArrayList<BuildResult>(
1194             buildResultDao.getBuildResultsForProjectWithDetails( projectId, startTime, buildResultId ) );
1195 
1196         Collections.reverse( buildResults );
1197 
1198         Iterator<BuildResult> buildResultsIterator = buildResults.iterator();
1199 
1200         boolean stop = false;
1201 
1202         //TODO: Shouldn't be used now with the previous call of buildResultDao.getBuildResultsForProjectWithDetails
1203         while ( !stop )
1204         {
1205             if ( buildResultsIterator.hasNext() )
1206             {
1207                 BuildResult buildResult = buildResultsIterator.next();
1208 
1209                 if ( buildResult.getId() == buildResultId )
1210                 {
1211                     stop = true;
1212                 }
1213             }
1214             else
1215             {
1216                 stop = true;
1217             }
1218         }
1219 
1220         if ( !buildResultsIterator.hasNext() )
1221         {
1222             return null;
1223         }
1224 
1225         BuildResult buildResult = buildResultsIterator.next();
1226 
1227         List<ChangeSet> changes = null;
1228 
1229         while ( buildResult.getState() != ContinuumProjectState.OK )
1230         {
1231             if ( changes == null )
1232             {
1233                 changes = new ArrayList<ChangeSet>();
1234             }
1235 
1236             ScmResult scmResult = buildResult.getScmResult();
1237 
1238             if ( scmResult != null )
1239             {
1240                 changes.addAll( scmResult.getChanges() );
1241             }
1242 
1243             if ( !buildResultsIterator.hasNext() )
1244             {
1245                 return changes;
1246             }
1247 
1248             buildResult = buildResultsIterator.next();
1249         }
1250 
1251         if ( changes == null )
1252         {
1253             changes = Collections.EMPTY_LIST;
1254         }
1255 
1256         return changes;
1257     }
1258 
1259     // ----------------------------------------------------------------------
1260     //
1261     // ----------------------------------------------------------------------
1262 
1263     /**
1264      * TODO: Remove this method when it won't be used
1265      */
1266     private List<Project> getProjectsInBuildOrder()
1267         throws ContinuumException
1268     {
1269         return getProjectsInBuildOrder( getProjectsWithDependencies() );
1270     }
1271 
1272     /**
1273      * take a collection of projects and sort for order
1274      *
1275      * @param projects
1276      * @return
1277      */
1278     public List<Project> getProjectsInBuildOrder( Collection<Project> projects )
1279     {
1280         if ( projects == null || projects.isEmpty() )
1281         {
1282             return new ArrayList<Project>();
1283         }
1284 
1285         return ProjectSorter.getSortedProjects( projects, log );
1286     }
1287 
1288     // ----------------------------------------------------------------------
1289     // Maven 1.x projects
1290     // ----------------------------------------------------------------------
1291 
1292     public ContinuumProjectBuildingResult addMavenOneProject( String metadataUrl, int projectGroupId )
1293         throws ContinuumException
1294     {
1295         return addMavenOneProject( metadataUrl, projectGroupId, true );
1296     }
1297 
1298     public ContinuumProjectBuildingResult addMavenOneProject( String metadataUrl, int projectGroupId,
1299                                                               boolean checkProtocol )
1300         throws ContinuumException
1301     {
1302         return addMavenOneProject( metadataUrl, projectGroupId, checkProtocol, false );
1303     }
1304 
1305     public ContinuumProjectBuildingResult addMavenOneProject( String metadataUrl, int projectGroupId,
1306                                                               boolean checkProtocol, boolean useCredentialsCache )
1307         throws ContinuumException
1308     {
1309         try
1310         {
1311             return addMavenOneProject( metadataUrl, projectGroupId, checkProtocol, useCredentialsCache,
1312                                        buildDefinitionService.getDefaultMavenOneBuildDefinitionTemplate().getId() );
1313         }
1314         catch ( BuildDefinitionServiceException e )
1315         {
1316             throw new ContinuumException( e.getMessage(), e );
1317         }
1318     }
1319 
1320     public ContinuumProjectBuildingResult addMavenOneProject( String metadataUrl, int projectGroupId,
1321                                                               boolean checkProtocol, boolean useCredentialsCache,
1322                                                               int buildDefinitionTemplateId )
1323         throws ContinuumException
1324     {
1325         return executeAddProjectsFromMetadataActivity( metadataUrl, MavenOneContinuumProjectBuilder.ID, projectGroupId,
1326                                                        checkProtocol, useCredentialsCache, true,
1327                                                        buildDefinitionTemplateId, false );
1328     }
1329 
1330     // ----------------------------------------------------------------------
1331     // Maven 2.x projects
1332     // ----------------------------------------------------------------------
1333 
1334     public ContinuumProjectBuildingResult addMavenTwoProject( String metadataUrl )
1335         throws ContinuumException
1336     {
1337         return addMavenTwoProject( metadataUrl, true );
1338     }
1339 
1340     public ContinuumProjectBuildingResult addMavenTwoProject( String metadataUrl, boolean checkProtocol )
1341         throws ContinuumException
1342     {
1343         try
1344         {
1345             return executeAddProjectsFromMetadataActivity( metadataUrl, MavenTwoContinuumProjectBuilder.ID, -1,
1346                                                            checkProtocol,
1347                                                            buildDefinitionService.getDefaultMavenTwoBuildDefinitionTemplate().getId() );
1348         }
1349         catch ( BuildDefinitionServiceException e )
1350         {
1351             throw new ContinuumException( e.getMessage(), e );
1352         }
1353     }
1354 
1355     public ContinuumProjectBuildingResult addMavenTwoProject( String metadataUrl, int projectGroupId )
1356         throws ContinuumException
1357     {
1358         return addMavenTwoProject( metadataUrl, projectGroupId, true );
1359     }
1360 
1361     public ContinuumProjectBuildingResult addMavenTwoProject( String metadataUrl, int projectGroupId,
1362                                                               boolean checkProtocol )
1363         throws ContinuumException
1364     {
1365         return addMavenTwoProject( metadataUrl, projectGroupId, checkProtocol, false );
1366     }
1367 
1368     public ContinuumProjectBuildingResult addMavenTwoProject( String metadataUrl, int projectGroupId,
1369                                                               boolean checkProtocol, boolean useCredentialsCache )
1370         throws ContinuumException
1371     {
1372         try
1373         {
1374             return executeAddProjectsFromMetadataActivity( metadataUrl, MavenTwoContinuumProjectBuilder.ID,
1375                                                            projectGroupId, checkProtocol, useCredentialsCache, true,
1376                                                            buildDefinitionService.getDefaultMavenTwoBuildDefinitionTemplate().getId(),
1377                                                            false );
1378         }
1379         catch ( BuildDefinitionServiceException e )
1380         {
1381             throw new ContinuumException( e.getMessage(), e );
1382         }
1383     }
1384 
1385     public ContinuumProjectBuildingResult addMavenTwoProject( String metadataUrl, int projectGroupId,
1386                                                               boolean checkProtocol, boolean useCredentialsCache,
1387                                                               boolean recursiveProjects )
1388         throws ContinuumException
1389     {
1390         try
1391         {
1392             return executeAddProjectsFromMetadataActivity( metadataUrl, MavenTwoContinuumProjectBuilder.ID,
1393                                                            projectGroupId, checkProtocol, useCredentialsCache,
1394                                                            recursiveProjects,
1395                                                            buildDefinitionService.getDefaultMavenTwoBuildDefinitionTemplate().getId(),
1396                                                            false );
1397         }
1398         catch ( BuildDefinitionServiceException e )
1399         {
1400             throw new ContinuumException( e.getMessage(), e );
1401         }
1402     }
1403 
1404     public ContinuumProjectBuildingResult addMavenTwoProject( String metadataUrl, int projectGroupId,
1405                                                               boolean checkProtocol, boolean useCredentialsCache,
1406                                                               boolean recursiveProjects, int buildDefinitionTemplateId,
1407                                                               boolean checkoutInSingleDirectory )
1408         throws ContinuumException
1409     {
1410         return executeAddProjectsFromMetadataActivity( metadataUrl, MavenTwoContinuumProjectBuilder.ID, projectGroupId,
1411                                                        checkProtocol, useCredentialsCache, recursiveProjects,
1412                                                        buildDefinitionTemplateId, checkoutInSingleDirectory );
1413     }
1414 
1415     // ----------------------------------------------------------------------
1416     // Shell projects
1417     // ----------------------------------------------------------------------
1418 
1419     public int addProject( Project project, String executorId, int groupId )
1420         throws ContinuumException
1421     {
1422         return addProject( project, executorId, groupId, -1 );
1423     }
1424 
1425     /**
1426      * @see org.apache.maven.continuum.Continuum#addProject(org.apache.maven.continuum.model.project.Project, java.lang.String, int, int)
1427      */
1428     public int addProject( Project project, String executorId, int groupId, int buildDefinitionTemplateId )
1429         throws ContinuumException
1430     {
1431         project.setExecutorId( executorId );
1432 
1433         return executeAddProjectFromScmActivity( project, groupId, buildDefinitionTemplateId );
1434     }
1435 
1436     // ----------------------------------------------------------------------
1437     // Activities. These should end up as workflows in werkflow
1438     // ----------------------------------------------------------------------
1439 
1440     private int executeAddProjectFromScmActivity( Project project, int groupId, int buildDefinitionTemplateId )
1441         throws ContinuumException
1442     {
1443         String executorId = project.getExecutorId();
1444 
1445         ProjectGroup projectGroup = getProjectGroupWithBuildDetails( groupId );
1446 
1447         Map<String, Object> context = new HashMap<String, Object>();
1448 
1449         String scmUrl = project.getScmUrl();
1450 
1451         List<ProjectScmRoot> scmRoots = getProjectScmRootByProjectGroup( groupId );
1452 
1453         boolean found = false;
1454 
1455         for ( ProjectScmRoot scmRoot : scmRoots )
1456         {
1457             if ( scmUrl.startsWith( scmRoot.getScmRootAddress() ) )
1458             {
1459                 found = true;
1460                 break;
1461             }
1462         }
1463 
1464         if ( !found )
1465         {
1466             createProjectScmRoot( projectGroup, scmUrl );
1467         }
1468 
1469         // ----------------------------------------------------------------------
1470         //
1471         // ----------------------------------------------------------------------
1472 
1473         AbstractContinuumAction.setWorkingDirectory( context, getWorkingDirectory() );
1474 
1475         AbstractContinuumAction.setUnvalidatedProject( context, project );
1476 
1477         AbstractContinuumAction.setUnvalidatedProjectGroup( context, projectGroup );
1478 
1479         AbstractContinuumAction.setProjectGroupId( context, projectGroup.getId() );
1480 
1481         StoreProjectAction.setUseScmCredentialsCache( context, project.isScmUseCache() );
1482 
1483         // set for initial checkout
1484         String scmUsername = project.getScmUsername();
1485         String scmPassword = project.getScmPassword();
1486 
1487         if ( scmUsername != null && !StringUtils.isEmpty( scmUsername ) )
1488         {
1489             CheckoutProjectContinuumAction.setScmUsername( context, scmUsername );
1490         }
1491 
1492         if ( scmPassword != null && !StringUtils.isEmpty( scmPassword ) )
1493         {
1494             CheckoutProjectContinuumAction.setScmPassword( context, scmPassword );
1495         }
1496 
1497         executeAction( "validate-project", context );
1498 
1499         executeAction( "store-project", context );
1500 
1501         try
1502         {
1503             BuildDefinitionTemplate bdt;
1504 
1505             if ( executorId.equalsIgnoreCase( ContinuumBuildExecutorConstants.ANT_BUILD_EXECUTOR ) )
1506             {
1507                 if ( buildDefinitionTemplateId <= 0 )
1508                 {
1509                     bdt = buildDefinitionService.getDefaultAntBuildDefinitionTemplate();
1510                 }
1511                 else
1512                 {
1513                     bdt = buildDefinitionService.getBuildDefinitionTemplate( buildDefinitionTemplateId );
1514                 }
1515             }
1516             else
1517             {
1518                 //shell default
1519                 if ( buildDefinitionTemplateId <= 0 )
1520                 {
1521                     bdt = buildDefinitionService.getDefaultShellBuildDefinitionTemplate();
1522                 }
1523                 else
1524                 {
1525                     bdt = buildDefinitionService.getBuildDefinitionTemplate( buildDefinitionTemplateId );
1526                 }
1527             }
1528 
1529             buildDefinitionService.addTemplateInProject( bdt.getId(), getProject( AbstractContinuumAction.getProjectId(
1530                 context ) ) );
1531         }
1532         catch ( BuildDefinitionServiceException e )
1533         {
1534             throw new ContinuumException( e.getMessage(), e );
1535         }
1536 
1537         if ( !configurationService.isDistributedBuildEnabled() )
1538         {
1539             // used by BuildManager to determine on which build queue will the project be put
1540             BuildDefinition bd = (BuildDefinition) getProjectWithBuildDetails( AbstractContinuumAction.getProjectId(
1541                 context ) ).getBuildDefinitions().get( 0 );
1542             AbstractContinuumAction.setBuildDefinition( context, bd );
1543 
1544             executeAction( "add-project-to-checkout-queue", context );
1545         }
1546 
1547         executeAction( "add-assignable-roles", context );
1548 
1549         return AbstractContinuumAction.getProjectId( context );
1550     }
1551 
1552     private ContinuumProjectBuildingResult executeAddProjectsFromMetadataActivity( String metadataUrl,
1553                                                                                    String projectBuilderId,
1554                                                                                    int projectGroupId,
1555                                                                                    boolean checkProtocol,
1556                                                                                    int buildDefinitionTemplateId )
1557         throws ContinuumException
1558     {
1559         return executeAddProjectsFromMetadataActivity( metadataUrl, projectBuilderId, projectGroupId, checkProtocol,
1560                                                        false, false, buildDefinitionTemplateId, false );
1561     }
1562 
1563 
1564     protected ContinuumProjectBuildingResult executeAddProjectsFromMetadataActivity( String metadataUrl,
1565                                                                                      String projectBuilderId,
1566                                                                                      int projectGroupId,
1567                                                                                      boolean checkProtocol,
1568                                                                                      boolean useCredentialsCache,
1569                                                                                      boolean loadRecursiveProjects,
1570                                                                                      int buildDefinitionTemplateId,
1571                                                                                      boolean addAssignableRoles,
1572                                                                                      boolean checkoutInSingleDirectory )
1573         throws ContinuumException
1574     {
1575         if ( checkProtocol )
1576         {
1577             if ( !urlValidator.validate( metadataUrl ) )
1578             {
1579                 ContinuumProjectBuildingResult res = new ContinuumProjectBuildingResult();
1580                 res.addError( ContinuumProjectBuildingResult.ERROR_PROTOCOL_NOT_ALLOWED );
1581                 return res;
1582             }
1583         }
1584 
1585         Map<String, Object> context = new HashMap<String, Object>();
1586 
1587         CreateProjectsFromMetadataAction.setProjectBuilderId( context, projectBuilderId );
1588 
1589         CreateProjectsFromMetadataAction.setUrl( context, metadataUrl );
1590 
1591         CreateProjectsFromMetadataAction.setLoadRecursiveProject( context, loadRecursiveProjects );
1592 
1593         StoreProjectAction.setUseScmCredentialsCache( context, useCredentialsCache );
1594 
1595         AbstractContinuumAction.setWorkingDirectory( context, getWorkingDirectory() );
1596 
1597         CreateProjectsFromMetadataAction.setCheckoutProjectsInSingleDirectory( context, checkoutInSingleDirectory );
1598 
1599         // CreateProjectsFromMetadataAction will check null and use default
1600         if ( buildDefinitionTemplateId > 0 )
1601         {
1602             try
1603             {
1604                 AbstractContinuumAction.setBuildDefinitionTemplate( context,
1605                                                                     buildDefinitionService.getBuildDefinitionTemplate(
1606                                                                         buildDefinitionTemplateId ) );
1607             }
1608             catch ( BuildDefinitionServiceException e )
1609             {
1610                 throw new ContinuumException( e.getMessage(), e );
1611             }
1612         }
1613         // ----------------------------------------------------------------------
1614         // Create the projects from the URL
1615         // ----------------------------------------------------------------------
1616 
1617         ProjectGroup projectGroup;
1618 
1619         if ( projectGroupId != -1 )
1620         {
1621             CreateProjectsFromMetadataAction.setProjectGroupId( context, projectGroupId );
1622         }
1623 
1624         executeAction( "create-projects-from-metadata", context );
1625 
1626         ContinuumProjectBuildingResult result = CreateProjectsFromMetadataAction.getProjectBuildingResult( context );
1627 
1628         if ( log.isInfoEnabled() )
1629         {
1630             if ( result.getProjects() != null )
1631             {
1632                 log.info( "Created " + result.getProjects().size() + " projects." );
1633             }
1634             if ( result.getProjectGroups() != null )
1635             {
1636                 log.info( "Created " + result.getProjectGroups().size() + " project groups." );
1637             }
1638             log.info( result.getErrors().size() + " errors." );
1639 
1640             // ----------------------------------------------------------------------
1641             // Look for any errors.
1642             // ----------------------------------------------------------------------
1643 
1644             if ( result.hasErrors() )
1645             {
1646                 log.info( result.getErrors().size() + " errors during project add: " );
1647                 log.info( result.getErrorsAsString() );
1648                 return result;
1649             }
1650         }
1651 
1652         // ----------------------------------------------------------------------
1653         // Save any new project groups that we've found. Currently all projects
1654         // will go into the first project group in the list.
1655         // ----------------------------------------------------------------------
1656 
1657         if ( result.getProjectGroups().size() != 1 )
1658         {
1659             throw new ContinuumException( "The project building result has to contain exactly one project group." );
1660         }
1661 
1662         boolean projectGroupCreation = false;
1663 
1664         try
1665         {
1666             if ( projectGroupId == -1 )
1667             {
1668                 projectGroup = result.getProjectGroups().iterator().next();
1669 
1670                 try
1671                 {
1672                     projectGroup = projectGroupDao.getProjectGroupByGroupId( projectGroup.getGroupId() );
1673 
1674                     projectGroupId = projectGroup.getId();
1675 
1676                     log.info( "Using existing project group with the group id: '" + projectGroup.getGroupId() + "'." );
1677                 }
1678                 catch ( ContinuumObjectNotFoundException e )
1679                 {
1680                     log.info( "Creating project group with the group id: '" + projectGroup.getGroupId() + "'." );
1681 
1682                     Map<String, Object> pgContext = new HashMap<String, Object>();
1683 
1684                     AbstractContinuumAction.setWorkingDirectory( pgContext, getWorkingDirectory() );
1685 
1686                     AbstractContinuumAction.setUnvalidatedProjectGroup( pgContext, projectGroup );
1687 
1688                     executeAction( "validate-project-group", pgContext );
1689 
1690                     executeAction( "store-project-group", pgContext );
1691 
1692                     projectGroupId = AbstractContinuumAction.getProjectGroupId( pgContext );
1693 
1694                     projectGroupCreation = true;
1695                 }
1696             }
1697 
1698             projectGroup = projectGroupDao.getProjectGroupWithBuildDetailsByProjectGroupId( projectGroupId );
1699 
1700             //String url = CreateProjectsFromMetadataAction.getUrl( context );
1701             String url = AbstractContinuumAction.getProjectScmRootUrl( context, null );
1702 
1703             List<ProjectScmRoot> scmRoots = getProjectScmRootByProjectGroup( projectGroup.getId() );
1704 
1705             boolean found = false;
1706 
1707             for ( ProjectScmRoot scmRoot : scmRoots )
1708             {
1709                 if ( url.startsWith( scmRoot.getScmRootAddress() ) )
1710                 {
1711                     found = true;
1712                     break;
1713                 }
1714             }
1715 
1716             if ( !found )
1717             {
1718                 createProjectScmRoot( projectGroup, url );
1719             }
1720 
1721             /* add the project group loaded from database, which has more info, like id */
1722             result.getProjectGroups().remove( 0 );
1723             result.getProjectGroups().add( projectGroup );
1724         }
1725         catch ( ContinuumStoreException e )
1726         {
1727             throw new ContinuumException( "Error while querying for project group.", e );
1728         }
1729 
1730         // ----------------------------------------------------------------------
1731         // Save all the projects if recursive mode asked
1732         // TODO: Validate all the projects before saving them
1733         // ----------------------------------------------------------------------
1734 
1735         List<Project> projects = result.getProjects();
1736 
1737         String scmUserName = null;
1738         String scmPassword = null;
1739 
1740         for ( Project project : projects )
1741         {
1742             checkForDuplicateProjectInGroup( projectGroup, project, result );
1743 
1744             if ( result.hasErrors() )
1745             {
1746                 log.info( result.getErrors().size() + " errors during project add: " );
1747                 log.info( result.getErrorsAsString() );
1748                 return result;
1749             }
1750 
1751             project.setScmUseCache( useCredentialsCache );
1752 
1753             // values backup for first checkout
1754             scmUserName = project.getScmUsername();
1755             scmPassword = project.getScmPassword();
1756             // CONTINUUM-1792 : we don't store it
1757             if ( useCredentialsCache )
1758             {
1759                 project.setScmUsername( null );
1760                 project.setScmPassword( null );
1761             }
1762 
1763             projectGroup.addProject( project );
1764         }
1765 
1766         try
1767         {
1768             projectGroupDao.updateProjectGroup( projectGroup );
1769 
1770             if ( !checkoutInSingleDirectory )
1771             {
1772                 for ( Project project : projects )
1773                 {
1774                     context = new HashMap<String, Object>();
1775 
1776                     Project fetchedProject = projectDao.getProjectWithBuildDetails( project.getId() );
1777 
1778                     addProjectToCheckoutQueue( projectBuilderId, buildDefinitionTemplateId, context,
1779                                                projectGroupCreation, scmUserName, scmPassword, project,
1780                                                isDefaultProjectBuildDefSet( fetchedProject ) );
1781                 }
1782             }
1783             else
1784             {
1785                 Project project = result.getRootProject();
1786 
1787                 if ( project != null )
1788                 {
1789                     String scmRootUrl = AbstractContinuumAction.getProjectScmRootUrl( context, null );
1790                     context = new HashMap<String, Object>();
1791 
1792                     AbstractContinuumAction.setProjectScmRootUrl( context, scmRootUrl );
1793 
1794                     List<Project> projectsWithSimilarScmRoot = new ArrayList<Project>();
1795                     for ( Project projectWithSimilarScmRoot : projects )
1796                     {
1797                         projectsWithSimilarScmRoot.add( projectWithSimilarScmRoot );
1798                     }
1799 
1800                     AbstractContinuumAction.setListOfProjectsInGroupWithCommonScmRoot( context,
1801                                                                                        projectsWithSimilarScmRoot );
1802 
1803                     Project fetchedProject = projectDao.getProjectWithBuildDetails( project.getId() );
1804 
1805                     addProjectToCheckoutQueue( projectBuilderId, buildDefinitionTemplateId, context,
1806                                                projectGroupCreation, scmUserName, scmPassword, project,
1807                                                isDefaultProjectBuildDefSet( fetchedProject ) );
1808                 }
1809             }
1810         }
1811         catch ( BuildDefinitionServiceException e )
1812         {
1813             throw new ContinuumException( "Error attaching buildDefintionTemplate to project ", e );
1814         }
1815         catch ( ContinuumStoreException e )
1816         {
1817             throw new ContinuumException( "Error adding projects from modules", e );
1818         }
1819 
1820         AbstractContinuumAction.setProjectGroupId( context, projectGroup.getId() );
1821         // add the relevant security administration roles for this project
1822         if ( addAssignableRoles )
1823         {
1824             executeAction( "add-assignable-roles", context );
1825         }
1826         return result;
1827     }
1828 
1829     private boolean isDefaultProjectBuildDefSet( Project project )
1830     {
1831         for ( BuildDefinition bd : project.getBuildDefinitions() )
1832         {
1833             if ( bd.isDefaultForProject() )
1834             {
1835                 return true;
1836             }
1837         }
1838 
1839         return false;
1840     }
1841 
1842     private void addProjectToCheckoutQueue( String projectBuilderId, int buildDefinitionTemplateId,
1843                                             Map<String, Object> context, boolean projectGroupCreation,
1844                                             String scmUserName, String scmPassword, Project project,
1845                                             boolean defaultProjectBuildDefSet )
1846         throws BuildDefinitionServiceException, ContinuumStoreException, ContinuumException
1847     {
1848         // CONTINUUM-1953 olamy : attached buildDefs from template here
1849         // if no group creation
1850         if ( !projectGroupCreation && buildDefinitionTemplateId > 0 && !defaultProjectBuildDefSet )
1851         {
1852             buildDefinitionService.addTemplateInProject( buildDefinitionTemplateId, projectDao.getProject(
1853                 project.getId() ) );
1854         }
1855 
1856         AbstractContinuumAction.setUnvalidatedProject( context, project );
1857         //
1858         //            executeAction( "validate-project", context );
1859         //
1860         //            executeAction( "store-project", context );
1861         //
1862 
1863         AbstractContinuumAction.setProjectId( context, project.getId() );
1864 
1865         // does the scm username & password really have to be set in the project?
1866         if ( !StringUtils.isEmpty( scmUserName ) )
1867         {
1868             project.setScmUsername( scmUserName );
1869             CheckoutProjectContinuumAction.setScmUsername( context, scmUserName );
1870         }
1871         if ( !StringUtils.isEmpty( scmPassword ) )
1872         {
1873             project.setScmPassword( scmPassword );
1874             CheckoutProjectContinuumAction.setScmPassword( context, scmPassword );
1875         }
1876         //FIXME
1877         // olamy  : read again the project to have values because store.updateProjectGroup( projectGroup );
1878         // remove object data -> we don't display the project name in the build queue
1879         AbstractContinuumAction.setProject( context, projectDao.getProject( project.getId() ) );
1880 
1881         BuildDefinition defaultBuildDefinition = null;
1882         BuildDefinitionTemplate template = null;
1883         if ( projectBuilderId.equals( MavenTwoContinuumProjectBuilder.ID ) )
1884         {
1885             template = buildDefinitionService.getDefaultMavenTwoBuildDefinitionTemplate();
1886 
1887             if ( template != null && template.getBuildDefinitions().size() > 0 )
1888             {
1889                 defaultBuildDefinition = template.getBuildDefinitions().get( 0 );
1890             }
1891         }
1892         else if ( projectBuilderId.equals( MavenOneContinuumProjectBuilder.ID ) )
1893         {
1894             template = buildDefinitionService.getDefaultMavenOneBuildDefinitionTemplate();
1895 
1896             if ( template != null && template.getBuildDefinitions().size() > 0 )
1897             {
1898                 defaultBuildDefinition = template.getBuildDefinitions().get( 0 );
1899             }
1900         }
1901 
1902         if ( defaultBuildDefinition == null )
1903         {
1904             // do not throw exception
1905             // project already added so might as well continue with the rest
1906             log.warn( "No default build definition found in the template. Project cannot be checked out." );
1907         }
1908         else
1909         {
1910             // used by BuildManager to determine on which build queue will the project be put
1911             AbstractContinuumAction.setBuildDefinition( context, defaultBuildDefinition );
1912 
1913             if ( !configurationService.isDistributedBuildEnabled() )
1914             {
1915                 executeAction( "add-project-to-checkout-queue", context );
1916             }
1917         }
1918     }
1919 
1920     private ContinuumProjectBuildingResult executeAddProjectsFromMetadataActivity( String metadataUrl,
1921                                                                                    String projectBuilderId,
1922                                                                                    int projectGroupId,
1923                                                                                    boolean checkProtocol,
1924                                                                                    boolean useCredentialsCache,
1925                                                                                    boolean loadRecursiveProjects,
1926                                                                                    int buildDefinitionTemplateId,
1927                                                                                    boolean checkoutInSingleDirectory )
1928         throws ContinuumException
1929     {
1930         return executeAddProjectsFromMetadataActivity( metadataUrl, projectBuilderId, projectGroupId, checkProtocol,
1931                                                        useCredentialsCache, loadRecursiveProjects,
1932                                                        buildDefinitionTemplateId, true, checkoutInSingleDirectory );
1933     }
1934 
1935     // ----------------------------------------------------------------------
1936     // Notification
1937     // ----------------------------------------------------------------------
1938 
1939     // This whole section needs a scrub but will need to be dealt with generally
1940     // when we add schedules and profiles to the mix.
1941 
1942     public ProjectNotifier getNotifier( int projectId, int notifierId )
1943         throws ContinuumException
1944     {
1945         Project project = getProjectWithAllDetails( projectId );
1946 
1947         List<ProjectNotifier> notifiers = project.getNotifiers();
1948 
1949         ProjectNotifier notifier = null;
1950 
1951         for ( ProjectNotifier notif : notifiers )
1952         {
1953             notifier = notif;
1954 
1955             if ( notifier.getId() == notifierId )
1956             {
1957                 break;
1958             }
1959         }
1960 
1961         return notifier;
1962     }
1963 
1964     public ProjectNotifier getGroupNotifier( int projectGroupId, int notifierId )
1965         throws ContinuumException
1966     {
1967         ProjectGroup projectGroup = getProjectGroupWithBuildDetails( projectGroupId );
1968 
1969         List<ProjectNotifier> notifiers = projectGroup.getNotifiers();
1970 
1971         ProjectNotifier notifier = null;
1972 
1973         for ( ProjectNotifier notif : notifiers )
1974         {
1975             notifier = notif;
1976 
1977             if ( notifier.getId() == notifierId )
1978             {
1979                 break;
1980             }
1981         }
1982 
1983         return notifier;
1984     }
1985 
1986     public ProjectNotifier updateNotifier( int projectId, ProjectNotifier notifier )
1987         throws ContinuumException
1988     {
1989         Project project = getProjectWithAllDetails( projectId );
1990 
1991         ProjectNotifier notif = getNotifier( projectId, notifier.getId() );
1992 
1993         // I remove notifier then add it instead of update it due to a ClassCastException in jpox
1994         project.removeNotifier( notif );
1995 
1996         updateProject( project );
1997 
1998         return addNotifier( projectId, notifier );
1999     }
2000 
2001     public ProjectNotifier updateGroupNotifier( int projectGroupId, ProjectNotifier notifier )
2002         throws ContinuumException
2003     {
2004         ProjectGroup projectGroup = getProjectGroupWithBuildDetails( projectGroupId );
2005 
2006         ProjectNotifier notif = getGroupNotifier( projectGroupId, notifier.getId() );
2007 
2008         // I remove notifier then add it instead of update it due to a ClassCastException in jpox
2009         projectGroup.removeNotifier( notif );
2010 
2011         try
2012         {
2013             projectGroupDao.updateProjectGroup( projectGroup );
2014         }
2015         catch ( ContinuumStoreException cse )
2016         {
2017             throw new ContinuumException( "Unable to update project group.", cse );
2018         }
2019 
2020         return addGroupNotifier( projectGroupId, notifier );
2021     }
2022 
2023     public ProjectNotifier addNotifier( int projectId, ProjectNotifier notifier )
2024         throws ContinuumException
2025     {
2026         ProjectNotifier notif = new ProjectNotifier();
2027 
2028         notif.setSendOnSuccess( notifier.isSendOnSuccess() );
2029 
2030         notif.setSendOnFailure( notifier.isSendOnFailure() );
2031 
2032         notif.setSendOnError( notifier.isSendOnError() );
2033 
2034         notif.setSendOnWarning( notifier.isSendOnWarning() );
2035 
2036         notif.setSendOnScmFailure( notifier.isSendOnScmFailure() );
2037 
2038         notif.setConfiguration( notifier.getConfiguration() );
2039 
2040         notif.setType( notifier.getType() );
2041 
2042         notif.setFrom( ProjectNotifier.FROM_USER );
2043 
2044         Project project = getProjectWithAllDetails( projectId );
2045 
2046         project.addNotifier( notif );
2047 
2048         updateProject( project );
2049 
2050         return notif;
2051     }
2052 
2053     public ProjectNotifier addGroupNotifier( int projectGroupId, ProjectNotifier notifier )
2054         throws ContinuumException
2055     {
2056         ProjectNotifier notif = new ProjectNotifier();
2057 
2058         notif.setSendOnSuccess( notifier.isSendOnSuccess() );
2059 
2060         notif.setSendOnFailure( notifier.isSendOnFailure() );
2061 
2062         notif.setSendOnError( notifier.isSendOnError() );
2063 
2064         notif.setSendOnWarning( notifier.isSendOnWarning() );
2065 
2066         notif.setSendOnScmFailure( notifier.isSendOnScmFailure() );
2067 
2068         notif.setConfiguration( notifier.getConfiguration() );
2069 
2070         notif.setType( notifier.getType() );
2071 
2072         notif.setFrom( ProjectNotifier.FROM_USER );
2073 
2074         ProjectGroup projectGroup = getProjectGroupWithBuildDetails( projectGroupId );
2075 
2076         projectGroup.addNotifier( notif );
2077         try
2078         {
2079             projectGroupDao.updateProjectGroup( projectGroup );
2080         }
2081         catch ( ContinuumStoreException cse )
2082         {
2083             throw new ContinuumException( "unable to add notifier to project group", cse );
2084         }
2085 
2086         return notif;
2087     }
2088 
2089     public void removeNotifier( int projectId, int notifierId )
2090         throws ContinuumException
2091     {
2092         Project project = getProjectWithAllDetails( projectId );
2093 
2094         ProjectNotifier n = getNotifier( projectId, notifierId );
2095 
2096         if ( n != null )
2097         {
2098             if ( n.isFromProject() )
2099             {
2100                 n.setEnabled( false );
2101 
2102                 storeNotifier( n );
2103             }
2104             else
2105             {
2106                 project.removeNotifier( n );
2107 
2108                 updateProject( project );
2109             }
2110         }
2111     }
2112 
2113     public void removeGroupNotifier( int projectGroupId, int notifierId )
2114         throws ContinuumException
2115     {
2116         ProjectGroup projectGroup = getProjectGroupWithBuildDetails( projectGroupId );
2117 
2118         ProjectNotifier n = getGroupNotifier( projectGroupId, notifierId );
2119 
2120         if ( n != null )
2121         {
2122             if ( n.isFromProject() )
2123             {
2124                 n.setEnabled( false );
2125 
2126                 storeNotifier( n );
2127             }
2128             else
2129             {
2130                 projectGroup.removeNotifier( n );
2131 
2132                 try
2133                 {
2134                     projectGroupDao.updateProjectGroup( projectGroup );
2135                 }
2136                 catch ( ContinuumStoreException cse )
2137                 {
2138                     throw new ContinuumException( "Unable to remove notifer from project group.", cse );
2139                 }
2140             }
2141         }
2142     }
2143 
2144     // ----------------------------------------------------------------------
2145     // Build Definition
2146     // ----------------------------------------------------------------------
2147 
2148     public List<BuildDefinition> getBuildDefinitions( int projectId )
2149         throws ContinuumException
2150     {
2151         Project project = getProjectWithAllDetails( projectId );
2152 
2153         return project.getBuildDefinitions();
2154     }
2155 
2156     public BuildDefinition getBuildDefinition( int projectId, int buildDefinitionId )
2157         throws ContinuumException
2158     {
2159         List<BuildDefinition> buildDefinitions = getBuildDefinitions( projectId );
2160 
2161         BuildDefinition buildDefinition = null;
2162 
2163         for ( BuildDefinition bd : buildDefinitions )
2164         {
2165             if ( bd.getId() == buildDefinitionId )
2166             {
2167                 buildDefinition = bd;
2168                 break;
2169             }
2170         }
2171 
2172         return buildDefinition;
2173     }
2174 
2175     public BuildDefinition getDefaultBuildDefinition( int projectId )
2176         throws ContinuumException
2177     {
2178         try
2179         {
2180             return buildDefinitionDao.getDefaultBuildDefinition( projectId );
2181         }
2182         catch ( ContinuumObjectNotFoundException cne )
2183         {
2184             throw new ContinuumException( "no default build definition for project", cne );
2185         }
2186         catch ( ContinuumStoreException cse )
2187         {
2188             throw new ContinuumException(
2189                 "error attempting to access default build definition for project " + projectId, cse );
2190         }
2191     }
2192 
2193     public List<BuildDefinition> getDefaultBuildDefinitionsForProjectGroup( int projectGroupId )
2194         throws ContinuumException
2195     {
2196         try
2197         {
2198             return buildDefinitionDao.getDefaultBuildDefinitionsForProjectGroup( projectGroupId );
2199         }
2200         catch ( ContinuumObjectNotFoundException cne )
2201         {
2202             throw new ContinuumException( "Project Group (id=" + projectGroupId +
2203                                               ") doesn't have a default build definition, this should be impossible, it should always have a default definition set." );
2204         }
2205         catch ( ContinuumStoreException cse )
2206         {
2207             throw new ContinuumException( "Project Group (id=" + projectGroupId +
2208                                               ") doesn't have a default build definition, this should be impossible, it should always have a default definition set." );
2209         }
2210     }
2211 
2212     public BuildDefinition getBuildDefinition( int buildDefinitionId )
2213         throws ContinuumException
2214     {
2215         try
2216         {
2217             return buildDefinitionDao.getBuildDefinition( buildDefinitionId );
2218         }
2219         catch ( ContinuumObjectNotFoundException cne )
2220         {
2221             throw new ContinuumException( "no build definition found", cne );
2222         }
2223         catch ( ContinuumStoreException cse )
2224         {
2225             throw new ContinuumException( "error attempting to access build definition", cse );
2226         }
2227     }
2228 
2229     public List<BuildDefinition> getBuildDefinitionsForProject( int projectId )
2230         throws ContinuumException
2231     {
2232         Project project = getProjectWithAllDetails( projectId );
2233 
2234         return project.getBuildDefinitions();
2235     }
2236 
2237     public List<BuildDefinition> getBuildDefinitionsForProjectGroup( int projectGroupId )
2238         throws ContinuumException
2239     {
2240 
2241         ProjectGroup projectGroup = getProjectGroupWithBuildDetails( projectGroupId );
2242 
2243         return projectGroup.getBuildDefinitions();
2244     }
2245 
2246     public BuildDefinition addBuildDefinitionToProject( int projectId, BuildDefinition buildDefinition )
2247         throws ContinuumException
2248     {
2249         HashMap<String, Object> context = new HashMap<String, Object>();
2250         Schedule schedule = buildDefinition.getSchedule();
2251 
2252         AbstractContinuumAction.setBuildDefinition( context, buildDefinition );
2253         AbstractContinuumAction.setProjectId( context, projectId );
2254 
2255         executeAction( "add-build-definition-to-project", context );
2256 
2257         activeBuildDefinitionSchedule( schedule );
2258 
2259         return AbstractContinuumAction.getBuildDefinition( context );
2260     }
2261 
2262     public void removeBuildDefinitionFromProject( int projectId, int buildDefinitionId )
2263         throws ContinuumException
2264     {
2265         HashMap<String, Object> context = new HashMap<String, Object>();
2266         BuildDefinition buildDefinition = getBuildDefinition( buildDefinitionId );
2267 
2268         AbstractContinuumAction.setBuildDefinition( context, buildDefinition );
2269         AbstractContinuumAction.setProjectId( context, projectId );
2270 
2271         executeAction( "remove-build-definition-from-project", context );
2272     }
2273 
2274     public BuildDefinition updateBuildDefinitionForProject( int projectId, BuildDefinition buildDefinition )
2275         throws ContinuumException
2276     {
2277         HashMap<String, Object> context = new HashMap<String, Object>();
2278         Schedule schedule = buildDefinition.getSchedule();
2279 
2280         AbstractContinuumAction.setBuildDefinition( context, buildDefinition );
2281         AbstractContinuumAction.setProjectId( context, projectId );
2282 
2283         executeAction( "update-build-definition-from-project", context );
2284 
2285         activeBuildDefinitionSchedule( schedule );
2286 
2287         return AbstractContinuumAction.getBuildDefinition( context );
2288     }
2289 
2290     public BuildDefinition addBuildDefinitionToProjectGroup( int projectGroupId, BuildDefinition buildDefinition )
2291         throws ContinuumException
2292     {
2293         HashMap<String, Object> context = new HashMap<String, Object>();
2294         Schedule schedule = buildDefinition.getSchedule();
2295 
2296         AbstractContinuumAction.setBuildDefinition( context, buildDefinition );
2297         AbstractContinuumAction.setProjectGroupId( context, projectGroupId );
2298 
2299         executeAction( "add-build-definition-to-project-group", context );
2300 
2301         activeBuildDefinitionSchedule( schedule );
2302 
2303         return AbstractContinuumAction.getBuildDefinition( context );
2304     }
2305 
2306     public void removeBuildDefinitionFromProjectGroup( int projectGroupId, int buildDefinitionId )
2307         throws ContinuumException
2308     {
2309         HashMap<String, Object> context = new HashMap<String, Object>();
2310 
2311         AbstractContinuumAction.setBuildDefinition( context, getBuildDefinition( buildDefinitionId ) );
2312         AbstractContinuumAction.setProjectGroupId( context, projectGroupId );
2313 
2314         executeAction( "remove-build-definition-from-project-group", context );
2315     }
2316 
2317     public BuildDefinition updateBuildDefinitionForProjectGroup( int projectGroupId, BuildDefinition buildDefinition )
2318         throws ContinuumException
2319     {
2320         HashMap<String, Object> context = new HashMap<String, Object>();
2321         Schedule schedule = buildDefinition.getSchedule();
2322 
2323         AbstractContinuumAction.setBuildDefinition( context, buildDefinition );
2324         AbstractContinuumAction.setProjectGroupId( context, projectGroupId );
2325 
2326         executeAction( "update-build-definition-from-project-group", context );
2327 
2328         activeBuildDefinitionSchedule( schedule );
2329 
2330         return AbstractContinuumAction.getBuildDefinition( context );
2331     }
2332 
2333     public void removeBuildDefinition( int projectId, int buildDefinitionId )
2334         throws ContinuumException
2335     {
2336         Project project = getProjectWithAllDetails( projectId );
2337 
2338         BuildDefinition buildDefinition = getBuildDefinition( projectId, buildDefinitionId );
2339 
2340         if ( buildDefinition != null )
2341         {
2342             project.removeBuildDefinition( buildDefinition );
2343 
2344             updateProject( project );
2345         }
2346     }
2347 
2348     // ----------------------------------------------------------------------
2349     // Schedule
2350     // ----------------------------------------------------------------------
2351 
2352     public Schedule getSchedule( int scheduleId )
2353         throws ContinuumException
2354     {
2355         try
2356         {
2357             return scheduleDao.getSchedule( scheduleId );
2358         }
2359         catch ( Exception ex )
2360         {
2361             throw logAndCreateException( "Error while getting schedule.", ex );
2362         }
2363     }
2364 
2365     public Schedule getScheduleByName( String scheduleName )
2366         throws ContinuumException
2367     {
2368         try
2369         {
2370             return scheduleDao.getScheduleByName( scheduleName );
2371         }
2372         catch ( ContinuumStoreException e )
2373         {
2374             throw logAndCreateException( "Error while accessing the store.", e );
2375         }
2376     }
2377 
2378     public Collection<Schedule> getSchedules()
2379         throws ContinuumException
2380     {
2381         return scheduleDao.getAllSchedulesByName();
2382     }
2383 
2384     public void addSchedule( Schedule schedule )
2385         throws ContinuumException
2386     {
2387         Schedule s;
2388 
2389         s = getScheduleByName( schedule.getName() );
2390 
2391         if ( s != null )
2392         {
2393             throw logAndCreateException( "Can't create schedule. A schedule with the same name already exists.", null );
2394         }
2395 
2396         s = scheduleDao.addSchedule( schedule );
2397 
2398         try
2399         {
2400             schedulesActivator.activateSchedule( s, this );
2401         }
2402         catch ( SchedulesActivationException e )
2403         {
2404             throw new ContinuumException( "Error activating schedule " + s.getName() + ".", e );
2405         }
2406     }
2407 
2408     public void updateSchedule( Schedule schedule )
2409         throws ContinuumException
2410     {
2411         updateSchedule( schedule, true );
2412     }
2413 
2414     private void updateSchedule( Schedule schedule, boolean updateScheduler )
2415         throws ContinuumException
2416     {
2417 
2418         Schedule old = getSchedule( schedule.getId() );
2419 
2420         storeSchedule( schedule );
2421 
2422         if ( updateScheduler )
2423         {
2424             try
2425             {
2426                 if ( schedule.isActive() )
2427                 {
2428                     // I unactivate old shcedule (could change name) before if it's already active
2429                     schedulesActivator.unactivateSchedule( old, this );
2430 
2431                     schedulesActivator.activateSchedule( schedule, this );
2432                 }
2433                 else
2434                 {
2435                     // Unactivate old because could change name in new schedule
2436                     schedulesActivator.unactivateSchedule( old, this );
2437                 }
2438             }
2439             catch ( SchedulesActivationException e )
2440             {
2441                 log.error( "Can't unactivate schedule. You need to restart Continuum.", e );
2442             }
2443         }
2444     }
2445 
2446     public void updateSchedule( int scheduleId, Map<String, String> configuration )
2447         throws ContinuumException
2448     {
2449         Schedule schedule = getSchedule( scheduleId );
2450 
2451         schedule.setName( configuration.get( "schedule.name" ) );
2452 
2453         schedule.setDescription( configuration.get( "schedule.description" ) );
2454 
2455         schedule.setCronExpression( configuration.get( "schedule.cronExpression" ) );
2456 
2457         schedule.setDelay( Integer.parseInt( configuration.get( "schedule.delay" ) ) );
2458 
2459         schedule.setActive( Boolean.valueOf( configuration.get( "schedule.active" ) ) );
2460 
2461         updateSchedule( schedule, true );
2462     }
2463 
2464     public void removeSchedule( int scheduleId )
2465         throws ContinuumException
2466     {
2467         Schedule schedule = getSchedule( scheduleId );
2468 
2469         try
2470         {
2471             schedulesActivator.unactivateSchedule( schedule, this );
2472         }
2473         catch ( SchedulesActivationException e )
2474         {
2475             log.error( "Can't unactivate the schedule. You need to restart Continuum.", e );
2476         }
2477 
2478         try
2479         {
2480             scheduleDao.removeSchedule( schedule );
2481         }
2482         catch ( Exception e )
2483         {
2484             log.error( "Can't remove the schedule.", e );
2485 
2486             try
2487             {
2488                 schedulesActivator.activateSchedule( schedule, this );
2489             }
2490             catch ( SchedulesActivationException sae )
2491             {
2492                 log.error( "Can't reactivate the schedule. You need to restart Continuum.", e );
2493             }
2494             throw new ContinuumException( "Can't remove the schedule", e );
2495         }
2496     }
2497 
2498     private Schedule storeSchedule( Schedule schedule )
2499         throws ContinuumException
2500     {
2501         try
2502         {
2503             return scheduleDao.storeSchedule( schedule );
2504         }
2505         catch ( ContinuumStoreException ex )
2506         {
2507             throw logAndCreateException( "Error while storing schedule.", ex );
2508         }
2509     }
2510 
2511     public void activePurgeSchedule( Schedule schedule )
2512     {
2513         try
2514         {
2515             schedulesActivator.activatePurgeSchedule( schedule, this );
2516         }
2517         catch ( SchedulesActivationException e )
2518         {
2519             log.error( "Can't activate schedule for purgeConfiguration" );
2520         }
2521     }
2522 
2523     public void activeBuildDefinitionSchedule( Schedule schedule )
2524     {
2525         try
2526         {
2527             schedulesActivator.activateBuildSchedule( schedule, this );
2528         }
2529         catch ( SchedulesActivationException e )
2530         {
2531             log.error( "Can't activate schedule for buildDefinition" );
2532         }
2533     }
2534     // ----------------------------------------------------------------------
2535     // Working copy
2536     // ----------------------------------------------------------------------
2537 
2538     public File getWorkingDirectory( int projectId )
2539         throws ContinuumException
2540     {
2541         try
2542         {
2543             return workingDirectoryService.getWorkingDirectory( projectDao.getProject( projectId ) );
2544         }
2545         catch ( ContinuumStoreException e )
2546         {
2547             throw new ContinuumException( "Can't get files list.", e );
2548         }
2549     }
2550 
2551     public String getFileContent( int projectId, String directory, String filename )
2552         throws ContinuumException
2553     {
2554         String relativePath = "\\.\\./"; // prevent users from using relative paths.
2555         Pattern pattern = Pattern.compile( relativePath );
2556         Matcher matcher = pattern.matcher( directory );
2557         String filteredDirectory = matcher.replaceAll( "" );
2558 
2559         matcher = pattern.matcher( filename );
2560         String filteredFilename = matcher.replaceAll( "" );
2561 
2562         File workingDirectory = getWorkingDirectory( projectId );
2563 
2564         File fileDirectory = new File( workingDirectory, filteredDirectory );
2565 
2566         File userFile = new File( fileDirectory, filteredFilename );
2567 
2568         try
2569         {
2570             return FileUtils.fileRead( userFile );
2571         }
2572         catch ( IOException e )
2573         {
2574             throw new ContinuumException( "Can't read file " + filename, e );
2575         }
2576     }
2577 
2578     public List<File> getFiles( int projectId, String userDirectory )
2579         throws ContinuumException
2580     {
2581         File workingDirectory = getWorkingDirectory( projectId );
2582 
2583         return getFiles( workingDirectory, null, userDirectory );
2584     }
2585 
2586     private List<File> getFiles( File baseDirectory, String currentSubDirectory, String userDirectory )
2587     {
2588         List<File> dirs = new ArrayList<File>();
2589 
2590         File workingDirectory;
2591 
2592         if ( currentSubDirectory != null )
2593         {
2594             workingDirectory = new File( baseDirectory, currentSubDirectory );
2595         }
2596         else
2597         {
2598             workingDirectory = baseDirectory;
2599         }
2600 
2601         String[] files = workingDirectory.list();
2602         Arrays.sort( files, String.CASE_INSENSITIVE_ORDER );
2603 
2604         if ( files != null )
2605         {
2606             for ( String file : files )
2607             {
2608                 File current = new File( workingDirectory, file );
2609 
2610                 String currentFile;
2611 
2612                 if ( currentSubDirectory == null )
2613                 {
2614                     currentFile = file;
2615                 }
2616                 else
2617                 {
2618                     currentFile = currentSubDirectory + "/" + file;
2619                 }
2620 
2621                 if ( userDirectory != null && current.isDirectory() && userDirectory.startsWith( currentFile ) )
2622                 {
2623                     dirs.add( current );
2624 
2625                     dirs.addAll( getFiles( baseDirectory, currentFile, userDirectory ) );
2626                 }
2627                 else
2628                 {
2629                     dirs.add( current );
2630                 }
2631             }
2632         }
2633 
2634         return dirs;
2635     }
2636 
2637     // ----------------------------------------------------------------------
2638     // Configuration
2639     // ----------------------------------------------------------------------
2640 
2641     public ConfigurationService getConfiguration()
2642     {
2643         return configurationService;
2644     }
2645 
2646     public void reloadConfiguration()
2647         throws ContinuumException
2648     {
2649         try
2650         {
2651             configurationService.reload();
2652         }
2653         catch ( Exception e )
2654         {
2655             throw new ContinuumException( "Can't reload configuration.", e );
2656         }
2657     }
2658 
2659     // ----------------------------------------------------------------------
2660     // Lifecycle Management
2661     // ----------------------------------------------------------------------
2662 
2663     public void initialize()
2664         throws InitializationException
2665     {
2666         log.info( "Initializing Continuum." );
2667 
2668         log.info( "Showing all groups:" );
2669         try
2670         {
2671             for ( ProjectGroup group : projectGroupDao.getAllProjectGroups() )
2672             {
2673                 createProjectScmRootForProjectGroup( group );
2674             }
2675         }
2676         catch ( ContinuumException e )
2677         {
2678             throw new InitializationException( "Error while creating project scm root for the project group", e );
2679         }
2680 
2681         log.info( "Showing all projects: " );
2682 
2683         for ( Project project : projectDao.getAllProjectsByNameWithBuildDetails() )
2684         {
2685             for ( ProjectNotifier notifier : (List<ProjectNotifier>) project.getNotifiers() )
2686             {
2687                 if ( StringUtils.isEmpty( notifier.getType() ) )
2688                 {
2689                     try
2690                     {
2691                         removeNotifier( project.getId(), notifier.getId() );
2692                     }
2693                     catch ( ContinuumException e )
2694                     {
2695                         throw new InitializationException( "Database is corrupted.", e );
2696                     }
2697                 }
2698             }
2699 
2700             if ( project.getState() != ContinuumProjectState.NEW &&
2701                 project.getState() != ContinuumProjectState.CHECKEDOUT &&
2702                 project.getState() != ContinuumProjectState.OK && project.getState() != ContinuumProjectState.FAILED &&
2703                 project.getState() != ContinuumProjectState.ERROR )
2704             {
2705                 int state = project.getState();
2706 
2707                 project.setState( project.getOldState() );
2708 
2709                 project.setOldState( 0 );
2710 
2711                 try
2712                 {
2713                     log.info( "Fix project state for project " + project.getId() + ":" + project.getName() + ":" +
2714                                   project.getVersion() );
2715 
2716                     projectDao.updateProject( project );
2717 
2718                     Project p = projectDao.getProject( project.getId() );
2719 
2720                     if ( state == p.getState() )
2721                     {
2722                         log.info( "Can't fix the project state." );
2723                     }
2724                 }
2725                 catch ( ContinuumStoreException e )
2726                 {
2727                     throw new InitializationException( "Database is corrupted.", e );
2728                 }
2729             }
2730 
2731             log.info( " " + project.getId() + ":" + project.getName() + ":" + project.getVersion() + ":" +
2732                           project.getExecutorId() );
2733         }
2734 
2735         for ( ProjectScmRoot projectScmRoot : projectScmRootDao.getAllProjectScmRoots() )
2736         {
2737             if ( projectScmRoot.getState() == ContinuumProjectState.UPDATING )
2738             {
2739                 projectScmRoot.setState( projectScmRoot.getOldState() );
2740 
2741                 projectScmRoot.setOldState( 0 );
2742 
2743                 try
2744                 {
2745                     log.info( "Fix state for projectScmRoot " + projectScmRoot.getScmRootAddress() );
2746 
2747                     projectScmRootDao.updateProjectScmRoot( projectScmRoot );
2748                 }
2749                 catch ( ContinuumStoreException e )
2750                 {
2751                     throw new InitializationException( "Database is corrupted.", e );
2752                 }
2753             }
2754         }
2755     }
2756 
2757     // --------------------------------
2758     //  Plexus Lifecycle
2759     // --------------------------------
2760     public void start()
2761         throws StartingException
2762     {
2763         startMessage();
2764 
2765         try
2766         {
2767             initializer.initialize();
2768 
2769             configurationService.reload();
2770         }
2771         catch ( ConfigurationLoadingException e )
2772         {
2773             throw new StartingException( "Error loading the Continuum configuration.", e );
2774         }
2775         catch ( ContinuumConfigurationException e )
2776         {
2777             throw new StartingException( "Error loading the Continuum configuration.", e );
2778         }
2779         catch ( ContinuumInitializationException e )
2780         {
2781             throw new StartingException( "Cannot initializing Continuum for the first time.", e );
2782         }
2783 
2784         try
2785         {
2786             // ----------------------------------------------------------------------
2787             // Activate all the schedules in the system
2788             // ----------------------------------------------------------------------
2789             schedulesActivator.activateSchedules( this );
2790         }
2791         catch ( SchedulesActivationException e )
2792         {
2793             // We don't throw an exception here, so users will can modify schedules in interface instead of database
2794             log.error( "Error activating schedules.", e );
2795         }
2796     }
2797 
2798     public void stop()
2799         throws StoppingException
2800     {
2801         stopContinuum();
2802     }
2803 
2804     private void closeStore()
2805     {
2806         if ( daoUtils != null )
2807         {
2808             daoUtils.closeStore();
2809         }
2810     }
2811 
2812 
2813     public void startup()
2814         throws ContinuumException
2815     {
2816         try
2817         {
2818             this.start();
2819         }
2820         catch ( StartingException e )
2821         {
2822             throw new ContinuumException( e.getMessage(), e );
2823         }
2824     }
2825 
2826     private void stopContinuum()
2827     {
2828         //TODO: Remove all projects from queues, stop scheduler and wait the end of current builds so build results will be ok
2829         if ( stopped )
2830         {
2831             return;
2832         }
2833 
2834         try
2835         {
2836             if ( configurationService != null )
2837             {
2838                 configurationService.store();
2839             }
2840         }
2841         catch ( Exception e )
2842         {
2843             log.info( "Error storing the Continuum configuration.", e );
2844         }
2845 
2846         closeStore();
2847 
2848         stopMessage();
2849 
2850         stopped = true;
2851     }
2852 
2853     public long getNbBuildResultsForProject( int projectId )
2854     {
2855         return buildResultDao.getNbBuildResultsForProject( projectId );
2856     }
2857 
2858     public Collection<BuildResult> getBuildResultsForProject( int projectId )
2859         throws ContinuumException
2860     {
2861         return buildResultDao.getBuildResultsForProject( projectId );
2862     }
2863 
2864     // ----------------------------------------------------------------------
2865     // Workflow
2866     // ----------------------------------------------------------------------
2867 
2868     protected void executeAction( String actionName, Map<String, Object> context )
2869         throws ContinuumException
2870     {
2871         try
2872         {
2873             Action action = actionManager.lookup( actionName );
2874 
2875             action.execute( context );
2876         }
2877         catch ( ActionNotFoundException e )
2878         {
2879             e.printStackTrace();
2880             throw new ContinuumException( "Error while executing the action '" + actionName + "'.", e );
2881         }
2882         catch ( ContinuumException e )
2883         {
2884             throw e;
2885         }
2886         catch ( Exception e )
2887         {
2888             log.info( "exception", e );
2889             throw new ContinuumException( "Error while executing the action '" + actionName + "'.", e );
2890         }
2891     }
2892 
2893     // ----------------------------------------------------------------------
2894     // Logging
2895     // ----------------------------------------------------------------------
2896 
2897     private ContinuumException logAndCreateException( String message, Throwable cause )
2898     {
2899         if ( cause instanceof ContinuumObjectNotFoundException )
2900         {
2901             return new ContinuumException( "No such object.", cause );
2902         }
2903 
2904         log.error( message, cause );
2905 
2906         return new ContinuumException( message, cause );
2907     }
2908 
2909     // ----------------------------------------------------------------------
2910     // Build settings
2911     // ----------------------------------------------------------------------
2912 
2913     // core
2914 
2915     public void updateProject( Project project )
2916         throws ContinuumException
2917     {
2918         try
2919         {
2920             boolean removeWorkingDirectory = false;
2921 
2922             Project p = projectDao.getProject( project.getId() );
2923             ProjectScmRoot projectScmRoot = null;
2924 
2925             if ( !p.getScmUrl().equals( project.getScmUrl() ) )
2926             {
2927                 removeWorkingDirectory = true;
2928                 projectScmRoot = getProjectScmRootByProject( project.getId() );
2929             }
2930 
2931             if ( !p.getProjectGroup().equals( project.getProjectGroup() ) )
2932             {
2933                 projectScmRoot = getProjectScmRootByProject( project.getId() );
2934             }
2935 
2936             if ( StringUtils.isEmpty( p.getScmTag() ) && !StringUtils.isEmpty( project.getScmTag() ) )
2937             {
2938                 removeWorkingDirectory = true;
2939             }
2940             else if ( !StringUtils.isEmpty( p.getScmTag() ) && StringUtils.isEmpty( project.getScmTag() ) )
2941             {
2942                 removeWorkingDirectory = true;
2943             }
2944             else if ( !StringUtils.isEmpty( p.getScmTag() ) && !p.getScmTag().equals( project.getScmTag() ) )
2945             {
2946                 removeWorkingDirectory = true;
2947             }
2948 
2949             if ( removeWorkingDirectory )
2950             {
2951                 File workingDirectory = getWorkingDirectory( project.getId() );
2952 
2953                 FileUtils.deleteDirectory( workingDirectory );
2954             }
2955 
2956             if ( StringUtils.isEmpty( project.getScmTag() ) )
2957             {
2958                 project.setScmTag( null );
2959             }
2960 
2961             projectDao.updateProject( project );
2962 
2963             if ( projectScmRoot != null )
2964             {
2965                 updateProjectScmRoot( projectScmRoot, project );
2966             }
2967         }
2968         catch ( ContinuumStoreException ex )
2969         {
2970             throw logAndCreateException( "Error while updating project.", ex );
2971         }
2972         catch ( IOException ex )
2973         {
2974             throw logAndCreateException( "Error while updating project.", ex );
2975         }
2976     }
2977 
2978     public void updateProjectGroup( ProjectGroup projectGroup )
2979         throws ContinuumException
2980     {
2981         //CONTINUUM-1502
2982         projectGroup.setName( projectGroup.getName().trim() );
2983         try
2984         {
2985             projectGroupDao.updateProjectGroup( projectGroup );
2986         }
2987         catch ( ContinuumStoreException cse )
2988         {
2989             throw logAndCreateException( "Error while updating project group.", cse );
2990         }
2991     }
2992 
2993     private ProjectNotifier storeNotifier( ProjectNotifier notifier )
2994         throws ContinuumException
2995     {
2996         try
2997         {
2998             return notifierDao.storeNotifier( notifier );
2999         }
3000         catch ( ContinuumStoreException ex )
3001         {
3002             throw logAndCreateException( "Error while storing notifier.", ex );
3003         }
3004     }
3005 
3006     private String getWorkingDirectory()
3007     {
3008         return configurationService.getWorkingDirectory().getAbsolutePath();
3009     }
3010 
3011     public Project getProjectWithCheckoutResult( int projectId )
3012         throws ContinuumException
3013     {
3014         try
3015         {
3016             return projectDao.getProjectWithCheckoutResult( projectId );
3017         }
3018         catch ( ContinuumObjectNotFoundException e )
3019         {
3020             throw new ContinuumException( "Unable to find the requested project", e );
3021         }
3022         catch ( ContinuumStoreException e )
3023         {
3024             throw new ContinuumException( "Error retrieving the requested project", e );
3025         }
3026     }
3027 
3028     public Project getProjectWithAllDetails( int projectId )
3029         throws ContinuumException
3030     {
3031         try
3032         {
3033             return projectDao.getProjectWithAllDetails( projectId );
3034         }
3035         catch ( ContinuumObjectNotFoundException e )
3036         {
3037             throw new ContinuumException( "Unable to find the requested project", e );
3038         }
3039         catch ( ContinuumStoreException e )
3040         {
3041             throw new ContinuumException( "Error retrieving the requested project", e );
3042         }
3043     }
3044 
3045     public ProjectGroup getProjectGroupWithBuildDetails( int projectGroupId )
3046         throws ContinuumException
3047     {
3048         try
3049         {
3050             return projectGroupDao.getProjectGroupWithBuildDetailsByProjectGroupId( projectGroupId );
3051         }
3052         catch ( ContinuumObjectNotFoundException e )
3053         {
3054             throw new ContinuumException( "Unable to find the requested project", e );
3055         }
3056         catch ( ContinuumStoreException e )
3057         {
3058             throw new ContinuumException( "Error retrieving the requested project", e );
3059         }
3060     }
3061 
3062     public Project getProjectWithBuilds( int projectId )
3063         throws ContinuumException
3064     {
3065         try
3066         {
3067             return projectDao.getProjectWithBuilds( projectId );
3068         }
3069         catch ( ContinuumObjectNotFoundException e )
3070         {
3071             throw new ContinuumException( "Unable to find the requested project", e );
3072         }
3073         catch ( ContinuumStoreException e )
3074         {
3075             throw new ContinuumException( "Error retrieving the requested project", e );
3076         }
3077     }
3078 
3079     public List<ProjectGroup> getAllProjectGroupsWithBuildDetails()
3080     {
3081         return projectGroupDao.getAllProjectGroupsWithBuildDetails();
3082     }
3083 
3084     public Collection<Project> getProjectsInGroup( int projectGroupId )
3085         throws ContinuumException
3086     {
3087         try
3088         {
3089             return projectDao.getProjectsInGroup( projectGroupId );
3090         }
3091         catch ( ContinuumObjectNotFoundException e )
3092         {
3093             throw new ContinuumException( "Unable to find the requested project", e );
3094         }
3095         catch ( ContinuumStoreException e )
3096         {
3097             throw new ContinuumException( "Error retrieving the requested project", e );
3098         }
3099     }
3100 
3101     public Collection<Project> getProjectsInGroupWithDependencies( int projectGroupId )
3102         throws ContinuumException
3103     {
3104         try
3105         {
3106             return projectDao.getProjectsInGroupWithDependencies( projectGroupId );
3107         }
3108         catch ( ContinuumObjectNotFoundException e )
3109         {
3110             throw new ContinuumException( "Unable to find the requested project", e );
3111         }
3112         catch ( ContinuumStoreException e )
3113         {
3114             throw new ContinuumException( "Error retrieving the requested project", e );
3115         }
3116     }
3117 
3118     // ----------------------------------------------------------------------
3119     // Private Utilities
3120     // ----------------------------------------------------------------------
3121 
3122     private void startMessage()
3123     {
3124         log.info( "Starting Continuum." );
3125 
3126         // ----------------------------------------------------------------------
3127         //
3128         // ----------------------------------------------------------------------
3129 
3130         String banner = StringUtils.repeat( "-", getVersion().length() );
3131 
3132         log.info( "" );
3133         log.info( "" );
3134         log.info( "< Continuum " + getVersion() + " started! >" );
3135         log.info( "-----------------------" + banner );
3136         log.info( "       \\   ^__^" );
3137         log.info( "        \\  (oo)\\_______" );
3138         log.info( "           (__)\\       )\\/\\" );
3139         log.info( "               ||----w |" );
3140         log.info( "               ||     ||" );
3141         log.info( "" );
3142         log.info( "" );
3143     }
3144 
3145     private void stopMessage()
3146     {
3147         // Yes dorothy, this can happen!
3148         if ( log != null )
3149         {
3150             log.info( "Stopping Continuum." );
3151 
3152             log.info( "Continuum stopped." );
3153         }
3154     }
3155 
3156     private String getVersion()
3157     {
3158         InputStream resourceAsStream = null;
3159         try
3160         {
3161             Properties properties = new Properties();
3162 
3163             String name = "META-INF/maven/org.apache.continuum/continuum-core/pom.properties";
3164 
3165             resourceAsStream = getClass().getClassLoader().getResourceAsStream( name );
3166 
3167             if ( resourceAsStream == null )
3168             {
3169                 return "unknown";
3170             }
3171 
3172             properties.load( resourceAsStream );
3173 
3174             return properties.getProperty( "version", "unknown" );
3175         }
3176         catch ( IOException e )
3177         {
3178             return "unknown";
3179         }
3180         finally
3181         {
3182             if ( resourceAsStream != null )
3183             {
3184                 IOUtil.close( resourceAsStream );
3185             }
3186         }
3187     }
3188 
3189     public InstallationService getInstallationService()
3190     {
3191         return installationService;
3192     }
3193 
3194     public ProfileService getProfileService()
3195     {
3196         return profileService;
3197     }
3198 
3199     public BuildDefinitionService getBuildDefinitionService()
3200     {
3201         return buildDefinitionService;
3202     }
3203 
3204     public ContinuumReleaseResult addContinuumReleaseResult( int projectId, String releaseId, String releaseType )
3205         throws ContinuumException
3206     {
3207         ReleaseResult result;
3208         String releaseBy = "";
3209 
3210         if ( getConfiguration().isDistributedBuildEnabled() )
3211         {
3212             try
3213             {
3214                 result = (ReleaseResult) distributedReleaseManager.getReleaseResult( releaseId );
3215                 PreparedRelease preparedRelease = distributedReleaseManager.getPreparedRelease( releaseId,
3216                                                                                                 releaseType );
3217                 if ( preparedRelease != null )
3218                 {
3219                     releaseBy = preparedRelease.getReleaseBy();
3220                 }
3221             }
3222             catch ( ContinuumReleaseException e )
3223             {
3224                 throw new ContinuumException( "Failed to release project: " + projectId, e );
3225             }
3226             catch ( BuildAgentConfigurationException e )
3227             {
3228                 throw new ContinuumException( "Failed to release project: " + projectId, e );
3229             }
3230         }
3231         else
3232         {
3233             result = (ReleaseResult) releaseManager.getReleaseResults().get( releaseId );
3234             ContinuumReleaseDescriptor descriptor =
3235                 (ContinuumReleaseDescriptor) releaseManager.getPreparedReleases().get( releaseId );
3236             if ( descriptor != null )
3237             {
3238                 releaseBy = descriptor.getReleaseBy();
3239             }
3240         }
3241 
3242         if ( result != null && getContinuumReleaseResult( projectId, releaseType, result.getStartTime(),
3243                                                           result.getEndTime() ) == null )
3244         {
3245             ContinuumReleaseResult releaseResult = createContinuumReleaseResult( projectId, releaseType, result,
3246                                                                                  releaseBy );
3247             return addContinuumReleaseResult( releaseResult );
3248         }
3249 
3250         return null;
3251     }
3252 
3253     private ContinuumReleaseResult createContinuumReleaseResult( int projectId, String releaseGoals,
3254                                                                  ReleaseResult result, String releaseBy )
3255         throws ContinuumException
3256     {
3257         ContinuumReleaseResult releaseResult = new ContinuumReleaseResult();
3258         releaseResult.setStartTime( result.getStartTime() );
3259         releaseResult.setEndTime( result.getEndTime() );
3260         releaseResult.setResultCode( result.getResultCode() );
3261 
3262         Project project = getProject( projectId );
3263         ProjectGroup projectGroup = project.getProjectGroup();
3264         releaseResult.setProjectGroup( projectGroup );
3265         releaseResult.setProject( project );
3266         releaseResult.setReleaseGoal( releaseGoals );
3267         releaseResult.setUsername( releaseBy );
3268 
3269         String releaseName = "releases-" + result.getStartTime();
3270 
3271         try
3272         {
3273             File logFile = getConfiguration().getReleaseOutputFile( projectGroup.getId(), releaseName );
3274 
3275             PrintWriter writer = new PrintWriter( new FileWriter( logFile ) );
3276             writer.write( result.getOutput() );
3277             writer.close();
3278         }
3279         catch ( ConfigurationException e )
3280         {
3281             throw new ContinuumException( e.getMessage(), e );
3282         }
3283         catch ( IOException e )
3284         {
3285             throw new ContinuumException( "Unable to write output to file", e );
3286         }
3287 
3288         return releaseResult;
3289     }
3290 
3291     public ContinuumReleaseResult addContinuumReleaseResult( ContinuumReleaseResult releaseResult )
3292         throws ContinuumException
3293     {
3294         try
3295         {
3296             return releaseResultDao.addContinuumReleaseResult( releaseResult );
3297         }
3298         catch ( ContinuumStoreException e )
3299         {
3300             throw new ContinuumException( "Error while adding continuumReleaseResult", e );
3301         }
3302     }
3303 
3304     public void removeContinuumReleaseResult( int releaseResultId )
3305         throws ContinuumException
3306     {
3307         ContinuumReleaseResult releaseResult = getContinuumReleaseResult( releaseResultId );
3308 
3309         try
3310         {
3311             releaseResultDao.removeContinuumReleaseResult( releaseResult );
3312         }
3313         catch ( ContinuumStoreException e )
3314         {
3315             throw new ContinuumException( "Error while deleting continuumReleaseResult: " + releaseResultId, e );
3316         }
3317 
3318         try
3319         {
3320             int projectGroupId = releaseResult.getProjectGroup().getId();
3321 
3322             String name = "releases-" + releaseResult.getStartTime();
3323 
3324             File releaseFile = getConfiguration().getReleaseOutputFile( projectGroupId, name );
3325 
3326             if ( releaseFile.exists() )
3327             {
3328                 try
3329                 {
3330                     FileUtils.forceDelete( releaseFile );
3331                 }
3332                 catch ( IOException e )
3333                 {
3334                     throw new ContinuumException( "Can't delete " + releaseFile.getAbsolutePath(), e );
3335                 }
3336             }
3337         }
3338         catch ( ConfigurationException e )
3339         {
3340             log.info( "skip error during cleanup release files " + e.getMessage(), e );
3341         }
3342     }
3343 
3344     public ContinuumReleaseResult getContinuumReleaseResult( int releaseResultId )
3345         throws ContinuumException
3346     {
3347         try
3348         {
3349             return releaseResultDao.getContinuumReleaseResult( releaseResultId );
3350         }
3351         catch ( ContinuumObjectNotFoundException e )
3352         {
3353             throw new ContinuumException( "No continuumReleaseResult found: " + releaseResultId );
3354         }
3355         catch ( ContinuumStoreException e )
3356         {
3357             throw new ContinuumException( "Error while retrieving continuumReleaseResult: " + releaseResultId, e );
3358         }
3359     }
3360 
3361     public List<ContinuumReleaseResult> getAllContinuumReleaseResults()
3362     {
3363         return releaseResultDao.getAllContinuumReleaseResults();
3364     }
3365 
3366     public List<ContinuumReleaseResult> getContinuumReleaseResultsByProjectGroup( int projectGroupId )
3367     {
3368         return releaseResultDao.getContinuumReleaseResultsByProjectGroup( projectGroupId );
3369     }
3370 
3371     public ContinuumReleaseResult getContinuumReleaseResult( int projectId, String releaseGoal, long startTime,
3372                                                              long endTime )
3373         throws ContinuumException
3374     {
3375         try
3376         {
3377             return releaseResultDao.getContinuumReleaseResult( projectId, releaseGoal, startTime, endTime );
3378         }
3379         catch ( ContinuumStoreException e )
3380         {
3381             throw new ContinuumException(
3382                 "Error while retrieving continuumReleaseResult of projectId " + projectId + " with releaseGoal: " +
3383                     releaseGoal, e );
3384         }
3385     }
3386 
3387     public String getReleaseOutput( int releaseResultId )
3388         throws ContinuumException
3389     {
3390         ContinuumReleaseResult releaseResult = getContinuumReleaseResult( releaseResultId );
3391 
3392         ProjectGroup projectGroup = releaseResult.getProjectGroup();
3393 
3394         try
3395         {
3396             return configurationService.getReleaseOutput( projectGroup.getId(),
3397                                                           "releases-" + releaseResult.getStartTime() );
3398         }
3399         catch ( ConfigurationException e )
3400         {
3401             throw new ContinuumException( "Error while retrieving release output for release: " + releaseResultId );
3402         }
3403     }
3404 
3405     public List<ProjectScmRoot> getProjectScmRootByProjectGroup( int projectGroupId )
3406     {
3407         return projectScmRootDao.getProjectScmRootByProjectGroup( projectGroupId );
3408     }
3409 
3410     public ProjectScmRoot getProjectScmRoot( int projectScmRootId )
3411         throws ContinuumException
3412     {
3413         try
3414         {
3415             return projectScmRootDao.getProjectScmRoot( projectScmRootId );
3416         }
3417         catch ( ContinuumObjectNotFoundException e )
3418         {
3419             throw new ContinuumException( "No projectScmRoot found with the given id: " + projectScmRootId );
3420         }
3421         catch ( ContinuumStoreException e )
3422         {
3423             throw new ContinuumException( "Error while retrieving projectScmRoot ", e );
3424         }
3425     }
3426 
3427     public ProjectScmRoot getProjectScmRootByProject( int projectId )
3428         throws ContinuumException
3429     {
3430         Project project = getProject( projectId );
3431         ProjectGroup group = getProjectGroupByProjectId( projectId );
3432 
3433         List<ProjectScmRoot> scmRoots = getProjectScmRootByProjectGroup( group.getId() );
3434 
3435         for ( ProjectScmRoot scmRoot : scmRoots )
3436         {
3437             if ( project.getScmUrl() != null && project.getScmUrl().startsWith( scmRoot.getScmRootAddress() ) )
3438             {
3439                 return scmRoot;
3440             }
3441         }
3442         return null;
3443     }
3444 
3445     public ProjectScmRoot getProjectScmRootByProjectGroupAndScmRootAddress( int projectGroupId, String scmRootAddress )
3446         throws ContinuumException
3447     {
3448         try
3449         {
3450             return projectScmRootDao.getProjectScmRootByProjectGroupAndScmRootAddress( projectGroupId, scmRootAddress );
3451         }
3452         catch ( ContinuumStoreException e )
3453         {
3454             throw new ContinuumException( "Error while retrieving project scm root for " + projectGroupId, e );
3455         }
3456     }
3457 
3458     private void removeProjectScmRoot( ProjectScmRoot projectScmRoot )
3459         throws ContinuumException
3460     {
3461         if ( projectScmRoot == null )
3462         {
3463             return;
3464         }
3465 
3466         //get all projects in the group
3467         ProjectGroup group = getProjectGroupWithProjects( projectScmRoot.getProjectGroup().getId() );
3468 
3469         List<Project> projects = group.getProjects();
3470 
3471         boolean found = false;
3472         for ( Project project : projects )
3473         {
3474             if ( project.getScmUrl() != null && project.getScmUrl().startsWith( projectScmRoot.getScmRootAddress() ) )
3475             {
3476                 found = true;
3477                 break;
3478             }
3479         }
3480 
3481         if ( !found )
3482         {
3483             log.info( "Removing project scm root '" + projectScmRoot.getScmRootAddress() + "'" );
3484             try
3485             {
3486                 projectScmRootDao.removeProjectScmRoot( projectScmRoot );
3487             }
3488             catch ( ContinuumStoreException e )
3489             {
3490                 log.error( "Failed to remove project scm root '" + projectScmRoot.getScmRootAddress() + "'", e );
3491                 throw new ContinuumException(
3492                     "Error while removing project scm root '" + projectScmRoot.getScmRootAddress() + "'", e );
3493             }
3494         }
3495         else
3496         {
3497             log.info(
3498                 "Project scm root '" + projectScmRoot.getScmRootAddress() + "' still has projects, not removing" );
3499         }
3500     }
3501 
3502     public BuildQueue addBuildQueue( BuildQueue buildQueue )
3503         throws ContinuumException
3504     {
3505         try
3506         {
3507             return buildQueueService.addBuildQueue( buildQueue );
3508         }
3509         catch ( BuildQueueServiceException e )
3510         {
3511             throw new ContinuumException( "Error adding build queue to the database.", e );
3512         }
3513     }
3514 
3515     public BuildQueue getBuildQueue( int buildQueueId )
3516         throws ContinuumException
3517     {
3518         try
3519         {
3520             return buildQueueService.getBuildQueue( buildQueueId );
3521         }
3522         catch ( BuildQueueServiceException e )
3523         {
3524             throw new ContinuumException( "Error retrieving build queue.", e );
3525         }
3526     }
3527 
3528     public BuildQueue getBuildQueueByName( String buildQueueName )
3529         throws ContinuumException
3530     {
3531         try
3532         {
3533             return buildQueueService.getBuildQueueByName( buildQueueName );
3534         }
3535         catch ( BuildQueueServiceException e )
3536         {
3537             throw new ContinuumException( "Error retrieving build queue.", e );
3538         }
3539     }
3540 
3541     public void removeBuildQueue( BuildQueue buildQueue )
3542         throws ContinuumException
3543     {
3544         try
3545         {
3546             buildQueueService.removeBuildQueue( buildQueue );
3547         }
3548         catch ( BuildQueueServiceException e )
3549         {
3550             throw new ContinuumException( "Error deleting build queue from database.", e );
3551         }
3552     }
3553 
3554     public BuildQueue storeBuildQueue( BuildQueue buildQueue )
3555         throws ContinuumException
3556     {
3557         try
3558         {
3559             return buildQueueService.updateBuildQueue( buildQueue );
3560         }
3561         catch ( BuildQueueServiceException e )
3562         {
3563             throw new ContinuumException( "Error updating build queue.", e );
3564         }
3565     }
3566 
3567     public List<BuildQueue> getAllBuildQueues()
3568         throws ContinuumException
3569     {
3570         try
3571         {
3572             return buildQueueService.getAllBuildQueues();
3573         }
3574         catch ( BuildQueueServiceException e )
3575         {
3576             throw new ContinuumException( "Error adding build queue.", e );
3577         }
3578     }
3579 
3580     private void prepareBuildProjects( Collection<Project> projects, List<BuildDefinition> bds,
3581                                        boolean checkDefaultBuildDefinitionForProject, BuildTrigger buildTrigger )
3582         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
3583     {
3584         Map<ProjectScmRoot, Map<Integer, Integer>> map = new HashMap<ProjectScmRoot, Map<Integer, Integer>>();
3585         List<ProjectScmRoot> sortedScmRoot = new ArrayList<ProjectScmRoot>();
3586 
3587         for ( Project project : projects )
3588         {
3589             int projectId = project.getId();
3590 
3591             int buildDefId = -1;
3592 
3593             if ( bds != null )
3594             {
3595                 for ( BuildDefinition bd : bds )
3596                 {
3597                     if ( project.getExecutorId().equals( bd.getType() ) || ( StringUtils.isEmpty( bd.getType() ) &&
3598                         ContinuumBuildExecutorConstants.MAVEN_TWO_BUILD_EXECUTOR.equals( project.getExecutorId() ) ) )
3599                     {
3600                         buildDefId = bd.getId();
3601                         break;
3602                     }
3603                 }
3604             }
3605 
3606             if ( checkDefaultBuildDefinitionForProject )
3607             {
3608                 BuildDefinition projectDefaultBD = null;
3609                 try
3610                 {
3611                     projectDefaultBD = buildDefinitionDao.getDefaultBuildDefinitionForProject( projectId );
3612                 }
3613                 catch ( ContinuumObjectNotFoundException e )
3614                 {
3615                     log.debug( e.getMessage() );
3616                 }
3617                 catch ( ContinuumStoreException e )
3618                 {
3619                     log.debug( e.getMessage() );
3620                 }
3621 
3622                 if ( projectDefaultBD != null )
3623                 {
3624                     buildDefId = projectDefaultBD.getId();
3625                     log.debug( "Project " + project.getId() +
3626                                    " has own default build definition, will use it instead of group's." );
3627                 }
3628             }
3629 
3630             if ( buildDefId == -1 )
3631             {
3632                 log.info( "Project " + projectId +
3633                               " don't have a default build definition defined in the project or project group, will not be included in group build." );
3634                 continue;
3635             }
3636 
3637             // check if project already in queue
3638             if ( !isProjectOkToBuild( projectId, buildDefId ) )
3639             {
3640                 log.info(
3641                     "Not queueing the build with projectId={} and buildDefinitionId={} because it is already building",
3642                     projectId, buildDefId );
3643                 continue;
3644             }
3645 
3646             ProjectScmRoot scmRoot = getProjectScmRootByProject( projectId );
3647 
3648             Map<Integer, Integer> projectsAndBuildDefinitionsMap = map.get( scmRoot );
3649 
3650             if ( projectsAndBuildDefinitionsMap == null )
3651             {
3652                 projectsAndBuildDefinitionsMap = new HashMap<Integer, Integer>();
3653             }
3654 
3655             projectsAndBuildDefinitionsMap.put( projectId, buildDefId );
3656 
3657             map.put( scmRoot, projectsAndBuildDefinitionsMap );
3658 
3659             if ( !sortedScmRoot.contains( scmRoot ) )
3660             {
3661                 sortedScmRoot.add( scmRoot );
3662             }
3663         }
3664 
3665         prepareBuildProjects( map, buildTrigger, sortedScmRoot );
3666     }
3667 
3668     private void prepareBuildProjects( Collection<Project> projects, int buildDefinitionId, BuildTrigger buildTrigger )
3669         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
3670     {
3671         Map<ProjectScmRoot, Map<Integer, Integer>> map = new HashMap<ProjectScmRoot, Map<Integer, Integer>>();
3672         List<ProjectScmRoot> sortedScmRoot = new ArrayList<ProjectScmRoot>();
3673 
3674         for ( Project project : projects )
3675         {
3676             int projectId = project.getId();
3677 
3678             // check if project already in queue
3679             if ( !isProjectOkToBuild( projectId, buildDefinitionId ) )
3680             {
3681                 log.info(
3682                     "Not queueing the build with projectId={} and buildDefinitionId={} because it is already building",
3683                     projectId, buildDefinitionId );
3684                 continue;
3685             }
3686 
3687             ProjectScmRoot scmRoot = getProjectScmRootByProject( projectId );
3688 
3689             Map<Integer, Integer> projectsAndBuildDefinitionsMap = map.get( scmRoot );
3690 
3691             if ( projectsAndBuildDefinitionsMap == null )
3692             {
3693                 projectsAndBuildDefinitionsMap = new HashMap<Integer, Integer>();
3694             }
3695 
3696             projectsAndBuildDefinitionsMap.put( projectId, buildDefinitionId );
3697 
3698             map.put( scmRoot, projectsAndBuildDefinitionsMap );
3699 
3700             if ( !sortedScmRoot.contains( scmRoot ) )
3701             {
3702                 sortedScmRoot.add( scmRoot );
3703             }
3704         }
3705 
3706         prepareBuildProjects( map, buildTrigger, sortedScmRoot );
3707     }
3708 
3709     private void prepareBuildProjects( Map<ProjectScmRoot, Map<Integer, Integer>> map, BuildTrigger buildTrigger,
3710                                        List<ProjectScmRoot> scmRoots )
3711         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
3712     {
3713         for ( ProjectScmRoot scmRoot : scmRoots )
3714         {
3715             prepareBuildProjects( map.get( scmRoot ), buildTrigger, scmRoot.getScmRootAddress(),
3716                                   scmRoot.getProjectGroup().getId(), scmRoot.getId(), scmRoots );
3717         }
3718     }
3719 
3720     private void prepareBuildProjects( Map<Integer, Integer> projectsBuildDefinitionsMap, BuildTrigger buildTrigger,
3721                                        String scmRootAddress, int projectGroupId, int scmRootId,
3722                                        List<ProjectScmRoot> scmRoots )
3723         throws ContinuumException, NoBuildAgentException, NoBuildAgentInGroupException
3724     {
3725         ProjectGroup group = getProjectGroup( projectGroupId );
3726 
3727         try
3728         {
3729             if ( configurationService.isDistributedBuildEnabled() )
3730             {
3731                 distributedBuildManager.prepareBuildProjects( projectsBuildDefinitionsMap, buildTrigger, projectGroupId,
3732                                                               group.getName(), scmRootAddress, scmRootId, scmRoots );
3733             }
3734             else
3735             {
3736                 parallelBuildsManager.prepareBuildProjects( projectsBuildDefinitionsMap, buildTrigger, projectGroupId,
3737                                                             group.getName(), scmRootAddress, scmRootId );
3738             }
3739         }
3740         catch ( BuildManagerException e )
3741         {
3742             throw logAndCreateException( "Error while creating enqueuing object.", e );
3743         }
3744     }
3745 
3746     private void createProjectScmRootForProjectGroup( ProjectGroup projectGroup )
3747         throws ContinuumException
3748     {
3749         List<Project> projectsList;
3750 
3751         projectsList = getProjectsInBuildOrder( projectDao.getProjectsWithDependenciesByGroupId(
3752             projectGroup.getId() ) );
3753 
3754         List<ProjectScmRoot> scmRoots = getProjectScmRootByProjectGroup( projectGroup.getId() );
3755 
3756         String url = "";
3757 
3758         for ( Project project : projectsList )
3759         {
3760             boolean found = false;
3761 
3762             if ( StringUtils.isEmpty( url ) || !project.getScmUrl().startsWith( url ) )
3763             {
3764                 // this is a root project or the project is part of a flat multi module
3765                 url = project.getScmUrl();
3766                 //createProjectScmRoot( projectGroup, url );
3767 
3768                 for ( ProjectScmRoot scmRoot : scmRoots )
3769                 {
3770                     if ( url.startsWith( scmRoot.getScmRootAddress() ) )
3771                     {
3772                         found = true;
3773                     }
3774                 }
3775 
3776                 if ( !found )
3777                 {
3778                     createProjectScmRoot( projectGroup, url );
3779                 }
3780             }
3781         }
3782     }
3783 
3784     private ProjectScmRoot createProjectScmRoot( ProjectGroup projectGroup, String url )
3785         throws ContinuumException
3786     {
3787         if ( StringUtils.isEmpty( url ) )
3788         {
3789             return null;
3790         }
3791 
3792         try
3793         {
3794             ProjectScmRoot scmRoot = projectScmRootDao.getProjectScmRootByProjectGroupAndScmRootAddress(
3795                 projectGroup.getId(), url );
3796 
3797             if ( scmRoot != null )
3798             {
3799                 return null;
3800             }
3801 
3802             ProjectScmRoot projectScmRoot = new ProjectScmRoot();
3803 
3804             projectScmRoot.setProjectGroup( projectGroup );
3805 
3806             projectScmRoot.setScmRootAddress( url );
3807 
3808             return projectScmRootDao.addProjectScmRoot( projectScmRoot );
3809         }
3810         catch ( ContinuumStoreException e )
3811         {
3812             throw new ContinuumException( "Error while creating project scm root with scm root address:" + url );
3813         }
3814     }
3815 
3816     private void updateProjectScmRoot( ProjectScmRoot oldScmRoot, Project project )
3817         throws ContinuumException
3818     {
3819         try
3820         {
3821             removeProjectScmRoot( oldScmRoot );
3822             ProjectScmRoot scmRoot = projectScmRootDao.getProjectScmRootByProjectGroupAndScmRootAddress(
3823                 project.getProjectGroup().getId(), project.getScmUrl() );
3824             if ( scmRoot == null )
3825             {
3826                 ProjectScmRoot newScmRoot = new ProjectScmRoot();
3827                 if ( project.getScmUrl().equals( oldScmRoot.getScmRootAddress() ) )
3828                 {
3829                     BeanUtils.copyProperties( oldScmRoot, newScmRoot, new String[]{"id", "projectGroup"} );
3830                 }
3831                 else
3832                 {
3833                     newScmRoot.setScmRootAddress( project.getScmUrl() );
3834                 }
3835                 newScmRoot.setProjectGroup( project.getProjectGroup() );
3836                 projectScmRootDao.addProjectScmRoot( newScmRoot );
3837             }
3838         }
3839         catch ( ContinuumStoreException ex )
3840         {
3841             throw logAndCreateException( "Error while updating project.", ex );
3842         }
3843     }
3844 
3845     private boolean isProjectInReleaseStage( Project project )
3846         throws ContinuumException
3847     {
3848         String releaseId = project.getGroupId() + ":" + project.getArtifactId();
3849         try
3850         {
3851             return taskQueueManager.isProjectInReleaseStage( releaseId );
3852         }
3853         catch ( TaskQueueManagerException e )
3854         {
3855             throw new ContinuumException( "Error occurred while checking if project is currently being released.", e );
3856         }
3857     }
3858 
3859     private boolean isAnyProjectInGroupInReleaseStage( int projectGroupId )
3860         throws ContinuumException
3861     {
3862         Collection<Project> projects = getProjectsInGroup( projectGroupId );
3863         for ( Project project : projects )
3864         {
3865             if ( isProjectInReleaseStage( project ) )
3866             {
3867                 throw new ContinuumException( "Cannot build project group. Project (id=" + project.getId() +
3868                                                   ") in group is currently in release stage." );
3869             }
3870         }
3871         return false;
3872     }
3873 
3874     private boolean isAnyProjectsInReleaseStage( List<Project> projects )
3875         throws ContinuumException
3876     {
3877         for ( Project project : projects )
3878         {
3879             if ( isProjectInReleaseStage( project ) )
3880             {
3881                 return true;
3882             }
3883         }
3884 
3885         return false;
3886     }
3887 
3888     private Collection<Project> getProjectsNotInReleaseStage( Collection<Project> projectsList )
3889         throws ContinuumException
3890     {
3891         // filter the projects to be built
3892         // projects that are in the release stage will not be built
3893         Collection<Project> filteredProjectsList = new ArrayList<Project>();
3894         for ( Project project : projectsList )
3895         {
3896             if ( !isProjectInReleaseStage( project ) )
3897             {
3898                 filteredProjectsList.add( project );
3899             }
3900             else
3901             {
3902                 log.warn(
3903                     "Project (id=" + project.getId() + ") will not be built. It is currently in the release stage." );
3904             }
3905         }
3906         return filteredProjectsList;
3907     }
3908 
3909     private void checkForDuplicateProjectInGroup( ProjectGroup projectGroup, Project projectToCheck,
3910                                                   ContinuumProjectBuildingResult result )
3911     {
3912         List<Project> projectsInGroup = projectGroup.getProjects();
3913 
3914         if ( projectsInGroup == null )
3915         {
3916             return;
3917         }
3918 
3919         for ( Project project : projectGroup.getProjects() )
3920         {
3921             // projectToCheck is first in the equals check, as projectToCheck must be a Maven project and will have
3922             // non-null values for each. project may be an Ant or Shell project and have null values.
3923             if ( projectToCheck.getGroupId().equals( project.getGroupId() ) && projectToCheck.getArtifactId().equals(
3924                 project.getArtifactId() ) && projectToCheck.getVersion().equals( project.getVersion() ) )
3925             {
3926                 result.addError( ContinuumProjectBuildingResult.ERROR_DUPLICATE_PROJECTS );
3927                 return;
3928             }
3929         }
3930     }
3931 
3932     private boolean isProjectOkToBuild( int projectId, int buildDefinitionId )
3933         throws ContinuumException
3934     {
3935         if ( configurationService.isDistributedBuildEnabled() )
3936         {
3937             if ( !distributedBuildManager.isProjectInAnyPrepareBuildQueue( projectId, buildDefinitionId ) &&
3938                 !distributedBuildManager.isProjectInAnyBuildQueue( projectId, buildDefinitionId ) &&
3939                 !distributedBuildManager.isProjectCurrentlyPreparingBuild( projectId, buildDefinitionId ) &&
3940                 !distributedBuildManager.isProjectCurrentlyBuilding( projectId, buildDefinitionId ) )
3941             {
3942                 return true;
3943             }
3944         }
3945         else
3946         {
3947             try
3948             {
3949                 if ( !parallelBuildsManager.isInAnyBuildQueue( projectId, buildDefinitionId ) &&
3950                     !parallelBuildsManager.isInAnyCheckoutQueue( projectId ) &&
3951                     !parallelBuildsManager.isInPrepareBuildQueue( projectId ) &&
3952                     !parallelBuildsManager.isProjectCurrentlyPreparingBuild( projectId ) )
3953                 {
3954                     if ( parallelBuildsManager.isInAnyCheckoutQueue( projectId ) )
3955                     {
3956                         parallelBuildsManager.removeProjectFromCheckoutQueue( projectId );
3957                     }
3958 
3959                     return true;
3960                 }
3961             }
3962             catch ( BuildManagerException e )
3963             {
3964                 throw new ContinuumException( e.getMessage(), e );
3965             }
3966         }
3967 
3968         return false;
3969     }
3970 
3971     void setTaskQueueManager( TaskQueueManager taskQueueManager )
3972     {
3973         this.taskQueueManager = taskQueueManager;
3974     }
3975 
3976     void setProjectDao( ProjectDao projectDao )
3977     {
3978         this.projectDao = projectDao;
3979     }
3980 
3981     public DistributedBuildManager getDistributedBuildManager()
3982     {
3983         return distributedBuildManager;
3984     }
3985 }