View Javadoc

1   package org.apache.continuum.buildagent.build.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.Collections;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Properties;
29  
30  import org.apache.continuum.buildagent.configuration.BuildAgentConfigurationService;
31  import org.apache.continuum.buildagent.installation.BuildAgentInstallationService;
32  import org.apache.continuum.buildagent.manager.BuildAgentManager;
33  import org.apache.continuum.buildagent.utils.ContinuumBuildAgentUtil;
34  import org.apache.continuum.utils.shell.ExecutionResult;
35  import org.apache.continuum.utils.shell.ShellCommandHelper;
36  import org.apache.maven.artifact.Artifact;
37  import org.apache.maven.continuum.model.project.BuildDefinition;
38  import org.apache.maven.continuum.model.project.Project;
39  import org.apache.maven.continuum.model.project.ProjectDependency;
40  import org.apache.maven.continuum.model.project.ProjectDeveloper;
41  import org.apache.maven.continuum.model.project.ProjectNotifier;
42  import org.apache.maven.continuum.project.ContinuumProjectState;
43  import org.apache.maven.project.MavenProject;
44  import org.codehaus.plexus.commandline.ExecutableResolver;
45  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
46  import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
47  import org.codehaus.plexus.util.StringUtils;
48  import org.codehaus.plexus.util.cli.CommandLineException;
49  import org.slf4j.Logger;
50  import org.slf4j.LoggerFactory;
51  
52  public abstract class AbstractBuildExecutor
53      implements ContinuumAgentBuildExecutor, Initializable
54  {
55      protected static final Logger log = LoggerFactory.getLogger( AbstractBuildExecutor.class );
56  
57      /**
58       * @plexus.requirement
59       */
60      private ShellCommandHelper shellCommandHelper;
61  
62      /**
63       * @plexus.requirement
64       */
65      private ExecutableResolver executableResolver;
66  
67      /**
68       * @plexus.requirement
69       */
70      private BuildAgentConfigurationService buildAgentConfigurationService;
71  
72      /**
73       * @plexus.requirement
74       */
75      private BuildAgentInstallationService buildAgentInstallationService;
76  
77      /**
78       * @plexus.configuration
79       */
80      private String defaultExecutable;
81  
82      /**
83       * @plexus.requirement
84       */
85      private BuildAgentManager buildAgentManager;
86  
87      // ----------------------------------------------------------------------
88      //
89      // ----------------------------------------------------------------------
90  
91      private final String id;
92  
93      private boolean resolveExecutable;
94  
95      // ----------------------------------------------------------------------
96      //
97      // ----------------------------------------------------------------------
98  
99      protected AbstractBuildExecutor( String id, boolean resolveExecutable )
100     {
101         this.id = id;
102 
103         this.resolveExecutable = resolveExecutable;
104     }
105 
106     public void setShellCommandHelper( ShellCommandHelper shellCommandHelper )
107     {
108         this.shellCommandHelper = shellCommandHelper;
109     }
110 
111     public ShellCommandHelper getShellCommandHelper()
112     {
113         return shellCommandHelper;
114     }
115 
116     public void setDefaultExecutable( String defaultExecutable )
117     {
118         this.defaultExecutable = defaultExecutable;
119     }
120 
121     public BuildAgentConfigurationService getBuildAgentConfigurationService()
122     {
123         return buildAgentConfigurationService;
124     }
125 
126     public void setBuildAgentConfigurationService( BuildAgentConfigurationService buildAgentConfigurationService )
127     {
128         this.buildAgentConfigurationService = buildAgentConfigurationService;
129     }
130 
131     public BuildAgentInstallationService getBuildAgentInstallationService()
132     {
133         return buildAgentInstallationService;
134     }
135 
136     public void setBuildAgentInstallationService( BuildAgentInstallationService buildAgentInstallationService )
137     {
138         this.buildAgentInstallationService = buildAgentInstallationService;
139     }
140 
141     public BuildAgentManager getBuildAgentManager()
142     {
143         return buildAgentManager;
144     }
145 
146     public void setBuildAgentManager( BuildAgentManager buildAgentManager )
147     {
148         this.buildAgentManager = buildAgentManager;
149     }
150 
151     // ----------------------------------------------------------------------
152     // Component Lifecycle
153     // ----------------------------------------------------------------------
154 
155     public String getDefaultExecutable()
156     {
157         return defaultExecutable;
158     }
159 
160     public void initialize()
161         throws InitializationException
162     {
163         List path = executableResolver.getDefaultPath();
164 
165         if ( resolveExecutable )
166         {
167             if ( StringUtils.isEmpty( defaultExecutable ) )
168             {
169                 log.warn( "The default executable for build executor '" + id + "' is not set. " +
170                     "This will cause a problem unless the project has a executable configured." );
171             }
172             else
173             {
174                 File resolvedExecutable = executableResolver.findExecutable( defaultExecutable, path );
175 
176                 if ( resolvedExecutable == null )
177                 {
178                     log.warn(
179                         "Could not find the executable '" + defaultExecutable + "' in the " + "path '" + path + "'." );
180                 }
181                 else
182                 {
183                     log.info( "Resolved the executable '" + defaultExecutable + "' to " + "'" +
184                         resolvedExecutable.getAbsolutePath() + "'." );
185                 }
186             }
187         }
188     }
189 
190     // ----------------------------------------------------------------------
191     //
192     // ----------------------------------------------------------------------
193 
194     /**
195      * Find the actual executable path to be used
196      *
197      * @param defaultExecutable
198      * @return The executable path
199      */
200     protected String findExecutable( String executable, String defaultExecutable, boolean resolveExecutable,
201                                      File workingDirectory )
202     {
203         // ----------------------------------------------------------------------
204         // If we're not searching the path for the executable, prefix the
205         // executable with the working directory to make sure the path is
206         // absolute and thus won't be tried resolved by using the PATH
207         // ----------------------------------------------------------------------
208 
209         String actualExecutable;
210 
211         if ( !resolveExecutable )
212         {
213             actualExecutable = new File( workingDirectory, executable ).getAbsolutePath();
214         }
215         else
216         {
217             List<String> path = executableResolver.getDefaultPath();
218 
219             if ( StringUtils.isEmpty( executable ) )
220             {
221                 executable = defaultExecutable;
222             }
223 
224             File e = executableResolver.findExecutable( executable, path );
225 
226             if ( e == null )
227             {
228                 log.debug( "Could not find the executable '" + executable + "' in this path: " );
229 
230                 for ( String element : path )
231                 {
232                     log.debug( element );
233                 }
234 
235                 actualExecutable = defaultExecutable;
236             }
237             else
238             {
239                 actualExecutable = e.getAbsolutePath();
240             }
241         }
242 
243         //sometimes executable isn't found in path but it exit (CONTINUUM-365)
244         File actualExecutableFile = new File( actualExecutable );
245 
246         if ( !actualExecutableFile.exists() )
247         {
248             actualExecutable = executable;
249         }
250 
251         return actualExecutable;
252     }
253 
254     protected ContinuumAgentBuildExecutionResult executeShellCommand( Project project, String executable,
255                                                                       String arguments, File output,
256                                                                       Map<String, String> environments )
257         throws ContinuumAgentBuildExecutorException, ContinuumAgentBuildCancelledException
258     {
259 
260         File workingDirectory = getWorkingDirectory( project.getId() );
261 
262         String actualExecutable = findExecutable( executable, defaultExecutable, resolveExecutable, workingDirectory );
263 
264         // ----------------------------------------------------------------------
265         // Execute the build
266         // ----------------------------------------------------------------------
267 
268         try
269         {
270             ExecutionResult result =
271                 getShellCommandHelper().executeShellCommand( workingDirectory, actualExecutable, arguments, output,
272                                                              project.getId(), environments );
273 
274             log.info( "Exit code: " + result.getExitCode() );
275 
276             return new ContinuumAgentBuildExecutionResult( output, result.getExitCode() );
277         }
278         catch ( CommandLineException e )
279         {
280             if ( e.getCause() instanceof InterruptedException )
281             {
282                 throw new ContinuumAgentBuildCancelledException( "The build was cancelled", e );
283             }
284             else
285             {
286                 throw new ContinuumAgentBuildExecutorException(
287                     "Error while executing shell command. The most common error is that '" + executable + "' " +
288                         "is not in your path.", e );
289             }
290         }
291         catch ( Exception e )
292         {
293             throw new ContinuumAgentBuildExecutorException(
294                 "Error while executing shell command. " + "The most common error is that '" + executable + "' " +
295                     "is not in your path.", e );
296         }
297     }
298 
299     protected Properties getContinuumSystemProperties( Project project )
300     {
301         Properties properties = new Properties();
302         properties.setProperty( "continuum.project.group.name", project.getProjectGroup().getName() );
303         properties.setProperty( "continuum.project.lastBuild.state", String.valueOf( project.getOldState() ) );
304         properties.setProperty( "continuum.project.lastBuild.number", String.valueOf( project.getBuildNumber() ) );
305         properties.setProperty( "continuum.project.nextBuild.number", String.valueOf( project.getBuildNumber() + 1 ) );
306         properties.setProperty( "continuum.project.id", String.valueOf( project.getId() ) );
307         properties.setProperty( "continuum.project.name", project.getName() );
308         properties.setProperty( "continuum.project.version", project.getVersion() );
309         return properties;
310     }
311 
312     protected String getBuildFileForProject( BuildDefinition buildDefinition )
313     {
314         return StringUtils.clean( buildDefinition.getBuildFile() );
315     }
316 
317     protected void updateProject( Project project )
318         throws ContinuumAgentBuildExecutorException
319     {
320         Map<String, Object> projectMap = new HashMap<String, Object>();
321 
322         projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_ID, project.getId() );
323         if ( StringUtils.isNotEmpty( project.getVersion() ) )
324         {
325             projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_VERSION, project.getVersion() );
326         }
327         else
328         {
329             projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_VERSION, "" );
330         }
331         if ( StringUtils.isNotEmpty( project.getArtifactId() ) )
332         {
333             projectMap.put( ContinuumBuildAgentUtil.KEY_ARTIFACT_ID, project.getArtifactId() );
334         }
335         else
336         {
337             projectMap.put( ContinuumBuildAgentUtil.KEY_ARTIFACT_ID, "" );
338         }
339         if ( StringUtils.isNotEmpty( project.getGroupId() ) )
340         {
341             projectMap.put( ContinuumBuildAgentUtil.KEY_GROUP_ID, project.getGroupId() );
342         }
343         else
344         {
345             projectMap.put( ContinuumBuildAgentUtil.KEY_GROUP_ID, "" );
346         }
347         if ( StringUtils.isNotEmpty( project.getName() ) )
348         {
349             projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_NAME, project.getName() );
350         }
351         else
352         {
353             projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_NAME, "" );
354         }
355         if ( StringUtils.isNotEmpty( project.getDescription() ) )
356         {
357             projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_DESCRIPTION, project.getDescription() );
358         }
359         else
360         {
361             projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_DESCRIPTION, "" );
362         }
363         if ( StringUtils.isNotEmpty( project.getScmUrl() ) )
364         {
365             projectMap.put( ContinuumBuildAgentUtil.KEY_SCM_URL, project.getScmUrl() );
366         }
367         else
368         {
369             projectMap.put( ContinuumBuildAgentUtil.KEY_SCM_URL, "" );
370         }
371         if ( StringUtils.isNotEmpty( project.getScmTag() ) )
372         {
373             projectMap.put( ContinuumBuildAgentUtil.KEY_SCM_TAG, project.getScmTag() );
374         }
375         else
376         {
377             projectMap.put( ContinuumBuildAgentUtil.KEY_SCM_TAG, "" );
378         }
379         if ( StringUtils.isNotEmpty( project.getUrl() ) )
380         {
381             projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_URL, project.getUrl() );
382         }
383         else
384         {
385             projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_URL, "" );
386         }
387         projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_PARENT, getProjectParent( project.getParent() ) );
388         projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEVELOPERS,
389                         getProjectDevelopers( project.getDevelopers() ) );
390         projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEPENDENCIES,
391                         getProjectDependencies( project.getDependencies() ) );
392         projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_NOTIFIERS, getProjectNotifiers( project.getNotifiers() ) );
393 
394         try
395         {
396             buildAgentManager.updateProject( projectMap );
397         }
398         catch ( Exception e )
399         {
400             throw new ContinuumAgentBuildExecutorException( "Failed to update project", e );
401         }
402     }
403 
404     protected List<Map<String, String>> getProjectDevelopers( List<ProjectDeveloper> developers )
405     {
406         List<Map<String, String>> pDevelopers = new ArrayList<Map<String, String>>();
407 
408         if ( developers != null )
409         {
410             for ( ProjectDeveloper developer : developers )
411             {
412                 Map<String, String> map = new HashMap<String, String>();
413                 map.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEVELOPER_NAME, developer.getName() );
414                 if ( StringUtils.isNotEmpty( developer.getEmail() ) )
415                 {
416                 	map.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEVELOPER_EMAIL, developer.getEmail() );
417                 }
418                 else
419                 {
420                 	map.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEVELOPER_EMAIL, "" );
421                 }
422                 if ( StringUtils.isNotEmpty( developer.getScmId() ) )
423                 {
424                 	map.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEVELOPER_SCMID, developer.getScmId() );
425                 }
426                 else
427                 {
428                     map.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEVELOPER_SCMID, "" );
429                 }
430 
431                 pDevelopers.add( map );
432             }
433         }
434         return pDevelopers;
435     }
436 
437     protected Map<String, Object> getProjectParent( ProjectDependency parent )
438     {
439         Map<String, Object> map = new HashMap<String, Object>();
440 
441         if ( parent != null )
442         {
443             if ( StringUtils.isNotEmpty( parent.getGroupId() ) )
444             {
445                 map.put( ContinuumBuildAgentUtil.KEY_GROUP_ID, parent.getGroupId() );
446             }
447             else
448             {
449                 map.put( ContinuumBuildAgentUtil.KEY_GROUP_ID, "" );
450             }
451             if ( StringUtils.isNotEmpty( parent.getArtifactId() ) )
452             {
453                 map.put( ContinuumBuildAgentUtil.KEY_ARTIFACT_ID, parent.getArtifactId() );
454             }
455             else
456             {
457                 map.put( ContinuumBuildAgentUtil.KEY_ARTIFACT_ID, "" );
458             }
459             if ( StringUtils.isNotEmpty( parent.getVersion() ) )
460             {
461                 map.put( ContinuumBuildAgentUtil.KEY_PROJECT_VERSION, parent.getVersion() );
462             }
463             else
464             {
465                 map.put( ContinuumBuildAgentUtil.KEY_PROJECT_VERSION, "" );
466             }
467         }
468         return map;
469     }
470 
471     protected List<Map<String, Object>> getProjectDependencies( List<ProjectDependency> dependencies )
472     {
473         List<Map<String, Object>> pDependencies = new ArrayList<Map<String, Object>>();
474 
475         if ( dependencies != null )
476         {
477             for ( ProjectDependency dependency : dependencies )
478             {
479                 Map<String, Object> map = new HashMap<String, Object>();
480                 map.put( ContinuumBuildAgentUtil.KEY_GROUP_ID, dependency.getGroupId() );
481                 map.put( ContinuumBuildAgentUtil.KEY_ARTIFACT_ID, dependency.getArtifactId() );
482                 if ( StringUtils.isNotBlank( dependency.getVersion() ) )
483                 {
484                     map.put( ContinuumBuildAgentUtil.KEY_PROJECT_VERSION, dependency.getVersion() );
485                 }
486                 else
487                 {
488                     map.put( ContinuumBuildAgentUtil.KEY_PROJECT_VERSION, "" );
489                 }
490 
491                 pDependencies.add( map );
492             }
493         }
494         return pDependencies;
495     }
496 
497     protected List<Map<String, Object>> getProjectNotifiers( List<ProjectNotifier> notifiers )
498     {
499         List<Map<String, Object>> pNotifiers = new ArrayList<Map<String, Object>>();
500 
501         if ( notifiers != null )
502         {
503             for ( ProjectNotifier notifier : notifiers )
504             {
505                 Map<String, Object> map = new HashMap<String, Object>();
506 
507                 if ( notifier.getConfiguration() != null )
508                 {
509                     map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_CONFIGURATION, notifier.getConfiguration() );
510                 }
511                 else
512                 {
513                     map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_CONFIGURATION, "" );
514                 }
515                 if ( StringUtils.isNotBlank( notifier.getType() ) )
516                 {
517                     map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_TYPE, notifier.getType() );
518                 }
519                 else
520                 {
521                     map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_TYPE, "" );
522                 }
523                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_FROM, notifier.getFrom() );
524                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_RECIPIENT_TYPE, notifier.getRecipientType() );
525                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_ENABLED, notifier.isEnabled() );
526                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_SEND_ON_ERROR, notifier.isSendOnError() );
527                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_SEND_ON_SUCCESS, notifier.isSendOnSuccess() );
528                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_SEND_ON_FAILURE, notifier.isSendOnFailure() );
529                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_SEND_ON_SCMFAILURE, notifier.isSendOnScmFailure() );
530                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_SEND_ON_WARNING, notifier.isSendOnWarning() );
531                 pNotifiers.add(map);
532             }
533         }
534         return pNotifiers;
535     }
536 
537     public boolean isBuilding( Project project )
538     {
539         return project.getState() == ContinuumProjectState.BUILDING ||
540             getShellCommandHelper().isRunning( project.getId() );
541     }
542 
543     public void killProcess( Project project )
544     {
545         getShellCommandHelper().killProcess( project.getId() );
546     }
547 
548     public List<Artifact> getDeployableArtifacts( Project project, File workingDirectory,
549                                                   BuildDefinition buildDefinition )
550         throws ContinuumAgentBuildExecutorException
551     {
552         // Not supported by this builder
553         return Collections.EMPTY_LIST;
554     }
555 
556     public MavenProject getMavenProject( File workingDirectory, BuildDefinition buildDefinition )
557         throws ContinuumAgentBuildExecutorException
558     {
559         return null;
560     }
561 
562     public File getWorkingDirectory( int projectId )
563     {
564         return getBuildAgentConfigurationService().getWorkingDirectory( projectId );
565     }
566 
567     public boolean isResolveExecutable()
568     {
569         return resolveExecutable;
570     }
571 
572     public void setResolveExecutable( boolean resolveExecutable )
573     {
574         this.resolveExecutable = resolveExecutable;
575     }
576 
577     public void setExecutableResolver( ExecutableResolver executableResolver )
578     {
579         this.executableResolver = executableResolver;
580     }
581 
582     public ExecutableResolver getExecutableResolver()
583     {
584         return executableResolver;
585     }
586 }