1 package org.apache.continuum.buildagent.taskqueue.execution;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.commons.lang.StringEscapeUtils;
23 import org.apache.continuum.buildagent.build.execution.ContinuumAgentBuildExecutor;
24 import org.apache.continuum.buildagent.build.execution.ContinuumAgentBuildExecutorException;
25 import org.apache.continuum.buildagent.build.execution.manager.BuildAgentBuildExecutorManager;
26 import org.apache.continuum.buildagent.buildcontext.BuildContext;
27 import org.apache.continuum.buildagent.buildcontext.manager.BuildContextManager;
28 import org.apache.continuum.buildagent.configuration.BuildAgentConfigurationService;
29 import org.apache.continuum.buildagent.installation.BuildAgentInstallationService;
30 import org.apache.continuum.buildagent.manager.BuildAgentManager;
31 import org.apache.continuum.buildagent.model.Installation;
32 import org.apache.continuum.buildagent.model.LocalRepository;
33 import org.apache.continuum.buildagent.utils.BuildContextToBuildDefinition;
34 import org.apache.continuum.buildagent.utils.BuildContextToProject;
35 import org.apache.continuum.buildagent.utils.ContinuumBuildAgentUtil;
36 import org.apache.continuum.taskqueue.BuildProjectTask;
37 import org.apache.maven.continuum.ContinuumException;
38 import org.apache.maven.continuum.execution.ContinuumBuildExecutorConstants;
39 import org.apache.maven.continuum.model.project.BuildDefinition;
40 import org.apache.maven.continuum.model.project.BuildResult;
41 import org.apache.maven.continuum.model.project.Project;
42 import org.apache.maven.continuum.model.project.ProjectGroup;
43 import org.apache.maven.continuum.model.scm.ChangeFile;
44 import org.apache.maven.continuum.model.scm.ChangeSet;
45 import org.apache.maven.continuum.model.scm.ScmResult;
46 import org.apache.maven.continuum.project.ContinuumProjectState;
47 import org.apache.maven.project.MavenProject;
48 import org.apache.maven.scm.ScmException;
49 import org.apache.maven.scm.repository.ScmRepositoryException;
50 import org.codehaus.plexus.action.ActionManager;
51 import org.codehaus.plexus.action.ActionNotFoundException;
52 import org.codehaus.plexus.taskqueue.Task;
53 import org.codehaus.plexus.taskqueue.execution.TaskExecutionException;
54 import org.codehaus.plexus.taskqueue.execution.TaskExecutor;
55 import org.codehaus.plexus.util.FileUtils;
56 import org.codehaus.plexus.util.StringUtils;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 import java.io.File;
61 import java.util.ArrayList;
62 import java.util.HashMap;
63 import java.util.Iterator;
64 import java.util.List;
65 import java.util.Map;
66
67
68
69
70
71 public class BuildProjectTaskExecutor
72 implements TaskExecutor
73 {
74 private static final Logger log = LoggerFactory.getLogger( BuildProjectTaskExecutor.class );
75
76
77
78
79 private BuildContextManager buildContextManager;
80
81
82
83
84 private ActionManager actionManager;
85
86
87
88
89 private BuildAgentConfigurationService buildAgentConfigurationService;
90
91
92
93
94 private BuildAgentManager buildAgentManager;
95
96
97
98
99 private BuildAgentBuildExecutorManager buildAgentBuildExecutorManager;
100
101 public void executeTask( Task task )
102 throws TaskExecutionException
103 {
104 BuildProjectTask buildProjectTask = (BuildProjectTask) task;
105
106 int projectId = buildProjectTask.getProjectId();
107
108 try
109 {
110 log.info( "Initializing build (projectId={})", projectId );
111 BuildContext context = buildContextManager.getBuildContext( projectId );
112 initializeBuildContext( context );
113
114 if ( !checkScmResult( context ) )
115 {
116 return;
117 }
118
119 log.info( "Checking if project '{}' should build", context.getProjectName() );
120 if ( !shouldBuild( context ) )
121 {
122 return;
123 }
124
125 log.info( "Starting build of {}", context.getProjectName() );
126 startBuild( context );
127
128 try
129 {
130 try
131 {
132 performAction( "update-project-from-agent-working-directory", context );
133 }
134 catch ( TaskExecutionException e )
135 {
136 updateBuildResult( context, ContinuumBuildAgentUtil.throwableToString( e ) );
137
138
139 log.error( "Error executing action update-project-from-agent-working-directory '", e );
140 }
141
142 performAction( "execute-agent-builder", context );
143
144 log.info( "Updating build result of project '{}'", context.getProjectName() );
145 updateBuildResult( context, null );
146 }
147 finally
148 {
149 log.info( "End build of project '{}'", context.getProjectName() );
150 endBuild( context );
151 }
152 }
153 catch ( TaskExecutionException e )
154 {
155 log.error( "Error while trying to build the project {}: {}", projectId, e.getMessage() );
156 }
157 }
158
159 private void initializeBuildContext( BuildContext buildContext )
160 throws TaskExecutionException
161 {
162 Map<String, Object> actionContext = new HashMap<String, Object>();
163
164 actionContext.put( ContinuumBuildAgentUtil.KEY_PROJECT_ID, buildContext.getProjectId() );
165
166 Project project = BuildContextToProject.getProject( buildContext );
167 ProjectGroup projectGroup = new ProjectGroup();
168 projectGroup.setId( buildContext.getProjectGroupId() );
169 projectGroup.setName( buildContext.getProjectGroupName() );
170 project.setProjectGroup( projectGroup );
171
172 actionContext.put( ContinuumBuildAgentUtil.KEY_PROJECT, project );
173 actionContext.put( ContinuumBuildAgentUtil.KEY_BUILD_DEFINITION,
174 BuildContextToBuildDefinition.getBuildDefinition( buildContext ) );
175 actionContext.put( ContinuumBuildAgentUtil.KEY_BUILD_DEFINITION_ID, buildContext.getBuildDefinitionId() );
176 actionContext.put( ContinuumBuildAgentUtil.KEY_TRIGGER, buildContext.getTrigger() );
177 actionContext.put( ContinuumBuildAgentUtil.KEY_USERNAME, buildContext.getUsername() );
178 actionContext.put( ContinuumBuildAgentUtil.KEY_ENVIRONMENTS, getEnvironments(
179 buildContext.getBuildDefinitionId(), getInstallationType( buildContext ) ) );
180
181
182 if ( buildContext.getLocalRepository() != null )
183 {
184 List<LocalRepository> localRepos = buildAgentConfigurationService.getLocalRepositories();
185 for ( LocalRepository local : localRepos )
186 {
187 if ( local.getName().equalsIgnoreCase( buildContext.getLocalRepository() ) )
188 {
189 actionContext.put( ContinuumBuildAgentUtil.KEY_LOCAL_REPOSITORY, local.getLocation() );
190 break;
191 }
192 }
193 }
194
195 actionContext.put( ContinuumBuildAgentUtil.KEY_SCM_RESULT, buildContext.getScmResult() );
196 buildContext.setActionContext( actionContext );
197
198 buildContext.setBuildStartTime( System.currentTimeMillis() );
199 }
200
201 private boolean checkScmResult( BuildContext buildContext )
202 {
203 if ( buildContext.getScmResult() == null )
204 {
205 log.info( "Error updating from SCM, SCM result is null, not building" );
206 return false;
207 }
208 else if ( !buildContext.getScmResult().isSuccess() )
209 {
210 log.info( "Error updating from SCM, SCM result has errors, not building" );
211 return false;
212 }
213 return true;
214 }
215
216 private void startBuild( BuildContext buildContext )
217 throws TaskExecutionException
218 {
219 try
220 {
221 buildAgentManager.startProjectBuild( buildContext.getProjectId() );
222 }
223 catch ( ContinuumException e )
224 {
225
226 log.error( "Failed to start project '" + buildContext.getProjectName() + "'", e );
227 throw new TaskExecutionException( "Failed to start project '" + buildContext.getProjectName() + "'", e );
228 }
229 }
230
231 private void endBuild( BuildContext buildContext )
232 throws TaskExecutionException
233 {
234
235 BuildResult buildResult = buildContext.getBuildResult();
236
237 Map<String, Object> result = new HashMap<String, Object>();
238 result.put( ContinuumBuildAgentUtil.KEY_PROJECT_ID, buildContext.getProjectId() );
239 result.put( ContinuumBuildAgentUtil.KEY_BUILD_DEFINITION_ID, buildContext.getBuildDefinitionId() );
240 result.put( ContinuumBuildAgentUtil.KEY_TRIGGER, buildContext.getTrigger() );
241 result.put( ContinuumBuildAgentUtil.KEY_USERNAME, buildContext.getUsername() );
242 result.put( ContinuumBuildAgentUtil.KEY_BUILD_STATE, buildResult.getState() );
243 result.put( ContinuumBuildAgentUtil.KEY_START_TIME, Long.toString( buildResult.getStartTime() ) );
244 result.put( ContinuumBuildAgentUtil.KEY_END_TIME, Long.toString( buildResult.getEndTime() ) );
245 result.put( ContinuumBuildAgentUtil.KEY_BUILD_EXIT_CODE, buildResult.getExitCode() );
246 if ( buildContext.getLatestUpdateDate() != null )
247 {
248 result.put( ContinuumBuildAgentUtil.KEY_LATEST_UPDATE_DATE, buildContext.getLatestUpdateDate() );
249 }
250
251 String buildOutput = getBuildOutputText( buildContext.getProjectId() );
252 if ( buildOutput == null )
253 {
254 result.put( ContinuumBuildAgentUtil.KEY_BUILD_OUTPUT, "" );
255 }
256 else
257 {
258 result.put( ContinuumBuildAgentUtil.KEY_BUILD_OUTPUT, buildOutput );
259 }
260
261 if ( buildResult.getError() != null )
262 {
263 result.put( ContinuumBuildAgentUtil.KEY_BUILD_ERROR, buildResult.getError() );
264 }
265 else
266 {
267 result.put( ContinuumBuildAgentUtil.KEY_BUILD_ERROR, "" );
268 }
269
270 result.put( ContinuumBuildAgentUtil.KEY_SCM_RESULT, ContinuumBuildAgentUtil.createScmResult( buildContext ) );
271 result.put( ContinuumBuildAgentUtil.KEY_BUILD_AGENT_URL, buildContext.getBuildAgentUrl() );
272
273 try
274 {
275 buildAgentManager.returnBuildResult( result );
276 buildContextManager.removeBuildContext( buildContext.getProjectId() );
277 }
278 catch ( ContinuumException e )
279 {
280 log.error( "Failed to return build result for project '" + buildContext.getProjectName() + "'", e );
281 throw new TaskExecutionException(
282 "Failed to return build result for project '" + buildContext.getProjectName() + "'", e );
283 }
284 }
285
286 private void performAction( String actionName, BuildContext context )
287 throws TaskExecutionException
288 {
289 String error;
290 TaskExecutionException exception;
291
292 try
293 {
294 log.info( "Performing action " + actionName );
295 actionManager.lookup( actionName ).execute( context.getActionContext() );
296 return;
297 }
298 catch ( ActionNotFoundException e )
299 {
300 error = ContinuumBuildAgentUtil.throwableToString( e );
301 exception = new TaskExecutionException( "Error looking up action '" + actionName + "'", e );
302 }
303 catch ( ScmRepositoryException e )
304 {
305 error = getValidationMessages( e ) + "\n" + ContinuumBuildAgentUtil.throwableToString( e );
306
307 exception = new TaskExecutionException( "SCM error while executing '" + actionName + "'", e );
308 }
309 catch ( ScmException e )
310 {
311 error = ContinuumBuildAgentUtil.throwableToString( e );
312
313 exception = new TaskExecutionException( "SCM error while executing '" + actionName + "'", e );
314 }
315 catch ( Exception e )
316 {
317 exception = new TaskExecutionException( "Error executing action '" + actionName + "'", e );
318 error = ContinuumBuildAgentUtil.throwableToString( exception );
319 }
320
321 updateBuildResult( context, error );
322
323 throw exception;
324 }
325
326 private void updateBuildResult( BuildContext context, String error )
327 {
328 context.setBuildResult( ContinuumBuildAgentUtil.getBuildResult( context.getActionContext(), null ) );
329
330 if ( context.getBuildResult() == null )
331 {
332 BuildResult build = new BuildResult();
333
334 build.setState( ContinuumProjectState.ERROR );
335
336 build.setTrigger( context.getTrigger() );
337
338 build.setUsername( context.getUsername() );
339
340 build.setStartTime( context.getBuildStartTime() );
341
342 build.setEndTime( System.currentTimeMillis() );
343
344 build.setBuildDefinition( BuildContextToBuildDefinition.getBuildDefinition( context ) );
345
346 build.setScmResult( context.getScmResult() );
347
348 if ( error != null )
349 {
350 build.setError( error );
351 }
352
353 context.setBuildResult( build );
354 }
355 }
356
357 private String getValidationMessages( ScmRepositoryException ex )
358 {
359 List<String> messages = ex.getValidationMessages();
360
361 StringBuffer message = new StringBuffer();
362
363 if ( messages != null && !messages.isEmpty() )
364 {
365 for ( Iterator<String> i = messages.iterator(); i.hasNext(); )
366 {
367 message.append( i.next() );
368
369 if ( i.hasNext() )
370 {
371 message.append( System.getProperty( "line.separator" ) );
372 }
373 }
374 }
375 return message.toString();
376 }
377
378 private String getBuildOutputText( int projectId )
379 {
380 try
381 {
382 File buildOutputFile = buildAgentConfigurationService.getBuildOutputFile( projectId );
383
384 if ( buildOutputFile.exists() )
385 {
386 return StringEscapeUtils.escapeHtml( FileUtils.fileRead( buildOutputFile ) );
387 }
388 }
389 catch ( Exception e )
390 {
391
392 log.error( "Error retrieving build output file", e );
393 }
394
395 return null;
396 }
397
398 private Map<String, String> getEnvironments( int buildDefinitionId, String installationType )
399 throws TaskExecutionException
400 {
401 try
402 {
403
404 Map<String, String> environments = buildAgentManager.getEnvironments( buildDefinitionId, installationType );
405
406 List<Installation> installations = buildAgentConfigurationService.getAvailableInstallations();
407
408 if ( installations != null )
409 {
410
411 for ( Installation installation : installations )
412 {
413
414 environments.put( installation.getVarName(), installation.getVarValue() );
415 }
416 }
417
418 return environments;
419 }
420 catch ( ContinuumException e )
421 {
422 log.error( "Error while retrieving environments of build definition: " + buildDefinitionId, e );
423 throw new TaskExecutionException(
424 "Error while retrieving environments of build definition: " + buildDefinitionId, e );
425 }
426 }
427
428 private String getInstallationType( BuildContext buildContext )
429 {
430 String executorId = buildContext.getExecutorId();
431
432 if ( ContinuumBuildExecutorConstants.MAVEN_TWO_BUILD_EXECUTOR.equals( executorId ) )
433 {
434 return BuildAgentInstallationService.MAVEN2_TYPE;
435 }
436 else if ( ContinuumBuildExecutorConstants.MAVEN_ONE_BUILD_EXECUTOR.equals( executorId ) )
437 {
438 return BuildAgentInstallationService.MAVEN1_TYPE;
439 }
440 else if ( ContinuumBuildExecutorConstants.ANT_BUILD_EXECUTOR.equals( executorId ) )
441 {
442 return BuildAgentInstallationService.ANT_TYPE;
443 }
444
445 return "";
446 }
447
448 private boolean shouldBuild( BuildContext context )
449 throws TaskExecutionException
450 {
451 Map<String, Object> map = new HashMap<String, Object>();
452 map.put( ContinuumBuildAgentUtil.KEY_PROJECT_ID, context.getProjectId() );
453 map.put( ContinuumBuildAgentUtil.KEY_BUILD_DEFINITION_ID, context.getBuildDefinitionId() );
454 map.put( ContinuumBuildAgentUtil.KEY_TRIGGER, context.getTrigger() );
455 map.put( ContinuumBuildAgentUtil.KEY_USERNAME, context.getUsername() );
456 map.put( ContinuumBuildAgentUtil.KEY_SCM_CHANGES, getScmChanges( context.getScmResult() ) );
457 map.put( ContinuumBuildAgentUtil.KEY_BUILD_AGENT_URL, context.getBuildAgentUrl() );
458
459 if ( context.getExecutorId().equals( ContinuumBuildExecutorConstants.MAVEN_TWO_BUILD_EXECUTOR ) )
460 {
461 map.put( ContinuumBuildAgentUtil.KEY_MAVEN_PROJECT, getMavenProject( context ) );
462 }
463
464 if ( context.getLatestUpdateDate() != null )
465 {
466 map.put( ContinuumBuildAgentUtil.KEY_LATEST_UPDATE_DATE, context.getLatestUpdateDate() );
467 }
468
469 try
470 {
471 return buildAgentManager.shouldBuild( map );
472 }
473 catch ( ContinuumException e )
474 {
475 log.error( "Failed to determine if project should build", e );
476 throw new TaskExecutionException( "Failed to determine if project should build", e );
477 }
478 }
479
480 private List<Map<String, Object>> getScmChanges( ScmResult scmResult )
481 {
482 List<Map<String, Object>> scmChanges = new ArrayList<Map<String, Object>>();
483
484 if ( scmResult != null && scmResult.getChanges() != null )
485 {
486 for ( Object obj : scmResult.getChanges() )
487 {
488 ChangeSet changeSet = (ChangeSet) obj;
489
490 Map<String, Object> map = new HashMap<String, Object>();
491 if ( StringUtils.isNotEmpty( changeSet.getAuthor() ) )
492 {
493 map.put( ContinuumBuildAgentUtil.KEY_CHANGESET_AUTHOR, changeSet.getAuthor() );
494 }
495 else
496 {
497 map.put( ContinuumBuildAgentUtil.KEY_CHANGESET_AUTHOR, "" );
498 }
499 if ( StringUtils.isNotEmpty( changeSet.getComment() ) )
500 {
501 map.put( ContinuumBuildAgentUtil.KEY_CHANGESET_COMMENT, changeSet.getComment() );
502 }
503 else
504 {
505 map.put( ContinuumBuildAgentUtil.KEY_CHANGESET_COMMENT, "" );
506 }
507 if ( changeSet.getDateAsDate() != null )
508 {
509 map.put( ContinuumBuildAgentUtil.KEY_CHANGESET_DATE, changeSet.getDateAsDate() );
510 }
511 map.put( ContinuumBuildAgentUtil.KEY_CHANGESET_FILES, getScmChangeFiles( changeSet.getFiles() ) );
512 scmChanges.add( map );
513 }
514 }
515
516 return scmChanges;
517 }
518
519 private List<Map<String, String>> getScmChangeFiles( List<ChangeFile> files )
520 {
521 List<Map<String, String>> scmChangeFiles = new ArrayList<Map<String, String>>();
522
523 if ( files != null )
524 {
525 for ( ChangeFile changeFile : files )
526 {
527 Map<String, String> map = new HashMap<String, String>();
528
529 if ( StringUtils.isNotEmpty( changeFile.getName() ) )
530 {
531 map.put( ContinuumBuildAgentUtil.KEY_CHANGEFILE_NAME, changeFile.getName() );
532 }
533 else
534 {
535 map.put( ContinuumBuildAgentUtil.KEY_CHANGEFILE_NAME, "" );
536 }
537 if ( StringUtils.isNotEmpty( changeFile.getRevision() ) )
538 {
539 map.put( ContinuumBuildAgentUtil.KEY_CHANGEFILE_REVISION, changeFile.getRevision() );
540 }
541 else
542 {
543 map.put( ContinuumBuildAgentUtil.KEY_CHANGEFILE_REVISION, "" );
544 }
545 if ( StringUtils.isNotEmpty( changeFile.getStatus() ) )
546 {
547 map.put( ContinuumBuildAgentUtil.KEY_CHANGEFILE_STATUS, changeFile.getStatus() );
548 }
549 else
550 {
551 map.put( ContinuumBuildAgentUtil.KEY_CHANGEFILE_STATUS, "" );
552 }
553 scmChangeFiles.add( map );
554 }
555 }
556 return scmChangeFiles;
557 }
558
559 private Map getMavenProject( BuildContext context )
560 throws TaskExecutionException
561 {
562 Map<String, Object> mavenProject = new HashMap<String, Object>();
563
564 try
565 {
566 ContinuumAgentBuildExecutor buildExecutor = buildAgentBuildExecutorManager.getBuildExecutor(
567 context.getExecutorId() );
568
569 BuildDefinition buildDefinition = BuildContextToBuildDefinition.getBuildDefinition( context );
570
571 File workingDirectory = buildAgentConfigurationService.getWorkingDirectory( context.getProjectId() );
572
573 MavenProject project = buildExecutor.getMavenProject( workingDirectory, buildDefinition );
574
575 mavenProject.put( ContinuumBuildAgentUtil.KEY_PROJECT_VERSION, project.getVersion() );
576
577 if ( project.getModules() != null )
578 {
579 mavenProject.put( ContinuumBuildAgentUtil.KEY_PROJECT_MODULES, project.getModules() );
580 }
581 }
582 catch ( ContinuumAgentBuildExecutorException e )
583 {
584 log.error( "Error getting maven project", e );
585 }
586 catch ( ContinuumException e )
587 {
588 log.error( "Error getting build executor", e );
589 }
590
591 return mavenProject;
592 }
593
594 public void setBuildContextManager( BuildContextManager buildContextManager )
595 {
596 this.buildContextManager = buildContextManager;
597 }
598
599 public void setActionManager( ActionManager actionManager )
600 {
601 this.actionManager = actionManager;
602 }
603
604 public void setBuildAgentConfigurationService( BuildAgentConfigurationService buildAgentConfigurationService )
605 {
606 this.buildAgentConfigurationService = buildAgentConfigurationService;
607 }
608
609 public void setBuildAgentManager( BuildAgentManager buildAgentManager )
610 {
611 this.buildAgentManager = buildAgentManager;
612 }
613
614 public void setBuildAgentBuildExecutorManager( BuildAgentBuildExecutorManager buildAgentBuildExecutorManager )
615 {
616 this.buildAgentBuildExecutorManager = buildAgentBuildExecutorManager;
617 }
618 }