Coverage Report - org.codehaus.mojo.exec.ExecMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
ExecMojo
0%
0/227
0%
0/114
4
 
 1  
 package org.codehaus.mojo.exec;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
 5  
  * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 7  
  * License. You may obtain a copy of the License at
 8  
  * 
 9  
  * http://www.apache.org/licenses/LICENSE-2.0
 10  
  * 
 11  
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 12  
  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 13  
  * specific language governing permissions and limitations under the License.
 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  
  * A Plugin for executing external programs.
 55  
  * 
 56  
  * @author Jerome Lacoste <jerome@coffeebreaks.org>
 57  
  * @version $Id: ExecMojo.java 12386 2010-07-16 22:10:38Z rfscholte $
 58  
  * @since 1.0
 59  
  */
 60  0
 public class ExecMojo extends AbstractExecMojo {
 61  
     /**
 62  
      * The executable. Can be a full path or a the name executable. In the latter case, the executable must be in the
 63  
      * PATH for the execution to work.
 64  
      */
 65  
     private String executable;
 66  
 
 67  
     /**
 68  
      * The current working directory. Optional. If not specified, basedir will be used.
 69  
      */
 70  
     private File workingDirectory;
 71  
 
 72  
     /**
 73  
      * Program standard and error output will be redirected to the file specified by this optional field. If not
 74  
      * specified the standard maven logging is used.
 75  
      */
 76  
     private File outputFile;
 77  
 
 78  
     /**
 79  
      * Can be of type <code>&lt;argument&gt;</code> or <code>&lt;classpath&gt;</code> Can be overridden using
 80  
      * "exec.args" env. variable
 81  
      */
 82  
     private List arguments;
 83  
 
 84  
     /**
 85  
      * @parameter expression="${basedir}"
 86  
      * @required
 87  
      * @readonly
 88  
      * @since 1.0
 89  
      */
 90  
     private File basedir;
 91  
 
 92  
     /**
 93  
      * Environment variables to pass to the executed program.
 94  
      */
 95  0
     private Map environmentVariables = new HashMap();
 96  
 
 97  
     /**
 98  
      * The current build session instance. This is used for toolchain manager API calls.
 99  
      * 
 100  
      * @parameter expression="${session}"
 101  
      * @required
 102  
      * @readonly
 103  
      */
 104  
     private MavenSession session;
 105  
 
 106  
     /**
 107  
      * Exit codes to be resolved as successful execution for non-compliant applications (applications not returning 0
 108  
      * for success).
 109  
      */
 110  
     private List successCodes;
 111  
 
 112  
     /**
 113  
      * If set to true the classpath and the main class will be written to a MANIFEST.MF file and wrapped into a jar.
 114  
      * Instead of '-classpath/-cp CLASSPATH mainClass' the exec plugin executes '-jar maven-exec.jar'.
 115  
      */
 116  
     private boolean longClasspath;
 117  
 
 118  
     public static final String CLASSPATH_TOKEN = "%classpath";
 119  
 
 120  
     /**
 121  
      * priority in the execute method will be to use System properties arguments over the pom specification.
 122  
      * 
 123  
      * @throws MojoExecutionException
 124  
      *             if a failure happens
 125  
      */
 126  
     @Override
 127  
     public void execute() throws MojoExecutionException {
 128  
         try {
 129  0
             if (isSkip()) {
 130  0
                 getLog().info("skipping execute as per configuraion");
 131  0
                 return;
 132  
             }
 133  
 
 134  0
             if (basedir == null) {
 135  0
                 throw new IllegalStateException("basedir is null. Should not be possible.");
 136  
             }
 137  
 
 138  0
             String argsProp = getSystemProperty("exec.args");
 139  
 
 140  0
             List commandArguments = new ArrayList();
 141  
 
 142  0
             if (hasCommandlineArgs()) {
 143  0
                 String[] args = parseCommandlineArgs();
 144  0
                 for (int i = 0; i < args.length; i++) {
 145  0
                     if (isLongClassPathArgument(args[i])) {
 146  
                         // it is assumed that starting from -cp or -classpath the arguments
 147  
                         // are: -classpath/-cp %classpath mainClass
 148  
                         // the arguments are replaced with: -jar $TMP/maven-exec.jar
 149  
                         // NOTE: the jar will contain the classpath and the main class
 150  0
                         commandArguments.add("-jar");
 151  0
                         File tmpFile = createJar(computeClasspath(null), args[i + 2]);
 152  0
                         commandArguments.add(tmpFile.getAbsolutePath());
 153  0
                         i += 2;
 154  0
                     } else if (CLASSPATH_TOKEN.equals(args[i])) {
 155  0
                         commandArguments.add(computeClasspathString(null));
 156  
                     } else {
 157  0
                         commandArguments.add(args[i]);
 158  
                     }
 159  
                 }
 160  0
             } else if (!isEmpty(argsProp)) {
 161  0
                 getLog().debug("got arguments from system properties: " + argsProp);
 162  
 
 163  
                 try {
 164  0
                     String[] args = CommandLineUtils.translateCommandline(argsProp);
 165  0
                     commandArguments.addAll(Arrays.asList(args));
 166  0
                 } catch (Exception e) {
 167  0
                     throw new MojoExecutionException("Couldn't parse systemproperty 'exec.args'");
 168  0
                 }
 169  
             } else {
 170  0
                 if (arguments != null) {
 171  0
                     for (int i = 0; i < arguments.size(); i++) {
 172  0
                         Object argument = arguments.get(i);
 173  
                         String arg;
 174  0
                         if (argument == null) {
 175  0
                             throw new MojoExecutionException("Misconfigured argument, value is null. "
 176  
                                     + "Set the argument to an empty value if this is the required behaviour.");
 177  0
                         } else if (argument instanceof String && isLongClassPathArgument((String) argument)) {
 178  
                             // it is assumed that starting from -cp or -classpath the arguments
 179  
                             // are: -classpath/-cp %classpath mainClass
 180  
                             // the arguments are replaced with: -jar $TMP/maven-exec.jar
 181  
                             // NOTE: the jar will contain the classpath and the main class
 182  0
                             commandArguments.add("-jar");
 183  0
                             File tmpFile = createJar(computeClasspath((Classpath) arguments.get(i + 1)),
 184  
                                     (String) arguments.get(i + 2));
 185  0
                             commandArguments.add(tmpFile.getAbsolutePath());
 186  0
                             i += 2;
 187  0
                         } else if (argument instanceof Classpath) {
 188  0
                             Classpath specifiedClasspath = (Classpath) argument;
 189  
 
 190  0
                             arg = computeClasspathString(specifiedClasspath);
 191  0
                             commandArguments.add(arg);
 192  0
                         } else {
 193  0
                             arg = argument.toString();
 194  0
                             commandArguments.add(arg);
 195  
                         }
 196  
                     }
 197  
                 }
 198  
             }
 199  
 
 200  0
             Map enviro = new HashMap();
 201  
             try {
 202  0
                 Properties systemEnvVars = CommandLineUtils.getSystemEnvVars();
 203  0
                 enviro.putAll(systemEnvVars);
 204  0
             } catch (IOException x) {
 205  0
                 getLog().error("Could not assign default system enviroment variables.", x);
 206  0
             }
 207  
 
 208  0
             if (environmentVariables != null) {
 209  0
                 Iterator iter = environmentVariables.keySet().iterator();
 210  0
                 while (iter.hasNext()) {
 211  0
                     String key = (String) iter.next();
 212  0
                     String value = (String) environmentVariables.get(key);
 213  0
                     enviro.put(key, value);
 214  0
                 }
 215  
             }
 216  
 
 217  0
             if (workingDirectory == null) {
 218  0
                 workingDirectory = basedir;
 219  
             }
 220  
 
 221  0
             if (!workingDirectory.exists()) {
 222  0
                 getLog().debug("Making working directory '" + workingDirectory.getAbsolutePath() + "'.");
 223  0
                 if (!workingDirectory.mkdirs()) {
 224  0
                     throw new MojoExecutionException("Could not make working directory: '"
 225  
                             + workingDirectory.getAbsolutePath() + "'");
 226  
                 }
 227  
             }
 228  
 
 229  0
             CommandLine commandLine = getExecutablePath(enviro, workingDirectory);
 230  
 
 231  0
             String[] args = new String[commandArguments.size()];
 232  0
             for (int i = 0; i < commandArguments.size(); i++) {
 233  0
                 args[i] = (String) commandArguments.get(i);
 234  
             }
 235  
 
 236  0
             commandLine.addArguments(args, false);
 237  
 
 238  0
             Executor exec = getExecutor();
 239  
 
 240  0
             exec.setWorkingDirectory(workingDirectory);
 241  
 
 242  
             // this code ensures the output gets logged vai maven logging, but at the same time prevents
 243  
             // partial line output, like input prompts.
 244  
             // final Log outputLog = getExecOutputLog();
 245  
             // LogOutputStream stdout = new LogOutputStream()
 246  
             // {
 247  
             // protected void processLine( String line, int level )
 248  
             // {
 249  
             // outputLog.info( line );
 250  
             // }
 251  
             // };
 252  
             //
 253  
             // LogOutputStream stderr = new LogOutputStream()
 254  
             // {
 255  
             // protected void processLine( String line, int level )
 256  
             // {
 257  
             // outputLog.info( line );
 258  
             // }
 259  
             // };
 260  0
             OutputStream stdout = System.out;
 261  0
             OutputStream stderr = System.err;
 262  
 
 263  
             try {
 264  0
                 getLog().debug("Executing command line: " + commandLine);
 265  
 
 266  0
                 int resultCode = executeCommandLine(exec, commandLine, enviro, stdout, stderr);
 267  
 
 268  0
                 if (isResultCodeAFailure(resultCode)) {
 269  0
                     throw new MojoExecutionException("Result of " + commandLine + " execution is: '" + resultCode
 270  
                             + "'.");
 271  
                 }
 272  0
             } catch (ExecuteException e) {
 273  0
                 throw new MojoExecutionException("Command execution failed.", e);
 274  
 
 275  0
             } catch (IOException e) {
 276  0
                 throw new MojoExecutionException("Command execution failed.", e);
 277  0
             }
 278  
 
 279  0
             registerSourceRoots();
 280  0
         } catch (IOException e) {
 281  0
             throw new MojoExecutionException("I/O Error", e);
 282  0
         }
 283  0
     }
 284  
 
 285  
     protected Executor getExecutor() {
 286  0
         DefaultExecutor exec = new DefaultExecutor();
 287  
 
 288  0
         if (successCodes != null) {
 289  0
             int size = successCodes.size();
 290  0
             int[] exitValues = new int[size];
 291  0
             for (int i = 0; i < size; i++) {
 292  0
                 exitValues[i] = new Integer(successCodes.get(i) + "");
 293  
             }
 294  0
             exec.setExitValues(exitValues);
 295  
         }
 296  0
         return exec;
 297  
     }
 298  
 
 299  
     boolean isResultCodeAFailure(int result) {
 300  0
         if (successCodes == null || successCodes.size() == 0) {
 301  0
             return result != 0;
 302  
         }
 303  0
         for (Iterator it = successCodes.iterator(); it.hasNext();) {
 304  0
             int code = Integer.parseInt((String) it.next());
 305  0
             if (code == result) {
 306  0
                 return false;
 307  
             }
 308  0
         }
 309  0
         return true;
 310  
     }
 311  
 
 312  
     private boolean isLongClassPathArgument(String arg) {
 313  0
         return longClasspath && ("-classpath".equals(arg) || "-cp".equals(arg));
 314  
     }
 315  
 
 316  
     private Log getExecOutputLog() {
 317  0
         Log log = getLog();
 318  0
         if (outputFile != null) {
 319  
             try {
 320  0
                 if (!outputFile.getParentFile().exists() && !outputFile.getParentFile().mkdirs()) {
 321  0
                     getLog().warn("Could not create non existing parent directories for log file: " + outputFile);
 322  
                 }
 323  0
                 PrintStream stream = new PrintStream(new FileOutputStream(outputFile));
 324  
 
 325  0
                 log = new StreamLog(stream);
 326  0
             } catch (Exception e) {
 327  0
                 getLog().warn("Could not open " + outputFile + ". Using default log", e);
 328  0
             }
 329  
         }
 330  
 
 331  0
         return log;
 332  
     }
 333  
 
 334  
     /**
 335  
      * Compute the classpath from the specified Classpath. The computed classpath is based on the classpathScope. The
 336  
      * plugin cannot know from maven the phase it is executed in. So we have to depend on the user to tell us he wants
 337  
      * the scope in which the plugin is expected to be executed.
 338  
      * 
 339  
      * @param specifiedClasspath
 340  
      *            Non null when the user restricted the dependenceis, null otherwise (the default classpath will be
 341  
      *            used)
 342  
      * @return a platform specific String representation of the classpath
 343  
      */
 344  
     private String computeClasspathString(Classpath specifiedClasspath) {
 345  0
         List resultList = computeClasspath(specifiedClasspath);
 346  0
         StringBuffer theClasspath = new StringBuffer();
 347  
 
 348  0
         for (Iterator it = resultList.iterator(); it.hasNext();) {
 349  0
             String str = (String) it.next();
 350  0
             addToClasspath(theClasspath, str);
 351  0
         }
 352  
 
 353  0
         return theClasspath.toString();
 354  
     }
 355  
 
 356  
     /**
 357  
      * Compute the classpath from the specified Classpath. The computed classpath is based on the classpathScope. The
 358  
      * plugin cannot know from maven the phase it is executed in. So we have to depend on the user to tell us he wants
 359  
      * the scope in which the plugin is expected to be executed.
 360  
      * 
 361  
      * @param specifiedClasspath
 362  
      *            Non null when the user restricted the dependenceis, null otherwise (the default classpath will be
 363  
      *            used)
 364  
      * @return a list of class path elements
 365  
      */
 366  
     private List computeClasspath(Classpath specifiedClasspath) {
 367  0
         List artifacts = new ArrayList();
 368  0
         List theClasspathFiles = new ArrayList();
 369  0
         List resultList = new ArrayList();
 370  
 
 371  0
         collectProjectArtifactsAndClasspath(artifacts, theClasspathFiles);
 372  
 
 373  0
         if ((specifiedClasspath != null) && (specifiedClasspath.getDependencies() != null)) {
 374  0
             artifacts = filterArtifacts(artifacts, specifiedClasspath.getDependencies());
 375  
         }
 376  
 
 377  0
         for (Iterator it = theClasspathFiles.iterator(); it.hasNext();) {
 378  0
             File f = (File) it.next();
 379  0
             resultList.add(f.getAbsolutePath());
 380  0
         }
 381  
 
 382  0
         for (Iterator it = artifacts.iterator(); it.hasNext();) {
 383  0
             Artifact artifact = (Artifact) it.next();
 384  0
             getLog().debug("dealing with " + artifact);
 385  0
             resultList.add(artifact.getFile().getAbsolutePath());
 386  0
         }
 387  
 
 388  0
         return resultList;
 389  
     }
 390  
 
 391  
     private static void addToClasspath(StringBuffer theClasspath, String toAdd) {
 392  0
         if (theClasspath.length() > 0) {
 393  0
             theClasspath.append(File.pathSeparator);
 394  
         }
 395  0
         theClasspath.append(toAdd);
 396  0
     }
 397  
 
 398  
     private List filterArtifacts(List artifacts, Collection dependencies) {
 399  0
         AndArtifactFilter filter = new AndArtifactFilter();
 400  
 
 401  0
         filter.add(new IncludesArtifactFilter(new ArrayList(dependencies))); // gosh
 402  
 
 403  0
         List filteredArtifacts = new ArrayList();
 404  0
         for (Iterator it = artifacts.iterator(); it.hasNext();) {
 405  0
             Artifact artifact = (Artifact) it.next();
 406  0
             if (filter.include(artifact)) {
 407  0
                 getLog().debug("filtering in " + artifact);
 408  0
                 filteredArtifacts.add(artifact);
 409  
             }
 410  0
         }
 411  0
         return filteredArtifacts;
 412  
     }
 413  
 
 414  
     CommandLine getExecutablePath(Map enviro, File dir) {
 415  0
         File execFile = new File(executable);
 416  0
         String exec = null;
 417  0
         if (execFile.exists()) {
 418  0
             getLog().debug("Toolchains are ignored, 'executable' parameter is set to " + executable);
 419  0
             exec = execFile.getAbsolutePath();
 420  
         } else {
 421  0
             Toolchain tc = getToolchain();
 422  
 
 423  
             // if the file doesn't exist & toolchain is null, the exec is probably in the PATH...
 424  
             // we should probably also test for isFile and canExecute, but the second one is only
 425  
             // available in SDK 6.
 426  0
             if (tc != null) {
 427  0
                 getLog().info("Toolchain in exec-maven-plugin: " + tc);
 428  0
                 exec = tc.findTool(executable);
 429  
             } else {
 430  0
                 if (OS.isFamilyWindows()) {
 431  0
                     String ex = executable.indexOf(".") < 0 ? executable + ".bat" : executable;
 432  0
                     File f = new File(dir, ex);
 433  0
                     if (f.exists()) {
 434  0
                         exec = ex;
 435  
                     } else {
 436  
                         // now try to figure the path from PATH, PATHEXT env vars
 437  
                         // if bat file, wrap in cmd /c
 438  0
                         String path = (String) enviro.get("PATH");
 439  0
                         if (path != null) {
 440  0
                             String[] elems = StringUtils.split(path, File.pathSeparator);
 441  0
                             for (int i = 0; i < elems.length; i++) {
 442  0
                                 f = new File(new File(elems[i]), ex);
 443  0
                                 if (f.exists()) {
 444  0
                                     exec = ex;
 445  0
                                     break;
 446  
                                 }
 447  
                             }
 448  
                         }
 449  
                     }
 450  
                 }
 451  
             }
 452  
         }
 453  
 
 454  0
         if (exec == null) {
 455  0
             exec = executable;
 456  
         }
 457  
 
 458  
         CommandLine toRet;
 459  0
         if (OS.isFamilyWindows() && exec.toLowerCase(Locale.getDefault()).endsWith(".bat")) {
 460  0
             toRet = new CommandLine("cmd");
 461  0
             toRet.addArgument("/c");
 462  0
             toRet.addArgument(exec);
 463  
         } else {
 464  0
             toRet = new CommandLine(exec);
 465  
         }
 466  
 
 467  0
         return toRet;
 468  
     }
 469  
 
 470  
     // private String[] DEFAULT_PATH_EXT = new String[] {
 471  
     // .COM; .EXE; .BAT; .CMD; .VBS; .VBE; .JS; .JSE; .WSF; .WSH
 472  
     // ".COM", ".EXE", ".BAT", ".CMD"
 473  
     // };
 474  
 
 475  
     private static boolean isEmpty(String string) {
 476  0
         return string == null || string.length() == 0;
 477  
     }
 478  
 
 479  
     //
 480  
     // methods used for tests purposes - allow mocking and simulate automatic setters
 481  
     //
 482  
 
 483  
     protected int executeCommandLine(Executor exec, CommandLine commandLine, Map enviro, OutputStream out,
 484  
             OutputStream err) throws IOException {
 485  0
         exec.setStreamHandler(new PumpStreamHandler(out, err, System.in));
 486  0
         return exec.execute(commandLine, enviro);
 487  
     }
 488  
 
 489  
     void setExecutable(String executable) {
 490  0
         this.executable = executable;
 491  0
     }
 492  
 
 493  
     String getExecutable() {
 494  0
         return executable;
 495  
     }
 496  
 
 497  
     void setWorkingDirectory(String workingDir) {
 498  0
         setWorkingDirectory(new File(workingDir));
 499  0
     }
 500  
 
 501  
     void setWorkingDirectory(File workingDir) {
 502  0
         this.workingDirectory = workingDir;
 503  0
     }
 504  
 
 505  
     void setArguments(List arguments) {
 506  0
         this.arguments = arguments;
 507  0
     }
 508  
 
 509  
     void setBasedir(File basedir) {
 510  0
         this.basedir = basedir;
 511  0
     }
 512  
 
 513  
     void setProject(MavenProject project) {
 514  0
         this.project = project;
 515  0
     }
 516  
 
 517  
     protected String getSystemProperty(String key) {
 518  0
         return System.getProperty(key);
 519  
     }
 520  
 
 521  
     public void setSuccessCodes(List list) {
 522  0
         this.successCodes = list;
 523  0
     }
 524  
 
 525  
     public List getSuccessCodes() {
 526  0
         return successCodes;
 527  
     }
 528  
 
 529  
     private Toolchain getToolchain() {
 530  0
         Toolchain tc = null;
 531  
 
 532  
         try {
 533  0
             if (session != null) { // session is null in tests..
 534  0
                 ToolchainManager toolchainManager = (ToolchainManager) session.getContainer().lookup(
 535  
                         ToolchainManager.ROLE);
 536  
 
 537  0
                 if (toolchainManager != null) {
 538  0
                     tc = toolchainManager.getToolchainFromBuildContext("jdk", session);
 539  
                 }
 540  
             }
 541  0
         } catch (ComponentLookupException componentLookupException) {
 542  
             // just ignore, could happen in pre-2.0.9 builds..
 543  0
         }
 544  0
         return tc;
 545  
     }
 546  
 
 547  
     /**
 548  
      * Create a jar with just a manifest containing a Main-Class entry for SurefireBooter and a Class-Path entry for all
 549  
      * classpath elements. Copied from surefire (ForkConfiguration#createJar())
 550  
      * 
 551  
      * @param classPath
 552  
      *            List&lt;String> of all classpath elements.
 553  
      * @return
 554  
      * @throws IOException
 555  
      */
 556  
     private File createJar(List classPath, String mainClass) throws IOException {
 557  0
         File file = File.createTempFile("maven-exec", ".jar");
 558  0
         file.deleteOnExit();
 559  0
         FileOutputStream fos = new FileOutputStream(file);
 560  0
         JarOutputStream jos = new JarOutputStream(fos);
 561  0
         jos.setLevel(JarOutputStream.STORED);
 562  0
         JarEntry je = new JarEntry("META-INF/MANIFEST.MF");
 563  0
         jos.putNextEntry(je);
 564  
 
 565  0
         Manifest man = new Manifest();
 566  
 
 567  
         // we can't use StringUtils.join here since we need to add a '/' to
 568  
         // the end of directory entries - otherwise the jvm will ignore them.
 569  0
         String cp = "";
 570  0
         for (Iterator it = classPath.iterator(); it.hasNext();) {
 571  0
             String el = (String) it.next();
 572  
             // NOTE: if File points to a directory, this entry MUST end in '/'.
 573  0
             cp += UrlUtils.getURL(new File(el)).toExternalForm() + " ";
 574  0
         }
 575  
 
 576  0
         man.getMainAttributes().putValue("Manifest-Version", "1.0");
 577  0
         man.getMainAttributes().putValue("Class-Path", cp.trim());
 578  0
         man.getMainAttributes().putValue("Main-Class", mainClass);
 579  
 
 580  0
         man.write(jos);
 581  0
         jos.close();
 582  
 
 583  0
         return file;
 584  
     }
 585  
 }