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 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   * @plexus.component role="org.codehaus.plexus.taskqueue.execution.TaskExecutor"
67   * role-hint="build-agent"
68   */
69  public class BuildProjectTaskExecutor
70      implements TaskExecutor
71  {
72      private static final Logger log = LoggerFactory.getLogger( BuildProjectTaskExecutor.class );
73  
74      /**
75       * @plexus.requirement
76       */
77      private BuildContextManager buildContextManager;
78  
79      /**
80       * @plexus.requirement
81       */
82      private ActionManager actionManager;
83  
84      /**
85       * @plexus.requirement
86       */
87      private BuildAgentConfigurationService buildAgentConfigurationService;
88  
89      /**
90       * @plexus.requirement
91       */
92      private BuildAgentManager buildAgentManager;
93  
94      /**
95       * @plexus.requirement
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                 //just log the error but don't stop the build from progressing in order not to suppress any build result messages there
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             // do not throw exception, just log?
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         // return build result to master
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             // do not throw exception, just log it
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 }