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