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