1 package org.codehaus.mojo.exec;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import java.io.File;
17 import java.io.FileOutputStream;
18 import java.io.IOException;
19 import java.io.OutputStream;
20 import java.io.PrintStream;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Locale;
28 import java.util.Map;
29 import java.util.Properties;
30 import java.util.jar.JarEntry;
31 import java.util.jar.JarOutputStream;
32 import java.util.jar.Manifest;
33
34 import org.apache.commons.exec.CommandLine;
35 import org.apache.commons.exec.DefaultExecutor;
36 import org.apache.commons.exec.ExecuteException;
37 import org.apache.commons.exec.Executor;
38 import org.apache.commons.exec.OS;
39 import org.apache.commons.exec.PumpStreamHandler;
40 import org.apache.maven.artifact.Artifact;
41 import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
42 import org.apache.maven.artifact.resolver.filter.IncludesArtifactFilter;
43 import org.apache.maven.execution.MavenSession;
44 import org.apache.maven.plugin.MojoExecutionException;
45 import org.apache.maven.plugin.logging.Log;
46 import org.apache.maven.project.MavenProject;
47 import org.apache.maven.toolchain.Toolchain;
48 import org.apache.maven.toolchain.ToolchainManager;
49 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
50 import org.codehaus.plexus.util.StringUtils;
51 import org.codehaus.plexus.util.cli.CommandLineUtils;
52
53
54
55
56
57
58
59
60 public class ExecMojo extends AbstractExecMojo {
61
62
63
64
65 private String executable;
66
67
68
69
70 private File workingDirectory;
71
72
73
74
75
76 private File outputFile;
77
78
79
80
81
82 private List arguments;
83
84
85
86
87
88
89
90 private File basedir;
91
92
93
94
95 private Map environmentVariables = new HashMap();
96
97
98
99
100
101
102
103
104 private MavenSession session;
105
106
107
108
109
110 private List successCodes;
111
112
113
114
115
116 private boolean longClasspath;
117
118 public static final String CLASSPATH_TOKEN = "%classpath";
119
120
121
122
123
124
125
126 @Override
127 public void execute() throws MojoExecutionException {
128 try {
129 if (isSkip()) {
130 getLog().info("skipping execute as per configuraion");
131 return;
132 }
133
134 if (basedir == null) {
135 throw new IllegalStateException("basedir is null. Should not be possible.");
136 }
137
138 String argsProp = getSystemProperty("exec.args");
139
140 List commandArguments = new ArrayList();
141
142 if (hasCommandlineArgs()) {
143 String[] args = parseCommandlineArgs();
144 for (int i = 0; i < args.length; i++) {
145 if (isLongClassPathArgument(args[i])) {
146
147
148
149
150 commandArguments.add("-jar");
151 File tmpFile = createJar(computeClasspath(null), args[i + 2]);
152 commandArguments.add(tmpFile.getAbsolutePath());
153 i += 2;
154 } else if (CLASSPATH_TOKEN.equals(args[i])) {
155 commandArguments.add(computeClasspathString(null));
156 } else {
157 commandArguments.add(args[i]);
158 }
159 }
160 } else if (!isEmpty(argsProp)) {
161 getLog().debug("got arguments from system properties: " + argsProp);
162
163 try {
164 String[] args = CommandLineUtils.translateCommandline(argsProp);
165 commandArguments.addAll(Arrays.asList(args));
166 } catch (Exception e) {
167 throw new MojoExecutionException("Couldn't parse systemproperty 'exec.args'");
168 }
169 } else {
170 if (arguments != null) {
171 for (int i = 0; i < arguments.size(); i++) {
172 Object argument = arguments.get(i);
173 String arg;
174 if (argument == null) {
175 throw new MojoExecutionException("Misconfigured argument, value is null. "
176 + "Set the argument to an empty value if this is the required behaviour.");
177 } else if (argument instanceof String && isLongClassPathArgument((String) argument)) {
178
179
180
181
182 commandArguments.add("-jar");
183 File tmpFile = createJar(computeClasspath((Classpath) arguments.get(i + 1)),
184 (String) arguments.get(i + 2));
185 commandArguments.add(tmpFile.getAbsolutePath());
186 i += 2;
187 } else if (argument instanceof Classpath) {
188 Classpath specifiedClasspath = (Classpath) argument;
189
190 arg = computeClasspathString(specifiedClasspath);
191 commandArguments.add(arg);
192 } else {
193 arg = argument.toString();
194 commandArguments.add(arg);
195 }
196 }
197 }
198 }
199
200 Map enviro = new HashMap();
201 try {
202 Properties systemEnvVars = CommandLineUtils.getSystemEnvVars();
203 enviro.putAll(systemEnvVars);
204 } catch (IOException x) {
205 getLog().error("Could not assign default system enviroment variables.", x);
206 }
207
208 if (environmentVariables != null) {
209 Iterator iter = environmentVariables.keySet().iterator();
210 while (iter.hasNext()) {
211 String key = (String) iter.next();
212 String value = (String) environmentVariables.get(key);
213 enviro.put(key, value);
214 }
215 }
216
217 if (workingDirectory == null) {
218 workingDirectory = basedir;
219 }
220
221 if (!workingDirectory.exists()) {
222 getLog().debug("Making working directory '" + workingDirectory.getAbsolutePath() + "'.");
223 if (!workingDirectory.mkdirs()) {
224 throw new MojoExecutionException("Could not make working directory: '"
225 + workingDirectory.getAbsolutePath() + "'");
226 }
227 }
228
229 CommandLine commandLine = getExecutablePath(enviro, workingDirectory);
230
231 String[] args = new String[commandArguments.size()];
232 for (int i = 0; i < commandArguments.size(); i++) {
233 args[i] = (String) commandArguments.get(i);
234 }
235
236 commandLine.addArguments(args, false);
237
238 Executor exec = getExecutor();
239
240 exec.setWorkingDirectory(workingDirectory);
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260 OutputStream stdout = System.out;
261 OutputStream stderr = System.err;
262
263 try {
264 getLog().debug("Executing command line: " + commandLine);
265
266 int resultCode = executeCommandLine(exec, commandLine, enviro, stdout, stderr);
267
268 if (isResultCodeAFailure(resultCode)) {
269 throw new MojoExecutionException("Result of " + commandLine + " execution is: '" + resultCode
270 + "'.");
271 }
272 } catch (ExecuteException e) {
273 throw new MojoExecutionException("Command execution failed.", e);
274
275 } catch (IOException e) {
276 throw new MojoExecutionException("Command execution failed.", e);
277 }
278
279 registerSourceRoots();
280 } catch (IOException e) {
281 throw new MojoExecutionException("I/O Error", e);
282 }
283 }
284
285 protected Executor getExecutor() {
286 DefaultExecutor exec = new DefaultExecutor();
287
288 if (successCodes != null) {
289 int size = successCodes.size();
290 int[] exitValues = new int[size];
291 for (int i = 0; i < size; i++) {
292 exitValues[i] = new Integer(successCodes.get(i) + "");
293 }
294 exec.setExitValues(exitValues);
295 }
296 return exec;
297 }
298
299 boolean isResultCodeAFailure(int result) {
300 if (successCodes == null || successCodes.size() == 0) {
301 return result != 0;
302 }
303 for (Iterator it = successCodes.iterator(); it.hasNext();) {
304 int code = Integer.parseInt((String) it.next());
305 if (code == result) {
306 return false;
307 }
308 }
309 return true;
310 }
311
312 private boolean isLongClassPathArgument(String arg) {
313 return longClasspath && ("-classpath".equals(arg) || "-cp".equals(arg));
314 }
315
316 private Log getExecOutputLog() {
317 Log log = getLog();
318 if (outputFile != null) {
319 try {
320 if (!outputFile.getParentFile().exists() && !outputFile.getParentFile().mkdirs()) {
321 getLog().warn("Could not create non existing parent directories for log file: " + outputFile);
322 }
323 PrintStream stream = new PrintStream(new FileOutputStream(outputFile));
324
325 log = new StreamLog(stream);
326 } catch (Exception e) {
327 getLog().warn("Could not open " + outputFile + ". Using default log", e);
328 }
329 }
330
331 return log;
332 }
333
334
335
336
337
338
339
340
341
342
343
344 private String computeClasspathString(Classpath specifiedClasspath) {
345 List resultList = computeClasspath(specifiedClasspath);
346 StringBuffer theClasspath = new StringBuffer();
347
348 for (Iterator it = resultList.iterator(); it.hasNext();) {
349 String str = (String) it.next();
350 addToClasspath(theClasspath, str);
351 }
352
353 return theClasspath.toString();
354 }
355
356
357
358
359
360
361
362
363
364
365
366 private List computeClasspath(Classpath specifiedClasspath) {
367 List artifacts = new ArrayList();
368 List theClasspathFiles = new ArrayList();
369 List resultList = new ArrayList();
370
371 collectProjectArtifactsAndClasspath(artifacts, theClasspathFiles);
372
373 if ((specifiedClasspath != null) && (specifiedClasspath.getDependencies() != null)) {
374 artifacts = filterArtifacts(artifacts, specifiedClasspath.getDependencies());
375 }
376
377 for (Iterator it = theClasspathFiles.iterator(); it.hasNext();) {
378 File f = (File) it.next();
379 resultList.add(f.getAbsolutePath());
380 }
381
382 for (Iterator it = artifacts.iterator(); it.hasNext();) {
383 Artifact artifact = (Artifact) it.next();
384 getLog().debug("dealing with " + artifact);
385 resultList.add(artifact.getFile().getAbsolutePath());
386 }
387
388 return resultList;
389 }
390
391 private static void addToClasspath(StringBuffer theClasspath, String toAdd) {
392 if (theClasspath.length() > 0) {
393 theClasspath.append(File.pathSeparator);
394 }
395 theClasspath.append(toAdd);
396 }
397
398 private List filterArtifacts(List artifacts, Collection dependencies) {
399 AndArtifactFilter filter = new AndArtifactFilter();
400
401 filter.add(new IncludesArtifactFilter(new ArrayList(dependencies)));
402
403 List filteredArtifacts = new ArrayList();
404 for (Iterator it = artifacts.iterator(); it.hasNext();) {
405 Artifact artifact = (Artifact) it.next();
406 if (filter.include(artifact)) {
407 getLog().debug("filtering in " + artifact);
408 filteredArtifacts.add(artifact);
409 }
410 }
411 return filteredArtifacts;
412 }
413
414 CommandLine getExecutablePath(Map enviro, File dir) {
415 File execFile = new File(executable);
416 String exec = null;
417 if (execFile.exists()) {
418 getLog().debug("Toolchains are ignored, 'executable' parameter is set to " + executable);
419 exec = execFile.getAbsolutePath();
420 } else {
421 Toolchain tc = getToolchain();
422
423
424
425
426 if (tc != null) {
427 getLog().info("Toolchain in exec-maven-plugin: " + tc);
428 exec = tc.findTool(executable);
429 } else {
430 if (OS.isFamilyWindows()) {
431 String ex = executable.indexOf(".") < 0 ? executable + ".bat" : executable;
432 File f = new File(dir, ex);
433 if (f.exists()) {
434 exec = ex;
435 } else {
436
437
438 String path = (String) enviro.get("PATH");
439 if (path != null) {
440 String[] elems = StringUtils.split(path, File.pathSeparator);
441 for (int i = 0; i < elems.length; i++) {
442 f = new File(new File(elems[i]), ex);
443 if (f.exists()) {
444 exec = ex;
445 break;
446 }
447 }
448 }
449 }
450 }
451 }
452 }
453
454 if (exec == null) {
455 exec = executable;
456 }
457
458 CommandLine toRet;
459 if (OS.isFamilyWindows() && exec.toLowerCase(Locale.getDefault()).endsWith(".bat")) {
460 toRet = new CommandLine("cmd");
461 toRet.addArgument("/c");
462 toRet.addArgument(exec);
463 } else {
464 toRet = new CommandLine(exec);
465 }
466
467 return toRet;
468 }
469
470
471
472
473
474
475 private static boolean isEmpty(String string) {
476 return string == null || string.length() == 0;
477 }
478
479
480
481
482
483 protected int executeCommandLine(Executor exec, CommandLine commandLine, Map enviro, OutputStream out,
484 OutputStream err) throws IOException {
485 exec.setStreamHandler(new PumpStreamHandler(out, err, System.in));
486 return exec.execute(commandLine, enviro);
487 }
488
489 void setExecutable(String executable) {
490 this.executable = executable;
491 }
492
493 String getExecutable() {
494 return executable;
495 }
496
497 void setWorkingDirectory(String workingDir) {
498 setWorkingDirectory(new File(workingDir));
499 }
500
501 void setWorkingDirectory(File workingDir) {
502 this.workingDirectory = workingDir;
503 }
504
505 void setArguments(List arguments) {
506 this.arguments = arguments;
507 }
508
509 void setBasedir(File basedir) {
510 this.basedir = basedir;
511 }
512
513 void setProject(MavenProject project) {
514 this.project = project;
515 }
516
517 protected String getSystemProperty(String key) {
518 return System.getProperty(key);
519 }
520
521 public void setSuccessCodes(List list) {
522 this.successCodes = list;
523 }
524
525 public List getSuccessCodes() {
526 return successCodes;
527 }
528
529 private Toolchain getToolchain() {
530 Toolchain tc = null;
531
532 try {
533 if (session != null) {
534 ToolchainManager toolchainManager = (ToolchainManager) session.getContainer().lookup(
535 ToolchainManager.ROLE);
536
537 if (toolchainManager != null) {
538 tc = toolchainManager.getToolchainFromBuildContext("jdk", session);
539 }
540 }
541 } catch (ComponentLookupException componentLookupException) {
542
543 }
544 return tc;
545 }
546
547
548
549
550
551
552
553
554
555
556 private File createJar(List classPath, String mainClass) throws IOException {
557 File file = File.createTempFile("maven-exec", ".jar");
558 file.deleteOnExit();
559 FileOutputStream fos = new FileOutputStream(file);
560 JarOutputStream jos = new JarOutputStream(fos);
561 jos.setLevel(JarOutputStream.STORED);
562 JarEntry je = new JarEntry("META-INF/MANIFEST.MF");
563 jos.putNextEntry(je);
564
565 Manifest man = new Manifest();
566
567
568
569 String cp = "";
570 for (Iterator it = classPath.iterator(); it.hasNext();) {
571 String el = (String) it.next();
572
573 cp += UrlUtils.getURL(new File(el)).toExternalForm() + " ";
574 }
575
576 man.getMainAttributes().putValue("Manifest-Version", "1.0");
577 man.getMainAttributes().putValue("Class-Path", cp.trim());
578 man.getMainAttributes().putValue("Main-Class", mainClass);
579
580 man.write(jos);
581 jos.close();
582
583 return file;
584 }
585 }