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