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.continuum.buildagent.buildcontext.BuildContext;
23  import org.apache.continuum.buildagent.configuration.BuildAgentConfigurationService;
24  import org.apache.continuum.buildagent.manager.BuildAgentManager;
25  import org.apache.continuum.buildagent.taskqueue.PrepareBuildProjectsTask;
26  import org.apache.continuum.buildagent.utils.BuildContextToBuildDefinition;
27  import org.apache.continuum.buildagent.utils.BuildContextToProject;
28  import org.apache.continuum.buildagent.utils.ContinuumBuildAgentUtil;
29  import org.apache.maven.continuum.ContinuumException;
30  import org.apache.maven.continuum.model.project.BuildDefinition;
31  import org.apache.maven.continuum.model.project.Project;
32  import org.apache.maven.continuum.model.scm.ChangeSet;
33  import org.apache.maven.continuum.model.scm.ScmResult;
34  import org.apache.maven.continuum.project.ContinuumProjectState;
35  import org.codehaus.plexus.action.ActionManager;
36  import org.codehaus.plexus.action.ActionNotFoundException;
37  import org.codehaus.plexus.taskqueue.Task;
38  import org.codehaus.plexus.taskqueue.execution.TaskExecutionException;
39  import org.codehaus.plexus.taskqueue.execution.TaskExecutor;
40  import org.codehaus.plexus.util.StringUtils;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  import java.util.Date;
45  import java.util.HashMap;
46  import java.util.List;
47  import java.util.Map;
48  
49  /**
50   * @plexus.component role="org.codehaus.plexus.taskqueue.execution.TaskExecutor"
51   * role-hint="prepare-build-agent"
52   */
53  public class PrepareBuildProjectsTaskExecutor
54      implements TaskExecutor
55  {
56      private static final Logger log = LoggerFactory.getLogger( PrepareBuildProjectsTaskExecutor.class );
57  
58      /**
59       * @plexus.requirement
60       */
61      private ActionManager actionManager;
62  
63      /**
64       * @plexus.requirement
65       */
66      private BuildAgentConfigurationService buildAgentConfigurationService;
67  
68      /**
69       * @plexus.requirement
70       */
71      private BuildAgentManager buildAgentManager;
72  
73      public void executeTask( Task task )
74          throws TaskExecutionException
75      {
76          List<BuildContext> buildContexts = ( (PrepareBuildProjectsTask) task ).getBuildContexts();
77  
78          Map<String, Object> context = null;
79  
80          try
81          {
82              if ( buildContexts != null && buildContexts.size() > 0 )
83              {
84                  try
85                  {
86                      for ( BuildContext buildContext : buildContexts )
87                      {
88                          BuildDefinition buildDef = BuildContextToBuildDefinition.getBuildDefinition( buildContext );
89  
90                          log.debug( "Check scm root state of project group '{}'", buildContext.getProjectGroupName() );
91                          if ( !checkProjectScmRoot( context ) )
92                          {
93                              break;
94                          }
95  
96                          log.info( "Starting prepare build of project group '{}'", buildContext.getProjectGroupName() );
97                          startPrepareBuild( buildContext );
98  
99                          log.info( "Initializing prepare build" );
100                         initializeActionContext( buildContext );
101 
102                         try
103                         {
104                             if ( buildDef.isBuildFresh() )
105                             {
106                                 log.info( "Clean up working directory of project '{}'", buildContext.getProjectName() );
107                                 cleanWorkingDirectory( buildContext );
108                             }
109 
110                             log.info( "Updating working directory of project '{}'", buildContext.getProjectName() );
111                             updateWorkingDirectory( buildContext );
112 
113                             //CONTINUUM-1393
114                             if ( !buildDef.isBuildFresh() )
115                             {
116                                 log.info( "Merging SCM results of project '{}'", buildContext.getProjectName() );
117                                 mergeScmResults( buildContext );
118                             }
119                         }
120                         finally
121                         {
122                             endProjectPrepareBuild( buildContext );
123                             context = buildContext.getActionContext();
124                         }
125                     }
126                 }
127                 finally
128                 {
129                     endPrepareBuild( context );
130                 }
131 
132                 if ( checkProjectScmRoot( context ) )
133                 {
134                     log.debug( "Successful prepare build. Creating build task" );
135                     buildProjects( buildContexts );
136                 }
137             }
138             else
139             {
140                 throw new TaskExecutionException( "No project build context" );
141             }
142         }
143         catch ( TaskExecutionException e )
144         {
145             log.error( "Error while preparing build of project: {}", e.getMessage() );
146         }
147     }
148 
149     private void startPrepareBuild( BuildContext buildContext )
150         throws TaskExecutionException
151     {
152         Map<String, Object> actionContext = buildContext.getActionContext();
153 
154         if ( actionContext == null || !( ContinuumBuildAgentUtil.getScmRootState( actionContext ) ==
155             ContinuumProjectState.UPDATING ) )
156         {
157             Map<String, Object> map = new HashMap<String, Object>();
158             map.put( ContinuumBuildAgentUtil.KEY_PROJECT_GROUP_ID, buildContext.getProjectGroupId() );
159             map.put( ContinuumBuildAgentUtil.KEY_SCM_ROOT_ADDRESS, buildContext.getScmRootAddress() );
160             map.put( ContinuumBuildAgentUtil.KEY_BUILD_AGENT_URL, buildContext.getBuildAgentUrl() );
161 
162             try
163             {
164                 buildAgentManager.startPrepareBuild( map );
165             }
166             catch ( ContinuumException e )
167             {
168                 throw new TaskExecutionException( e.getMessage(), e );
169             }
170         }
171     }
172 
173     private void initializeActionContext( BuildContext buildContext )
174     {
175         Map<String, Object> actionContext = new HashMap<String, Object>();
176 
177         actionContext.put( ContinuumBuildAgentUtil.KEY_PROJECT_ID, buildContext.getProjectId() );
178         actionContext.put( ContinuumBuildAgentUtil.KEY_PROJECT, BuildContextToProject.getProject( buildContext ) );
179         actionContext.put( ContinuumBuildAgentUtil.KEY_BUILD_DEFINITION,
180                            BuildContextToBuildDefinition.getBuildDefinition( buildContext ) );
181         actionContext.put( ContinuumBuildAgentUtil.KEY_SCM_ROOT_STATE, ContinuumProjectState.UPDATING );
182         actionContext.put( ContinuumBuildAgentUtil.KEY_PROJECT_GROUP_ID, buildContext.getProjectGroupId() );
183         actionContext.put( ContinuumBuildAgentUtil.KEY_SCM_ROOT_ADDRESS, buildContext.getScmRootAddress() );
184         actionContext.put( ContinuumBuildAgentUtil.KEY_OLD_SCM_RESULT, buildContext.getOldScmResult() );
185         actionContext.put( ContinuumBuildAgentUtil.KEY_LATEST_UPDATE_DATE, buildContext.getLatestUpdateDate() );
186         actionContext.put( ContinuumBuildAgentUtil.KEY_TRIGGER, buildContext.getTrigger() );
187         actionContext.put( ContinuumBuildAgentUtil.KEY_USERNAME, buildContext.getUsername() );
188         actionContext.put( ContinuumBuildAgentUtil.KEY_SCM_USERNAME, buildContext.getScmUsername() );
189         actionContext.put( ContinuumBuildAgentUtil.KEY_SCM_PASSWORD, buildContext.getScmPassword() );
190         actionContext.put( ContinuumBuildAgentUtil.KEY_BUILD_AGENT_URL, buildContext.getBuildAgentUrl() );
191 
192         buildContext.setActionContext( actionContext );
193     }
194 
195     private boolean checkProjectScmRoot( Map<String, Object> context )
196     {
197         return !( context != null && ContinuumBuildAgentUtil.getScmRootState( context ) ==
198             ContinuumProjectState.ERROR );
199 
200     }
201 
202     private void cleanWorkingDirectory( BuildContext buildContext )
203         throws TaskExecutionException
204     {
205         performAction( "clean-agent-working-directory", buildContext );
206     }
207 
208     private void updateWorkingDirectory( BuildContext buildContext )
209         throws TaskExecutionException
210     {
211         Map<String, Object> actionContext = buildContext.getActionContext();
212 
213         performAction( "check-agent-working-directory", buildContext );
214 
215         boolean workingDirectoryExists = ContinuumBuildAgentUtil.getBoolean( actionContext,
216                                                                              ContinuumBuildAgentUtil.KEY_WORKING_DIRECTORY_EXISTS );
217 
218         ScmResult scmResult;
219 
220         Date date;
221 
222         if ( workingDirectoryExists )
223         {
224             performAction( "update-agent-working-directory", buildContext );
225 
226             scmResult = ContinuumBuildAgentUtil.getUpdateScmResult( actionContext, null );
227 
228             date = ContinuumBuildAgentUtil.getLatestUpdateDate( actionContext );
229         }
230         else
231         {
232             Project project = ContinuumBuildAgentUtil.getProject( actionContext );
233 
234             actionContext.put( ContinuumBuildAgentUtil.KEY_WORKING_DIRECTORY,
235                                buildAgentConfigurationService.getWorkingDirectory(
236                                    project.getId() ).getAbsolutePath() );
237 
238             performAction( "checkout-agent-project", buildContext );
239 
240             scmResult = ContinuumBuildAgentUtil.getCheckoutScmResult( actionContext, null );
241 
242             performAction( "changelog-agent-project", buildContext );
243 
244             date = ContinuumBuildAgentUtil.getLatestUpdateDate( actionContext );
245         }
246 
247         buildContext.setScmResult( scmResult );
248         buildContext.setLatestUpdateDate( date );
249         actionContext.put( ContinuumBuildAgentUtil.KEY_SCM_RESULT, scmResult );
250     }
251 
252     private void endProjectPrepareBuild( BuildContext buildContext )
253         throws TaskExecutionException
254     {
255         Map<String, Object> context = buildContext.getActionContext();
256 
257         ScmResult scmResult = ContinuumBuildAgentUtil.getScmResult( context, null );
258 
259         log.debug( "End prepare build of project '{}'", buildContext.getProjectName() );
260 
261         if ( scmResult == null || !scmResult.isSuccess() )
262         {
263             context.put( ContinuumBuildAgentUtil.KEY_SCM_ROOT_STATE, ContinuumProjectState.ERROR );
264         }
265         else
266         {
267             buildContext.setScmResult( scmResult );
268         }
269     }
270 
271     private void endPrepareBuild( Map<String, Object> context )
272         throws TaskExecutionException
273     {
274         if ( context != null )
275         {
276             Map<String, Object> result = new HashMap<String, Object>();
277             result.put( ContinuumBuildAgentUtil.KEY_PROJECT_GROUP_ID, ContinuumBuildAgentUtil.getProjectGroupId(
278                 context ) );
279             result.put( ContinuumBuildAgentUtil.KEY_SCM_ROOT_ADDRESS, ContinuumBuildAgentUtil.getScmRootAddress(
280                 context ) );
281             result.put( ContinuumBuildAgentUtil.KEY_SCM_ROOT_STATE, ContinuumBuildAgentUtil.getScmRootState(
282                 context ) );
283             result.put( ContinuumBuildAgentUtil.KEY_BUILD_AGENT_URL, ContinuumBuildAgentUtil.getBuildAgentUrl(
284                 context ) );
285 
286             if ( ContinuumBuildAgentUtil.getScmRootState( context ) == ContinuumProjectState.ERROR )
287             {
288                 String error = convertScmResultToError( ContinuumBuildAgentUtil.getScmResult( context, null ) );
289 
290                 if ( StringUtils.isEmpty( error ) )
291                 {
292                     result.put( ContinuumBuildAgentUtil.KEY_SCM_ERROR, "" );
293                 }
294                 else
295                 {
296                     result.put( ContinuumBuildAgentUtil.KEY_SCM_ERROR, error );
297                 }
298             }
299             else
300             {
301                 result.put( ContinuumBuildAgentUtil.KEY_SCM_ERROR, "" );
302             }
303 
304             try
305             {
306                 log.debug( "End prepare build of project group '{}'", ContinuumBuildAgentUtil.getProjectGroupId(
307                     context ) );
308                 buildAgentManager.endPrepareBuild( result );
309             }
310             catch ( ContinuumException e )
311             {
312                 throw new TaskExecutionException( e.getMessage(), e );
313             }
314         }
315         else
316         {
317             throw new TaskExecutionException( "No project build context" );
318         }
319     }
320 
321     private String convertScmResultToError( ScmResult result )
322     {
323         String error = "";
324 
325         if ( result == null )
326         {
327             error = "Scm result is null.";
328         }
329         else
330         {
331             if ( result.getCommandLine() != null )
332             {
333                 error = "Command line: " + StringUtils.clean( result.getCommandLine() ) +
334                     System.getProperty( "line.separator" );
335             }
336 
337             if ( result.getProviderMessage() != null )
338             {
339                 error = "Provider message: " + StringUtils.clean( result.getProviderMessage() ) +
340                     System.getProperty( "line.separator" );
341             }
342 
343             if ( result.getCommandOutput() != null )
344             {
345                 error += "Command output: " + System.getProperty( "line.separator" );
346                 error += "-------------------------------------------------------------------------------" +
347                     System.getProperty( "line.separator" );
348                 error += StringUtils.clean( result.getCommandOutput() ) + System.getProperty( "line.separator" );
349                 error += "-------------------------------------------------------------------------------" +
350                     System.getProperty( "line.separator" );
351             }
352 
353             if ( result.getException() != null )
354             {
355                 error += "Exception:" + System.getProperty( "line.separator" );
356                 error += result.getException();
357             }
358         }
359 
360         return error;
361     }
362 
363     private void performAction( String actionName, BuildContext buildContext )
364         throws TaskExecutionException
365     {
366         TaskExecutionException exception;
367 
368         try
369         {
370             log.info( "Performing action " + actionName );
371             actionManager.lookup( actionName ).execute( buildContext.getActionContext() );
372             return;
373         }
374         catch ( ActionNotFoundException e )
375         {
376             exception = new TaskExecutionException( "Error looking up action '" + actionName + "'", e );
377         }
378         catch ( Exception e )
379         {
380             exception = new TaskExecutionException( "Error executing action '" + actionName + "'", e );
381         }
382 
383         ScmResult result = new ScmResult();
384 
385         result.setSuccess( false );
386 
387         result.setException( ContinuumBuildAgentUtil.throwableToString( exception ) );
388 
389         buildContext.setScmResult( result );
390         buildContext.getActionContext().put( ContinuumBuildAgentUtil.KEY_UPDATE_SCM_RESULT, result );
391 
392         throw exception;
393     }
394 
395     private void mergeScmResults( BuildContext buildContext )
396     {
397         Map<String, Object> context = buildContext.getActionContext();
398         ScmResult oldScmResult = ContinuumBuildAgentUtil.getOldScmResult( context, null );
399         ScmResult newScmResult = ContinuumBuildAgentUtil.getScmResult( context, null );
400 
401         if ( oldScmResult != null )
402         {
403             if ( newScmResult == null )
404             {
405                 context.put( ContinuumBuildAgentUtil.KEY_SCM_RESULT, oldScmResult );
406             }
407             else
408             {
409                 List<ChangeSet> oldChanges = oldScmResult.getChanges();
410 
411                 List<ChangeSet> newChanges = newScmResult.getChanges();
412 
413                 for ( ChangeSet change : newChanges )
414                 {
415                     if ( !oldChanges.contains( change ) )
416                     {
417                         oldChanges.add( change );
418                     }
419                 }
420 
421                 newScmResult.setChanges( oldChanges );
422             }
423         }
424     }
425 
426     private void buildProjects( List<BuildContext> buildContexts )
427         throws TaskExecutionException
428     {
429         Map<String, Object> map = new HashMap<String, Object>();
430         map.put( ContinuumBuildAgentUtil.KEY_BUILD_CONTEXTS, buildContexts );
431 
432         BuildContext context = new BuildContext();
433         context.setActionContext( map );
434 
435         performAction( "create-agent-build-project-task", context );
436     }
437 }