1 package org.apache.maven.continuum.execution;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.continuum.utils.shell.ExecutionResult;
23 import org.apache.continuum.utils.shell.ShellCommandHelper;
24 import org.apache.maven.artifact.Artifact;
25 import org.apache.maven.continuum.builddefinition.BuildDefinitionUpdatePolicyConstants;
26 import org.apache.maven.continuum.installation.InstallationService;
27 import org.apache.maven.continuum.model.project.BuildDefinition;
28 import org.apache.maven.continuum.model.project.Project;
29 import org.apache.maven.continuum.model.scm.ChangeFile;
30 import org.apache.maven.continuum.model.scm.ChangeSet;
31 import org.apache.maven.continuum.model.scm.ScmResult;
32 import org.apache.maven.continuum.model.system.Installation;
33 import org.apache.maven.continuum.model.system.Profile;
34 import org.apache.maven.continuum.project.ContinuumProjectState;
35 import org.apache.maven.continuum.utils.WorkingDirectoryService;
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.Collections;
46 import java.util.HashMap;
47 import java.util.Iterator;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.Properties;
51
52
53
54
55
56 public abstract class AbstractBuildExecutor
57 implements ContinuumBuildExecutor, Initializable
58 {
59 protected final Logger log = LoggerFactory.getLogger( getClass() );
60
61 private static final String SUDO_EXECUTABLE = "sudo";
62
63 private static final String CHROOT_EXECUTABLE = "chroot";
64
65
66
67
68
69
70
71
72 private ShellCommandHelper shellCommandHelper;
73
74
75
76
77 private ExecutableResolver executableResolver;
78
79
80
81
82 private WorkingDirectoryService workingDirectoryService;
83
84
85
86
87 private InstallationService installationService;
88
89
90
91
92 private File chrootJailDirectory;
93
94
95
96
97 private String defaultExecutable;
98
99
100
101
102
103 private final String id;
104
105 private boolean resolveExecutable;
106
107
108
109
110
111 protected AbstractBuildExecutor( String id, boolean resolveExecutable )
112 {
113 this.id = id;
114
115 this.resolveExecutable = resolveExecutable;
116 }
117
118 public void setShellCommandHelper( ShellCommandHelper shellCommandHelper )
119 {
120 this.shellCommandHelper = shellCommandHelper;
121 }
122
123 public ShellCommandHelper getShellCommandHelper()
124 {
125 return shellCommandHelper;
126 }
127
128 public void setWorkingDirectoryService( WorkingDirectoryService workingDirectoryService )
129 {
130 this.workingDirectoryService = workingDirectoryService;
131 }
132
133 public WorkingDirectoryService getWorkingDirectoryService()
134 {
135 return workingDirectoryService;
136 }
137
138 public void setDefaultExecutable( String defaultExecutable )
139 {
140 this.defaultExecutable = defaultExecutable;
141 }
142
143
144
145
146
147 public String getDefaultExecutable()
148 {
149 return defaultExecutable;
150 }
151
152 public void initialize()
153 throws InitializationException
154 {
155 List path = executableResolver.getDefaultPath();
156
157 if ( resolveExecutable )
158 {
159 if ( StringUtils.isEmpty( defaultExecutable ) )
160 {
161 log.warn( "The default executable for build executor '" + id + "' is not set. " +
162 "This will cause a problem unless the project has a executable configured." );
163 }
164 else
165 {
166 File resolvedExecutable = executableResolver.findExecutable( defaultExecutable, path );
167
168 if ( resolvedExecutable == null )
169 {
170 log.warn(
171 "Could not find the executable '" + defaultExecutable + "' in the " + "path '" + path + "'." );
172 }
173 else
174 {
175 log.info( "Resolved the executable '" + defaultExecutable + "' to " + "'" +
176 resolvedExecutable.getAbsolutePath() + "'." );
177 }
178 }
179 }
180 }
181
182
183
184
185
186
187
188
189
190
191
192 protected String findExecutable( String executable, String defaultExecutable, boolean resolveExecutable,
193 File workingDirectory )
194 {
195
196
197
198
199
200
201 String actualExecutable;
202
203 if ( !resolveExecutable )
204 {
205 actualExecutable = new File( workingDirectory, executable ).getAbsolutePath();
206 }
207 else
208 {
209 List<String> path = executableResolver.getDefaultPath();
210
211 if ( StringUtils.isEmpty( executable ) )
212 {
213 executable = defaultExecutable;
214 }
215
216 File e = executableResolver.findExecutable( executable, path );
217
218 if ( e == null )
219 {
220 log.warn( "Could not find the executable '" + executable + "' in this path: " );
221
222 for ( String element : path )
223 {
224 log.warn( element );
225 }
226
227 actualExecutable = defaultExecutable;
228 }
229 else
230 {
231 actualExecutable = e.getAbsolutePath();
232 }
233 }
234
235
236 File actualExecutableFile = new File( actualExecutable );
237
238 if ( !actualExecutableFile.exists() )
239 {
240 actualExecutable = executable;
241 }
242
243 return actualExecutable;
244 }
245
246 protected ContinuumBuildExecutionResult executeShellCommand( Project project, String executable, String arguments,
247 File output, Map<String, String> environments,
248 List<Project> projectsWithCommonScmRoot,
249 String projectScmRootUrl )
250 throws ContinuumBuildExecutorException
251 {
252
253 File workingDirectory = getWorkingDirectory( project, projectScmRootUrl, projectsWithCommonScmRoot );
254
255 String actualExecutable = findExecutable( executable, defaultExecutable, resolveExecutable, workingDirectory );
256
257
258
259
260
261 try
262 {
263 File chrootJailDirectory = getChrootJailDirectory();
264 if ( chrootJailDirectory != null )
265 {
266 StringBuilder sb = new StringBuilder();
267 sb.append( CHROOT_EXECUTABLE );
268 sb.append( " " );
269 sb.append( new File( chrootJailDirectory, project.getGroupId() ) );
270 sb.append( " " );
271 sb.append( " /bin/sh -c 'cd " );
272 sb.append( getRelativePath( chrootJailDirectory, workingDirectory, project.getGroupId() ) );
273 sb.append( " && " );
274 sb.append( actualExecutable );
275 sb.append( " " );
276 sb.append( arguments );
277 sb.append( "'" );
278
279 arguments = sb.toString();
280 actualExecutable = SUDO_EXECUTABLE;
281 workingDirectory = chrootJailDirectory;
282 }
283
284 ExecutionResult result = getShellCommandHelper().executeShellCommand( workingDirectory, actualExecutable,
285 arguments, output, project.getId(),
286 environments );
287
288 log.info( "Exit code: " + result.getExitCode() );
289
290 return new ContinuumBuildExecutionResult( output, result.getExitCode() );
291 }
292 catch ( CommandLineException e )
293 {
294 if ( e.getCause() instanceof InterruptedException )
295 {
296 throw new ContinuumBuildCancelledException( "The build was cancelled", e );
297 }
298 else
299 {
300 throw new ContinuumBuildExecutorException(
301 "Error while executing shell command. The most common error is that '" + executable + "' " +
302 "is not in your path.", e );
303 }
304 }
305 catch ( Exception e )
306 {
307 throw new ContinuumBuildExecutorException(
308 "Error while executing shell command. " + "The most common error is that '" + executable + "' " +
309 "is not in your path.", e );
310 }
311 }
312
313 private String getRelativePath( File chrootDir, File workingDirectory, String groupId )
314 {
315 String path = workingDirectory.getPath();
316 String chrootBase = new File( chrootDir, groupId ).getPath();
317 if ( path.startsWith( chrootBase ) )
318 {
319 return path.substring( chrootBase.length(), path.length() );
320 }
321 else
322 {
323 throw new IllegalArgumentException(
324 "Working directory is not inside the chroot jail " + chrootBase + " , " + path );
325 }
326 }
327
328 protected abstract Map<String, String> getEnvironments( BuildDefinition buildDefinition );
329
330 protected String getJavaHomeValue( BuildDefinition buildDefinition )
331 {
332 Profile profile = buildDefinition.getProfile();
333 if ( profile == null )
334 {
335 return null;
336 }
337 Installation jdk = profile.getJdk();
338 if ( jdk == null )
339 {
340 return null;
341 }
342 return jdk.getVarValue();
343 }
344
345 public void backupTestFiles( Project project, int buildId, String projectScmRootUrl,
346 List<Project> projectsWithCommonScmRoot )
347 {
348
349 }
350
351
352
353
354 public boolean shouldBuild( List<ChangeSet> changes, Project continuumProject, File workingDirectory,
355 BuildDefinition buildDefinition )
356 throws ContinuumBuildExecutorException
357 {
358 return true;
359 }
360
361 protected Map<String, String> getEnvironmentVariables( BuildDefinition buildDefinition )
362 {
363 Profile profile = buildDefinition.getProfile();
364 Map<String, String> envVars = new HashMap<String, String>();
365 if ( profile == null )
366 {
367 return envVars;
368 }
369 List<Installation> environmentVariables = profile.getEnvironmentVariables();
370 if ( environmentVariables.isEmpty() )
371 {
372 return envVars;
373 }
374 for ( Installation installation : environmentVariables )
375 {
376 envVars.put( installation.getVarName(), installation.getVarValue() );
377 }
378 return envVars;
379 }
380
381 protected Properties getContinuumSystemProperties( Project project )
382 {
383 Properties properties = new Properties();
384 properties.setProperty( "continuum.project.group.name", project.getProjectGroup().getName() );
385 properties.setProperty( "continuum.project.lastBuild.state", String.valueOf( project.getOldState() ) );
386 properties.setProperty( "continuum.project.lastBuild.number", String.valueOf( project.getBuildNumber() ) );
387 properties.setProperty( "continuum.project.nextBuild.number", String.valueOf( project.getBuildNumber() + 1 ) );
388 properties.setProperty( "continuum.project.id", String.valueOf( project.getId() ) );
389 properties.setProperty( "continuum.project.name", project.getName() );
390 properties.setProperty( "continuum.project.version", project.getVersion() );
391 return properties;
392 }
393
394 protected String getBuildFileForProject( Project project, BuildDefinition buildDefinition )
395 {
396 String buildFile = StringUtils.clean( buildDefinition.getBuildFile() );
397 String relPath = StringUtils.clean( project.getRelativePath() );
398
399 if ( StringUtils.isEmpty( relPath ) )
400 {
401 return buildFile;
402 }
403
404 return relPath + File.separator + buildFile;
405 }
406
407 protected boolean isDescriptionUpdated( BuildDefinition buildDefinition, ScmResult scmResult, Project project )
408 {
409 boolean update = true;
410 if ( buildDefinition != null && scmResult != null )
411 {
412 int policy = buildDefinition.getUpdatePolicy();
413 if ( BuildDefinitionUpdatePolicyConstants.UPDATE_DESCRIPTION_NEVER == policy )
414 {
415 update = false;
416 }
417 else if ( BuildDefinitionUpdatePolicyConstants.UPDATE_DESCRIPTION_ONLY_FOR_NEW_POM == policy )
418 {
419 update = pomUpdated( buildDefinition.getBuildFile(), scmResult, project );
420 }
421 }
422 return update;
423 }
424
425 private boolean pomUpdated( String buildFile, ScmResult scmResult, Project project )
426 {
427 String filename = project.getScmUrl() + "/" + buildFile;
428 for ( Iterator changeIt = scmResult.getChanges().listIterator(); changeIt.hasNext(); )
429 {
430 ChangeSet change = (ChangeSet) changeIt.next();
431 for ( Iterator fileIt = change.getFiles().listIterator(); fileIt.hasNext(); )
432 {
433 ChangeFile changeFile = (ChangeFile) fileIt.next();
434 if ( filename.endsWith( changeFile.getName() ) )
435 {
436 return true;
437 }
438 }
439 }
440 return false;
441 }
442
443 public boolean isBuilding( Project project )
444 {
445 return project.getState() == ContinuumProjectState.BUILDING || getShellCommandHelper().isRunning(
446 project.getId() );
447 }
448
449 public void killProcess( Project project )
450 {
451 getShellCommandHelper().killProcess( project.getId() );
452 }
453
454 public List<Artifact> getDeployableArtifacts( Project project, File workingDirectory,
455 BuildDefinition buildDefinition )
456 throws ContinuumBuildExecutorException
457 {
458
459 return Collections.emptyList();
460 }
461
462 public File getWorkingDirectory( Project project, String projectScmRootUrl,
463 List<Project> projectsWithCommonScmRoot )
464 {
465 return getWorkingDirectoryService().getWorkingDirectory( project, projectScmRootUrl,
466 projectsWithCommonScmRoot );
467 }
468
469 public InstallationService getInstallationService()
470 {
471 return installationService;
472 }
473
474 public void setInstallationService( InstallationService installationService )
475 {
476 this.installationService = installationService;
477 }
478
479 public boolean isResolveExecutable()
480 {
481 return resolveExecutable;
482 }
483
484 public void setResolveExecutable( boolean resolveExecutable )
485 {
486 this.resolveExecutable = resolveExecutable;
487 }
488
489 public void setExecutableResolver( ExecutableResolver executableResolver )
490 {
491 this.executableResolver = executableResolver;
492 }
493
494 public ExecutableResolver getExecutableResolver()
495 {
496 return executableResolver;
497 }
498
499 public void setChrootJailDirectory( File chrootJailDirectory )
500 {
501 this.chrootJailDirectory = chrootJailDirectory;
502 }
503
504 public File getChrootJailDirectory()
505 {
506 return chrootJailDirectory;
507 }
508 }