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 org.apache.continuum.buildagent.configuration.BuildAgentConfigurationService;
23  import org.apache.continuum.buildagent.installation.BuildAgentInstallationService;
24  import org.apache.continuum.buildagent.manager.BuildAgentManager;
25  import org.apache.continuum.buildagent.utils.ContinuumBuildAgentUtil;
26  import org.apache.continuum.utils.shell.ExecutionResult;
27  import org.apache.continuum.utils.shell.ShellCommandHelper;
28  import org.apache.maven.artifact.Artifact;
29  import org.apache.maven.continuum.model.project.BuildDefinition;
30  import org.apache.maven.continuum.model.project.Project;
31  import org.apache.maven.continuum.model.project.ProjectDependency;
32  import org.apache.maven.continuum.model.project.ProjectDeveloper;
33  import org.apache.maven.continuum.model.project.ProjectNotifier;
34  import org.apache.maven.continuum.project.ContinuumProjectState;
35  import org.apache.maven.project.MavenProject;
36  import org.codehaus.plexus.commandline.ExecutableResolver;
37  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
38  import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
39  import org.codehaus.plexus.util.StringUtils;
40  import org.codehaus.plexus.util.cli.CommandLineException;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  import java.io.File;
45  import java.util.ArrayList;
46  import java.util.Collections;
47  import java.util.HashMap;
48  import java.util.List;
49  import java.util.Map;
50  import java.util.Properties;
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 = getShellCommandHelper().executeShellCommand( workingDirectory, actualExecutable,
271                                                                                   arguments, output, project.getId(),
272                                                                                   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, getProjectDevelopers(
389             project.getDevelopers() ) );
390         projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEPENDENCIES, getProjectDependencies(
391             project.getDependencies() ) );
392         projectMap.put( ContinuumBuildAgentUtil.KEY_PROJECT_NOTIFIERS, getProjectNotifiers( project.getNotifiers() ) );
393 
394         try
395         {
396             log.debug( "Update project {}" + project.getId() );
397             buildAgentManager.updateProject( projectMap );
398         }
399         catch ( Exception e )
400         {
401             throw new ContinuumAgentBuildExecutorException( "Failed to update project", e );
402         }
403     }
404 
405     protected List<Map<String, String>> getProjectDevelopers( List<ProjectDeveloper> developers )
406     {
407         List<Map<String, String>> pDevelopers = new ArrayList<Map<String, String>>();
408 
409         if ( developers != null )
410         {
411             for ( ProjectDeveloper developer : developers )
412             {
413                 Map<String, String> map = new HashMap<String, String>();
414                 if ( StringUtils.isNotEmpty( developer.getName() ) )
415                 {
416                     map.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEVELOPER_NAME, developer.getName() );
417                 }
418                 else
419                 {
420                     map.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEVELOPER_NAME, "" );
421                 }
422                 if ( StringUtils.isNotEmpty( developer.getEmail() ) )
423                 {
424                     map.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEVELOPER_EMAIL, developer.getEmail() );
425                 }
426                 else
427                 {
428                     map.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEVELOPER_EMAIL, "" );
429                 }
430                 if ( StringUtils.isNotEmpty( developer.getScmId() ) )
431                 {
432                     map.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEVELOPER_SCMID, developer.getScmId() );
433                 }
434                 else
435                 {
436                     map.put( ContinuumBuildAgentUtil.KEY_PROJECT_DEVELOPER_SCMID, "" );
437                 }
438 
439                 pDevelopers.add( map );
440             }
441         }
442         return pDevelopers;
443     }
444 
445     protected Map<String, Object> getProjectParent( ProjectDependency parent )
446     {
447         Map<String, Object> map = new HashMap<String, Object>();
448 
449         if ( parent != null )
450         {
451             if ( StringUtils.isNotEmpty( parent.getGroupId() ) )
452             {
453                 map.put( ContinuumBuildAgentUtil.KEY_GROUP_ID, parent.getGroupId() );
454             }
455             else
456             {
457                 map.put( ContinuumBuildAgentUtil.KEY_GROUP_ID, "" );
458             }
459             if ( StringUtils.isNotEmpty( parent.getArtifactId() ) )
460             {
461                 map.put( ContinuumBuildAgentUtil.KEY_ARTIFACT_ID, parent.getArtifactId() );
462             }
463             else
464             {
465                 map.put( ContinuumBuildAgentUtil.KEY_ARTIFACT_ID, "" );
466             }
467             if ( StringUtils.isNotEmpty( parent.getVersion() ) )
468             {
469                 map.put( ContinuumBuildAgentUtil.KEY_PROJECT_VERSION, parent.getVersion() );
470             }
471             else
472             {
473                 map.put( ContinuumBuildAgentUtil.KEY_PROJECT_VERSION, "" );
474             }
475         }
476         return map;
477     }
478 
479     protected List<Map<String, Object>> getProjectDependencies( List<ProjectDependency> dependencies )
480     {
481         List<Map<String, Object>> pDependencies = new ArrayList<Map<String, Object>>();
482 
483         if ( dependencies != null )
484         {
485             for ( ProjectDependency dependency : dependencies )
486             {
487                 Map<String, Object> map = new HashMap<String, Object>();
488                 if ( StringUtils.isNotEmpty( dependency.getGroupId() ) )
489                 {
490                     map.put( ContinuumBuildAgentUtil.KEY_GROUP_ID, dependency.getGroupId() );
491                 }
492                 else
493                 {
494                     map.put( ContinuumBuildAgentUtil.KEY_GROUP_ID, "" );
495                 }
496                 if ( StringUtils.isNotEmpty( dependency.getArtifactId() ) )
497                 {
498                     map.put( ContinuumBuildAgentUtil.KEY_ARTIFACT_ID, dependency.getArtifactId() );
499                 }
500                 else
501                 {
502                     map.put( ContinuumBuildAgentUtil.KEY_ARTIFACT_ID, "" );
503                 }
504                 if ( StringUtils.isNotEmpty( dependency.getVersion() ) )
505                 {
506                     map.put( ContinuumBuildAgentUtil.KEY_PROJECT_VERSION, dependency.getVersion() );
507                 }
508                 else
509                 {
510                     map.put( ContinuumBuildAgentUtil.KEY_PROJECT_VERSION, "" );
511                 }
512 
513                 pDependencies.add( map );
514             }
515         }
516         return pDependencies;
517     }
518 
519     protected List<Map<String, Object>> getProjectNotifiers( List<ProjectNotifier> notifiers )
520     {
521         List<Map<String, Object>> pNotifiers = new ArrayList<Map<String, Object>>();
522 
523         if ( notifiers != null )
524         {
525             for ( ProjectNotifier notifier : notifiers )
526             {
527                 Map<String, Object> map = new HashMap<String, Object>();
528 
529                 if ( notifier.getConfiguration() != null )
530                 {
531                     map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_CONFIGURATION, notifier.getConfiguration() );
532                 }
533                 else
534                 {
535                     map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_CONFIGURATION, "" );
536                 }
537                 if ( StringUtils.isNotBlank( notifier.getType() ) )
538                 {
539                     map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_TYPE, notifier.getType() );
540                 }
541                 else
542                 {
543                     map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_TYPE, "" );
544                 }
545                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_FROM, notifier.getFrom() );
546                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_RECIPIENT_TYPE, notifier.getRecipientType() );
547                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_ENABLED, notifier.isEnabled() );
548                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_SEND_ON_ERROR, notifier.isSendOnError() );
549                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_SEND_ON_SUCCESS, notifier.isSendOnSuccess() );
550                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_SEND_ON_FAILURE, notifier.isSendOnFailure() );
551                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_SEND_ON_SCMFAILURE, notifier.isSendOnScmFailure() );
552                 map.put( ContinuumBuildAgentUtil.KEY_NOTIFIER_SEND_ON_WARNING, notifier.isSendOnWarning() );
553                 pNotifiers.add( map );
554             }
555         }
556         return pNotifiers;
557     }
558 
559     public boolean isBuilding( Project project )
560     {
561         return project.getState() == ContinuumProjectState.BUILDING || getShellCommandHelper().isRunning(
562             project.getId() );
563     }
564 
565     public void killProcess( Project project )
566     {
567         getShellCommandHelper().killProcess( project.getId() );
568     }
569 
570     public List<Artifact> getDeployableArtifacts( Project project, File workingDirectory,
571                                                   BuildDefinition buildDefinition )
572         throws ContinuumAgentBuildExecutorException
573     {
574         // Not supported by this builder
575         return Collections.EMPTY_LIST;
576     }
577 
578     public MavenProject getMavenProject( File workingDirectory, BuildDefinition buildDefinition )
579         throws ContinuumAgentBuildExecutorException
580     {
581         return null;
582     }
583 
584     public File getWorkingDirectory( int projectId )
585     {
586         return getBuildAgentConfigurationService().getWorkingDirectory( projectId );
587     }
588 
589     public boolean isResolveExecutable()
590     {
591         return resolveExecutable;
592     }
593 
594     public void setResolveExecutable( boolean resolveExecutable )
595     {
596         this.resolveExecutable = resolveExecutable;
597     }
598 
599     public void setExecutableResolver( ExecutableResolver executableResolver )
600     {
601         this.executableResolver = executableResolver;
602     }
603 
604     public ExecutableResolver getExecutableResolver()
605     {
606         return executableResolver;
607     }
608 }