View Javadoc

1   package org.apache.continuum.buildagent.taskqueue.execution;
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.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   * @plexus.component role="org.codehaus.plexus.taskqueue.execution.TaskExecutor"
69   * role-hint="build-agent"
70   */
71  public class BuildProjectTaskExecutor
72      implements TaskExecutor
73  {
74      private static final Logger log = LoggerFactory.getLogger( BuildProjectTaskExecutor.class );
75  
76      /**
77       * @plexus.requirement
78       */
79      private BuildContextManager buildContextManager;
80  
81      /**
82       * @plexus.requirement
83       */
84      private ActionManager actionManager;
85  
86      /**
87       * @plexus.requirement
88       */
89      private BuildAgentConfigurationService buildAgentConfigurationService;
90  
91      /**
92       * @plexus.requirement
93       */
94      private BuildAgentManager buildAgentManager;
95  
96      /**
97       * @plexus.requirement
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                     //just log the error but don't stop the build from progressing in order not to suppress any build result messages there
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         // CONTINUUM-2391        
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             // do not throw exception, just log?
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         // return build result to master
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             // do not throw exception, just log it
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             // get environments from Master (Continuum)
404             Map<String, String> environments = buildAgentManager.getEnvironments( buildDefinitionId, installationType );
405 
406             List<Installation> installations = buildAgentConfigurationService.getAvailableInstallations();
407 
408             if ( installations != null )
409             {
410                 // get environments from Slave (Build Agent)
411                 for ( Installation installation : installations )
412                 {
413                     // combine environments (Master and Slave); Slave's environments overwrite Master's environments
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 }