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