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