1 package org.apache.maven.continuum.scm.queue;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.continuum.dao.BuildDefinitionDao;
23 import org.apache.continuum.dao.BuildResultDao;
24 import org.apache.continuum.dao.ProjectDao;
25 import org.apache.continuum.dao.ProjectGroupDao;
26 import org.apache.continuum.dao.ProjectScmRootDao;
27 import org.apache.continuum.model.project.ProjectScmRoot;
28 import org.apache.continuum.taskqueue.PrepareBuildProjectsTask;
29 import org.apache.continuum.utils.ContinuumUtils;
30 import org.apache.continuum.utils.ProjectSorter;
31 import org.apache.continuum.utils.build.BuildTrigger;
32 import org.apache.maven.continuum.core.action.AbstractContinuumAction;
33 import org.apache.maven.continuum.core.action.CheckWorkingDirectoryAction;
34 import org.apache.maven.continuum.core.action.CheckoutProjectContinuumAction;
35 import org.apache.maven.continuum.core.action.UpdateWorkingDirectoryFromScmContinuumAction;
36 import org.apache.maven.continuum.model.project.BuildDefinition;
37 import org.apache.maven.continuum.model.project.BuildResult;
38 import org.apache.maven.continuum.model.project.Project;
39 import org.apache.maven.continuum.model.project.ProjectGroup;
40 import org.apache.maven.continuum.model.scm.ChangeSet;
41 import org.apache.maven.continuum.model.scm.ScmResult;
42 import org.apache.maven.continuum.notification.ContinuumNotificationDispatcher;
43 import org.apache.maven.continuum.project.ContinuumProjectState;
44 import org.apache.maven.continuum.store.ContinuumStoreException;
45 import org.apache.maven.continuum.utils.WorkingDirectoryService;
46 import org.codehaus.plexus.action.ActionManager;
47 import org.codehaus.plexus.action.ActionNotFoundException;
48 import org.codehaus.plexus.taskqueue.Task;
49 import org.codehaus.plexus.taskqueue.execution.TaskExecutionException;
50 import org.codehaus.plexus.taskqueue.execution.TaskExecutor;
51 import org.codehaus.plexus.util.StringUtils;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 import java.io.File;
56 import java.util.ArrayList;
57 import java.util.HashMap;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.Set;
61
62
63
64
65
66
67
68 public class PrepareBuildProjectsTaskExecutor
69 implements TaskExecutor
70 {
71 private static final Logger log = LoggerFactory.getLogger( PrepareBuildProjectsTaskExecutor.class );
72
73
74
75
76 private ActionManager actionManager;
77
78
79
80
81 private ProjectDao projectDao;
82
83
84
85
86 private BuildDefinitionDao buildDefinitionDao;
87
88
89
90
91 private ProjectScmRootDao projectScmRootDao;
92
93
94
95
96 private BuildResultDao buildResultDao;
97
98
99
100
101 private WorkingDirectoryService workingDirectoryService;
102
103
104
105
106 private ContinuumNotificationDispatcher notifierDispatcher;
107
108
109
110
111 private ProjectGroupDao projectGroupDao;
112
113 public void executeTask( Task task )
114 throws TaskExecutionException
115 {
116 PrepareBuildProjectsTask prepareTask = (PrepareBuildProjectsTask) task;
117
118 Map<Integer, Integer> projectsBuildDefinitionsMap = prepareTask.getProjectsBuildDefinitionsMap();
119 BuildTrigger buildTrigger = prepareTask.getBuildTrigger();
120 Set<Integer> projectsId = projectsBuildDefinitionsMap.keySet();
121 Map<String, Object> context = new HashMap<String, Object>();
122 Map<Integer, ScmResult> scmResultMap = new HashMap<Integer, ScmResult>();
123 List<Project> projectList = new ArrayList<Project>();
124 int projectGroupId = 0;
125
126 try
127 {
128 if ( !projectsId.isEmpty() )
129 {
130 int projectId = projectsId.iterator().next();
131 Project project = projectDao.getProject( projectId );
132 ProjectGroup projectGroup = project.getProjectGroup();
133 projectGroupId = projectGroup.getId();
134
135 List<Project> projects = projectDao.getProjectsWithDependenciesByGroupId( projectGroupId );
136 projectList = ProjectSorter.getSortedProjects( projects, log );
137 }
138
139 Project rootProject = null;
140
141 for ( Project project : projectList )
142 {
143 if ( rootProject == null )
144 {
145
146 rootProject = project;
147 }
148
149 int projectId = project.getId();
150 int buildDefinitionId;
151
152 if ( projectsBuildDefinitionsMap.get( projectId ) != null )
153 {
154 buildDefinitionId = projectsBuildDefinitionsMap.get( projectId );
155
156 log.info( "Initializing prepare build" );
157 context = initializeContext( project, buildDefinitionId, prepareTask.getBuildTrigger() );
158
159 log.info( "Starting prepare build of project: " + AbstractContinuumAction.getProject(
160 context ).getName() );
161 startPrepareBuild( context );
162
163 if ( !checkProjectScmRoot( context ) )
164 {
165 break;
166 }
167
168 try
169 {
170 if ( AbstractContinuumAction.getBuildDefinition( context ).isBuildFresh() )
171 {
172 log.info( "Purging existing working copy" );
173 cleanWorkingDirectory( context );
174 }
175
176
177
178
179
180
181 log.info( "Updating working dir" );
182 updateWorkingDirectory( context, rootProject );
183
184 log.info( "Merging SCM results" );
185
186 if ( !AbstractContinuumAction.getBuildDefinition( context ).isBuildFresh() )
187 {
188 mergeScmResults( context );
189 }
190 }
191 finally
192 {
193 log.info( "Ending prepare build of project: " + AbstractContinuumAction.getProject(
194 context ).getName() );
195 scmResultMap.put( AbstractContinuumAction.getProjectId( context ),
196 AbstractContinuumAction.getScmResult( context, null ) );
197 endProjectPrepareBuild( context );
198 }
199 }
200 }
201 }
202 catch ( ContinuumStoreException e )
203 {
204 throw new TaskExecutionException( "Failed to prepare build project group: " + projectGroupId, e );
205 }
206 finally
207 {
208 log.info( "Ending prepare build" );
209 endPrepareBuild( context );
210 }
211
212 if ( checkProjectScmRoot( context ) )
213 {
214 projectGroupId = AbstractContinuumAction.getProjectGroupId( context );
215 buildProjects( projectGroupId, projectList, projectsBuildDefinitionsMap, buildTrigger, scmResultMap );
216 }
217 }
218
219 private Map<String, Object> initializeContext( Project project, int buildDefinitionId, BuildTrigger buildTrigger )
220 throws TaskExecutionException
221 {
222 Map<String, Object> context = new HashMap<String, Object>();
223
224 try
225 {
226 ProjectGroup projectGroup = project.getProjectGroup();
227
228 List<ProjectScmRoot> scmRoots = projectScmRootDao.getProjectScmRootByProjectGroup( projectGroup.getId() );
229 String projectScmUrl = project.getScmUrl();
230 String projectScmRootAddress = "";
231
232 for ( ProjectScmRoot projectScmRoot : scmRoots )
233 {
234 projectScmRootAddress = projectScmRoot.getScmRootAddress();
235
236 if ( projectScmUrl.startsWith( projectScmRoot.getScmRootAddress() ) )
237 {
238 AbstractContinuumAction.setProjectScmRoot( context, projectScmRoot );
239 AbstractContinuumAction.setProjectScmRootUrl( context, projectScmRootAddress );
240 break;
241 }
242 }
243
244 AbstractContinuumAction.setProjectGroupId( context, projectGroup.getId() );
245 AbstractContinuumAction.setProjectId( context, project.getId() );
246 AbstractContinuumAction.setProject( context, project );
247 AbstractContinuumAction.setBuildTrigger( context, buildTrigger );
248
249 AbstractContinuumAction.setBuildDefinitionId( context, buildDefinitionId );
250 AbstractContinuumAction.setBuildDefinition( context, buildDefinitionDao.getBuildDefinition(
251 buildDefinitionId ) );
252
253 if ( project.isCheckedOutInSingleDirectory() )
254 {
255 List<Project> projectsInGroup = projectGroupDao.getProjectGroupWithProjects(
256 projectGroup.getId() ).getProjects();
257 List<Project> projectsWithCommonScmRoot = new ArrayList<Project>();
258 for ( Project projectInGroup : projectsInGroup )
259 {
260 if ( projectInGroup.getScmUrl().startsWith( projectScmRootAddress ) )
261 {
262 projectsWithCommonScmRoot.add( projectInGroup );
263 }
264 }
265 AbstractContinuumAction.setListOfProjectsInGroupWithCommonScmRoot( context, projectsWithCommonScmRoot );
266 }
267
268 BuildResult oldBuildResult = buildResultDao.getLatestBuildResultForBuildDefinition( project.getId(),
269 buildDefinitionId );
270
271 if ( oldBuildResult != null )
272 {
273 AbstractContinuumAction.setOldScmResult( context, getOldScmResults( project.getId(),
274 oldBuildResult.getBuildNumber(),
275 oldBuildResult.getEndTime() ) );
276 }
277 else
278 {
279 AbstractContinuumAction.setOldScmResult( context, null );
280 }
281 }
282 catch ( ContinuumStoreException e )
283 {
284 throw new TaskExecutionException( "Error initializing pre-build context", e );
285 }
286
287 return context;
288 }
289
290 private void cleanWorkingDirectory( Map<String, Object> context )
291 throws TaskExecutionException
292 {
293 performAction( "clean-working-directory", context );
294 }
295
296 private void updateWorkingDirectory( Map<String, Object> context, Project rootProject )
297 throws TaskExecutionException
298 {
299 performAction( "check-working-directory", context );
300
301 boolean workingDirectoryExists = CheckWorkingDirectoryAction.isWorkingDirectoryExists( context );
302
303 ScmResult scmResult;
304
305 if ( workingDirectoryExists )
306 {
307 performAction( "update-working-directory-from-scm", context );
308
309 scmResult = UpdateWorkingDirectoryFromScmContinuumAction.getUpdateScmResult( context, null );
310 }
311 else
312 {
313 Project project = AbstractContinuumAction.getProject( context );
314
315 AbstractContinuumAction.setWorkingDirectory( context, workingDirectoryService.getWorkingDirectory(
316 project ).getAbsolutePath() );
317
318 List<Project> projectsWithCommonScmRoot = AbstractContinuumAction.getListOfProjectsInGroupWithCommonScmRoot(
319 context );
320 String projectScmRootUrl = AbstractContinuumAction.getProjectScmRootUrl( context, project.getScmUrl() );
321 String workingDir = null;
322
323 if ( rootProject.getId() == project.getId() )
324 {
325 workingDir = workingDirectoryService.getWorkingDirectory( project, false ).getAbsolutePath();
326
327 if ( project.isCheckedOutInSingleDirectory() )
328 {
329 File parentDir = new File( workingDir );
330
331 while ( !isRootDirectory( parentDir.getAbsolutePath(), project ) )
332 {
333 parentDir = parentDir.getParentFile();
334 }
335
336 if ( !parentDir.exists() )
337 {
338 workingDir = parentDir.getAbsolutePath();
339 }
340 }
341 }
342
343 if ( workingDir == null || new File( workingDir ).exists() )
344 {
345 workingDir = workingDirectoryService.getWorkingDirectory( project, projectScmRootUrl,
346 projectsWithCommonScmRoot ).getAbsolutePath();
347 }
348
349 AbstractContinuumAction.setWorkingDirectory( context, workingDir );
350
351 if ( rootProject.getId() != project.getId() || ( rootProject.getId() == project.getId() && !isRootDirectory(
352 workingDir, rootProject ) ) )
353 {
354 AbstractContinuumAction.setRootDirectory( context, false );
355 }
356
357 performAction( "checkout-project", context );
358
359 scmResult = CheckoutProjectContinuumAction.getCheckoutScmResult( context, null );
360 }
361
362
363 if ( scmResult == null )
364 {
365 log.debug( "Returned ScmResult is null when updating the working directory" );
366 scmResult = new ScmResult();
367 }
368
369 AbstractContinuumAction.setScmResult( context, scmResult );
370 }
371
372 private boolean checkProjectScmRoot( Map<String, Object> context )
373 throws TaskExecutionException
374 {
375 ProjectScmRoot projectScmRoot = AbstractContinuumAction.getProjectScmRoot( context );
376
377
378 return projectScmRoot.getState() != ContinuumProjectState.ERROR;
379
380 }
381
382 private void startPrepareBuild( Map<String, Object> context )
383 throws TaskExecutionException
384 {
385 ProjectScmRoot projectScmRoot = AbstractContinuumAction.getProjectScmRoot( context );
386 if ( projectScmRoot.getState() != ContinuumProjectState.UPDATING )
387 {
388 try
389 {
390 projectScmRoot.setOldState( projectScmRoot.getState() );
391 projectScmRoot.setState( ContinuumProjectState.UPDATING );
392 projectScmRootDao.updateProjectScmRoot( projectScmRoot );
393 }
394 catch ( ContinuumStoreException e )
395 {
396 throw new TaskExecutionException( "Error persisting projectScmRoot", e );
397 }
398 }
399 }
400
401 private void endPrepareBuild( Map<String, Object> context )
402 throws TaskExecutionException
403 {
404 ProjectScmRoot projectScmRoot = AbstractContinuumAction.getProjectScmRoot( context );
405
406 if ( projectScmRoot.getState() != ContinuumProjectState.ERROR )
407 {
408 projectScmRoot.setState( ContinuumProjectState.UPDATED );
409 projectScmRoot.setError( null );
410
411 try
412 {
413 projectScmRootDao.updateProjectScmRoot( projectScmRoot );
414 }
415 catch ( ContinuumStoreException e )
416 {
417 throw new TaskExecutionException( "Error persisting projectScmRoot", e );
418 }
419 }
420
421 notifierDispatcher.prepareBuildComplete( projectScmRoot );
422 }
423
424
425
426
427
428 private void endProjectPrepareBuild( Map<String, Object> context )
429 throws TaskExecutionException
430 {
431 ScmResult scmResult = AbstractContinuumAction.getScmResult( context, null );
432
433 if ( scmResult == null || !scmResult.isSuccess() )
434 {
435 String error = convertScmResultToError( scmResult );
436
437 updateProjectScmRoot( context, error );
438 }
439 }
440
441 private ScmResult getOldScmResults( int projectId, long startId, long fromDate )
442 throws ContinuumStoreException
443 {
444 List<BuildResult> results = buildResultDao.getBuildResultsForProjectFromId( projectId, startId );
445
446 ScmResult res = new ScmResult();
447
448 if ( results != null && results.size() > 0 )
449 {
450 for ( BuildResult result : results )
451 {
452 ScmResult scmResult = result.getScmResult();
453
454 if ( scmResult != null )
455 {
456 List<ChangeSet> changes = scmResult.getChanges();
457
458 if ( changes != null )
459 {
460 for ( ChangeSet changeSet : changes )
461 {
462 if ( changeSet.getDate() < fromDate )
463 {
464 continue;
465 }
466 if ( !res.getChanges().contains( changeSet ) )
467 {
468 res.addChange( changeSet );
469 }
470 }
471 }
472 }
473 }
474 }
475
476 return res;
477 }
478
479
480
481
482
483
484 private void mergeScmResults( Map<String, Object> context )
485 {
486 ScmResult oldScmResult = AbstractContinuumAction.getOldScmResult( context );
487 ScmResult newScmResult = AbstractContinuumAction.getScmResult( context, null );
488
489 if ( oldScmResult != null )
490 {
491 if ( newScmResult == null )
492 {
493 AbstractContinuumAction.setScmResult( context, oldScmResult );
494 }
495 else
496 {
497 List<ChangeSet> oldChanges = oldScmResult.getChanges();
498
499 List<ChangeSet> newChanges = newScmResult.getChanges();
500
501 for ( ChangeSet change : newChanges )
502 {
503 if ( !oldChanges.contains( change ) )
504 {
505 oldChanges.add( change );
506 }
507 }
508
509 newScmResult.setChanges( oldChanges );
510 }
511 }
512 }
513
514 private void performAction( String actionName, Map<String, Object> context )
515 throws TaskExecutionException
516 {
517 TaskExecutionException exception;
518
519 try
520 {
521 log.info( "Performing action " + actionName );
522 actionManager.lookup( actionName ).execute( context );
523 return;
524 }
525 catch ( ActionNotFoundException e )
526 {
527 exception = new TaskExecutionException( "Error looking up action '" + actionName + "'", e );
528 }
529 catch ( Exception e )
530 {
531 exception = new TaskExecutionException( "Error executing action '" + actionName + "'", e );
532 }
533
534 ScmResult result = new ScmResult();
535
536 result.setSuccess( false );
537
538 result.setException( ContinuumUtils.throwableToString( exception ) );
539
540 AbstractContinuumAction.setScmResult( context, result );
541
542 throw exception;
543 }
544
545 private String convertScmResultToError( ScmResult result )
546 {
547 String error = "";
548
549 if ( result == null )
550 {
551 error = "Scm result is null.";
552 }
553 else
554 {
555 if ( result.getCommandLine() != null )
556 {
557 error = "Command line: " + StringUtils.clean( result.getCommandLine() ) +
558 System.getProperty( "line.separator" );
559 }
560
561 if ( result.getProviderMessage() != null )
562 {
563 error = "Provider message: " + StringUtils.clean( result.getProviderMessage() ) +
564 System.getProperty( "line.separator" );
565 }
566
567 if ( result.getCommandOutput() != null )
568 {
569 error += "Command output: " + System.getProperty( "line.separator" );
570 error += "-------------------------------------------------------------------------------" +
571 System.getProperty( "line.separator" );
572 error += StringUtils.clean( result.getCommandOutput() ) + System.getProperty( "line.separator" );
573 error += "-------------------------------------------------------------------------------" +
574 System.getProperty( "line.separator" );
575 }
576
577 if ( result.getException() != null )
578 {
579 error += "Exception:" + System.getProperty( "line.separator" );
580 error += result.getException();
581 }
582 }
583
584 return error;
585 }
586
587 private void updateProjectScmRoot( Map<String, Object> context, String error )
588 throws TaskExecutionException
589 {
590 ProjectScmRoot projectScmRoot = AbstractContinuumAction.getProjectScmRoot( context );
591
592 try
593 {
594 projectScmRoot.setState( ContinuumProjectState.ERROR );
595 projectScmRoot.setError( error );
596
597 projectScmRootDao.updateProjectScmRoot( projectScmRoot );
598
599 AbstractContinuumAction.setProjectScmRoot( context, projectScmRoot );
600 }
601 catch ( ContinuumStoreException e )
602 {
603 throw new TaskExecutionException( "Error storing project scm root", e );
604 }
605 }
606
607 private void buildProjects( int projectGroupId, List<Project> projectList,
608 Map<Integer, Integer> projectsAndBuildDefinitionsMap, BuildTrigger buildTrigger,
609 Map<Integer, ScmResult> scmResultMap )
610 throws TaskExecutionException
611 {
612 List<Project> projectsToBeBuilt = new ArrayList<Project>();
613 Map<Integer, BuildDefinition> projectsBuildDefinitionsMap = new HashMap<Integer, BuildDefinition>();
614
615 for ( Project project : projectList )
616 {
617 int buildDefinitionId;
618
619 if ( projectsAndBuildDefinitionsMap.get( project.getId() ) != null )
620 {
621 buildDefinitionId = projectsAndBuildDefinitionsMap.get( project.getId() );
622
623 try
624 {
625 BuildDefinition buildDefinition = buildDefinitionDao.getBuildDefinition( buildDefinitionId );
626 projectsBuildDefinitionsMap.put( project.getId(), buildDefinition );
627 projectsToBeBuilt.add( project );
628 }
629 catch ( ContinuumStoreException e )
630 {
631 log.error( "Error while creating build object", e );
632 throw new TaskExecutionException( "Error while creating build object", e );
633 }
634 }
635 }
636
637 try
638 {
639 Map<String, Object> context = new HashMap<String, Object>();
640 AbstractContinuumAction.setListOfProjects( context, projectsToBeBuilt );
641 AbstractContinuumAction.setProjectsBuildDefinitionsMap( context, projectsBuildDefinitionsMap );
642 AbstractContinuumAction.setBuildTrigger( context, buildTrigger );
643 AbstractContinuumAction.setScmResultMap( context, scmResultMap );
644 AbstractContinuumAction.setProjectGroupId( context, projectGroupId );
645
646 log.info( "Performing action create-build-project-task" );
647 actionManager.lookup( "create-build-project-task" ).execute( context );
648 }
649 catch ( ActionNotFoundException e )
650 {
651 log.error( "Error looking up action 'build-project'" );
652 throw new TaskExecutionException( "Error looking up action 'build-project'", e );
653 }
654 catch ( Exception e )
655 {
656 log.error( e.getMessage(), e );
657 throw new TaskExecutionException( "Error executing action 'build-project'", e );
658 }
659 }
660
661 private boolean isRootDirectory( String workingDir, Project rootProject )
662 {
663 return workingDir.endsWith( Integer.toString( rootProject.getId() ) + System.getProperty(
664 "line.separator" ) ) || workingDir.endsWith( Integer.toString( rootProject.getId() ) );
665 }
666 }