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