1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the 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
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18 package org.apache.tools.ant.launch;
19
20 import java.net.URL;
21 import java.net.URLClassLoader;
22 import java.net.MalformedURLException;
23 import java.io.File;
24 import java.util.StringTokenizer;
25 import java.util.List;
26 import java.util.ArrayList;
27 import java.util.Iterator;
28
29
30
31 /**
32 * This is a launcher for Ant.
33 *
34 * @since Ant 1.6
35 */
36 public class Launcher {
37
38 /**
39 * The Ant Home (installation) Directory property.
40 * {@value}
41 */
42 public static final String ANTHOME_PROPERTY = "ant.home";
43
44 /**
45 * The Ant Library Directory property.
46 * {@value}
47 */
48 public static final String ANTLIBDIR_PROPERTY = "ant.library.dir";
49
50 /**
51 * The directory name of the per-user ant directory.
52 * {@value}
53 */
54 public static final String ANT_PRIVATEDIR = ".ant";
55
56 /**
57 * The name of a per-user library directory.
58 * {@value}
59 */
60 public static final String ANT_PRIVATELIB = "lib";
61
62 /**
63 * launch diagnostics flag; for debugging trouble at launch time.
64 */
65 public static boolean launchDiag = false;
66
67 /**
68 * The location of a per-user library directory.
69 * <p>
70 * It's value is the concatenation of {@link #ANT_PRIVATEDIR}
71 * with {@link #ANT_PRIVATELIB}, with an appropriate file separator
72 * in between. For example, on Unix, it's <code>.ant/lib</code>.
73 */
74 public static final String USER_LIBDIR =
75 ANT_PRIVATEDIR + File.separatorChar + ANT_PRIVATELIB;
76
77 /**
78 * The startup class that is to be run.
79 * {@value}
80 */
81 public static final String MAIN_CLASS = "org.apache.tools.ant.Main";
82
83 /**
84 * System property with user home directory.
85 * {@value}
86 */
87 public static final String USER_HOMEDIR = "user.home";
88
89 /**
90 * System property with application classpath.
91 * {@value}
92 */
93 private static final String JAVA_CLASS_PATH = "java.class.path";
94
95 /**
96 * Exit code on trouble
97 */
98 protected static final int EXIT_CODE_ERROR = 2;
99
100 /**
101 * Entry point for starting command line Ant.
102 *
103 * @param args commandline arguments
104 */
105 public static void main(String[] args) {
106 int exitCode;
107 try {
108 Launcher launcher = new Launcher();
109 exitCode = launcher.run(args);
110 } catch (LaunchException e) {
111 exitCode = EXIT_CODE_ERROR;
112 System.err.println(e.getMessage());
113 } catch (Throwable t) {
114 exitCode = EXIT_CODE_ERROR;
115 t.printStackTrace(System.err);
116 }
117 if (exitCode != 0) {
118 if (launchDiag) {
119 System.out.println("Exit code: "+exitCode);
120 }
121 System.exit(exitCode);
122 }
123 }
124
125
126 /**
127 * Add a CLASSPATH or -lib to lib path urls.
128 * Only filesystem resources are supported.
129 *
130 * @param path the classpath or lib path to add to the libPathULRLs
131 * @param getJars if true and a path is a directory, add the jars in
132 * the directory to the path urls
133 * @param libPathURLs the list of paths to add to
134 * @throws MalformedURLException if we can't create a URL
135 */
136 private void addPath(String path, boolean getJars, List libPathURLs)
137 throws MalformedURLException {
138 StringTokenizer tokenizer = new StringTokenizer(path, File.pathSeparator);
139 while (tokenizer.hasMoreElements()) {
140 String elementName = tokenizer.nextToken();
141 File element = new File(elementName);
142 if (elementName.indexOf('%') != -1 && !element.exists()) {
143 continue;
144 }
145 if (getJars && element.isDirectory()) {
146 // add any jars in the directory
147 URL[] dirURLs = Locator.getLocationURLs(element);
148 for (int j = 0; j < dirURLs.length; ++j) {
149 if (launchDiag) { System.out.println("adding library JAR: " + dirURLs[j]);}
150 libPathURLs.add(dirURLs[j]);
151 }
152 }
153
154 URL url = Locator.fileToURL(element);
155 if (launchDiag) { System.out.println("adding library URL: " + url) ;}
156 libPathURLs.add(url);
157 }
158 }
159
160 /**
161 * Run the launcher to launch Ant.
162 *
163 * @param args the command line arguments
164 * @return an exit code. As the normal ant main calls exit when it ends,
165 * this is for handling failures at bind-time
166 * @throws MalformedURLException if the URLs required for the classloader
167 * cannot be created.
168 * @throws LaunchException for launching problems
169 */
170 private int run(String[] args)
171 throws LaunchException, MalformedURLException {
172 String antHomeProperty = System.getProperty(ANTHOME_PROPERTY);
173 File antHome = null;
174
175 File sourceJar = Locator.getClassSource(getClass());
176 File jarDir = sourceJar.getParentFile();
177 String mainClassname = MAIN_CLASS;
178
179 if (antHomeProperty != null) {
180 antHome = new File(antHomeProperty);
181 }
182
183 if (antHome == null || !antHome.exists()) {
184 antHome = jarDir.getParentFile();
185 setProperty(ANTHOME_PROPERTY, antHome.getAbsolutePath());
186 }
187
188 if (!antHome.exists()) {
189 throw new LaunchException("Ant home is set incorrectly or "
190 + "ant could not be located (estimated value="+antHome.getAbsolutePath()+")");
191 }
192
193 List libPaths = new ArrayList();
194 String cpString = null;
195 List argList = new ArrayList();
196 String[] newArgs;
197 boolean noUserLib = false;
198 boolean noClassPath = false;
199
200 for (int i = 0; i < args.length; ++i) {
201 if (args[i].equals("-lib")) {
202 if (i == args.length - 1) {
203 throw new LaunchException("The -lib argument must "
204 + "be followed by a library location");
205 }
206 libPaths.add(args[++i]);
207 } else if (args[i].equals("-cp")) {
208 if (i == args.length - 1) {
209 throw new LaunchException("The -cp argument must "
210 + "be followed by a classpath expression");
211 }
212 if (cpString != null) {
213 throw new LaunchException("The -cp argument must "
214 + "not be repeated");
215 }
216 cpString = args[++i];
217 } else if (args[i].equals("--nouserlib") || args[i].equals("-nouserlib")) {
218 noUserLib = true;
219 } else if (args[i].equals("--launchdiag")) {
220 launchDiag = true;
221 } else if (args[i].equals("--noclasspath") || args[i].equals("-noclasspath")) {
222 noClassPath = true;
223 } else if (args[i].equals("-main")) {
224 if (i == args.length - 1) {
225 throw new LaunchException("The -main argument must "
226 + "be followed by a library location");
227 }
228 mainClassname = args[++i];
229 } else {
230 argList.add(args[i]);
231 }
232 }
233
234 logPath("Launcher JAR",sourceJar);
235 logPath("Launcher JAR directory", sourceJar.getParentFile());
236 logPath("java.home", new File(System.getProperty("java.home")));
237
238 //decide whether to copy the existing arg set, or
239 //build a new one from the list of all args excluding the special
240 //operations that only we handle
241 if (argList.size() == args.length) {
242 newArgs = args;
243 } else {
244 newArgs = (String[]) argList.toArray(new String[argList.size()]);
245 }
246
247 URL[] libURLs = getLibPathURLs(
248 noClassPath ? null : cpString, libPaths);
249 URL[] systemURLs = getSystemURLs(jarDir);
250 URL[] userURLs = noUserLib ? new URL[0] : getUserURLs();
251
252 File toolsJAR = Locator.getToolsJar();
253 logPath("tools.jar",toolsJAR);
254 URL[] jars = getJarArray(
255 libURLs, userURLs, systemURLs, toolsJAR);
256
257 // now update the class.path property
258 StringBuffer baseClassPath
259 = new StringBuffer(System.getProperty(JAVA_CLASS_PATH));
260 if (baseClassPath.charAt(baseClassPath.length() - 1)
261 == File.pathSeparatorChar) {
262 baseClassPath.setLength(baseClassPath.length() - 1);
263 }
264
265 for (int i = 0; i < jars.length; ++i) {
266 baseClassPath.append(File.pathSeparatorChar);
267 baseClassPath.append(Locator.fromURI(jars[i].toString()));
268 }
269
270 setProperty(JAVA_CLASS_PATH, baseClassPath.toString());
271
272 URLClassLoader loader = new URLClassLoader(jars);
273 Thread.currentThread().setContextClassLoader(loader);
274 Class mainClass = null;
275 int exitCode = 0;
276 Throwable thrown=null;
277 try {
278 mainClass = loader.loadClass(mainClassname);
279 AntMain main = (AntMain) mainClass.newInstance();
280 main.startAnt(newArgs, null, null);
281 } catch (InstantiationException ex) {
282 System.err.println(
283 "Incompatible version of " + mainClassname + " detected");
284 File mainJar = Locator.getClassSource(mainClass);
285 System.err.println(
286 "Location of this class " + mainJar);
287 thrown = ex;
288 } catch (ClassNotFoundException cnfe) {
289 System.err.println(
290 "Failed to locate" + mainClassname);
291 thrown = cnfe;
292 } catch (Throwable t) {
293 t.printStackTrace(System.err);
294 thrown=t;
295 }
296 if(thrown!=null) {
297 System.err.println(ANTHOME_PROPERTY+": "+antHome.getAbsolutePath());
298 System.err.println("Classpath: " + baseClassPath.toString());
299 System.err.println("Launcher JAR: " + sourceJar.getAbsolutePath());
300 System.err.println("Launcher Directory: " + jarDir.getAbsolutePath());
301 exitCode = EXIT_CODE_ERROR;
302 }
303 return exitCode;
304 }
305
306 /**
307 * Get the list of -lib entries and -cp entry into
308 * a URL array.
309 * @param cpString the classpath string
310 * @param libPaths the list of -lib entries.
311 * @return an array of URLs.
312 * @throws MalformedURLException if the URLs cannot be created.
313 */
314 private URL[] getLibPathURLs(String cpString, List libPaths)
315 throws MalformedURLException {
316 List libPathURLs = new ArrayList();
317
318 if (cpString != null) {
319 addPath(cpString, false, libPathURLs);
320 }
321
322 for (Iterator i = libPaths.iterator(); i.hasNext();) {
323 String libPath = (String) i.next();
324 addPath(libPath, true, libPathURLs);
325 }
326
327 return (URL[]) libPathURLs.toArray(new URL[libPathURLs.size()]);
328 }
329
330 /**
331 * Get the jar files in ANT_HOME/lib.
332 * determine ant library directory for system jars: use property
333 * or default using location of ant-launcher.jar
334 * @param antLauncherDir the dir that ant-launcher ran from
335 * @return the URLs
336 * @throws MalformedURLException if the URLs cannot be created.
337 */
338 private URL[] getSystemURLs(File antLauncherDir) throws MalformedURLException {
339 File antLibDir = null;
340 String antLibDirProperty = System.getProperty(ANTLIBDIR_PROPERTY);
341 if (antLibDirProperty != null) {
342 antLibDir = new File(antLibDirProperty);
343 }
344 if ((antLibDir == null) || !antLibDir.exists()) {
345 antLibDir = antLauncherDir;
346 setProperty(ANTLIBDIR_PROPERTY, antLibDir.getAbsolutePath());
347 }
348 return Locator.getLocationURLs(antLibDir);
349 }
350
351 /**
352 * Get the jar files in user.home/.ant/lib
353 * @return the URLS from the user's lib dir
354 * @throws MalformedURLException if the URLs cannot be created.
355 */
356 private URL[] getUserURLs() throws MalformedURLException {
357 File userLibDir
358 = new File(System.getProperty(USER_HOMEDIR), USER_LIBDIR);
359
360 return Locator.getLocationURLs(userLibDir);
361 }
362
363 /**
364 * Combine the various jar sources into a single array of jars.
365 * @param libJars the jars specified in -lib command line options
366 * @param userJars the jars in ~/.ant/lib
367 * @param systemJars the jars in $ANT_HOME/lib
368 * @param toolsJar the tools.jar file
369 * @return a combined array
370 * @throws MalformedURLException if there is a problem.
371 */
372 private URL[] getJarArray (
373 URL[] libJars, URL[] userJars, URL[] systemJars, File toolsJar)
374 throws MalformedURLException {
375 int numJars = libJars.length + userJars.length + systemJars.length;
376 if (toolsJar != null) {
377 numJars++;
378 }
379 URL[] jars = new URL[numJars];
380 System.arraycopy(libJars, 0, jars, 0, libJars.length);
381 System.arraycopy(userJars, 0, jars, libJars.length, userJars.length);
382 System.arraycopy(systemJars, 0, jars, userJars.length + libJars.length,
383 systemJars.length);
384
385 if (toolsJar != null) {
386 jars[jars.length - 1] = Locator.fileToURL(toolsJar);
387 }
388 return jars;
389 }
390
391 /**
392 * set a system property, optionally log what is going on
393 * @param name property name
394 * @param value value
395 */
396 private void setProperty(String name, String value) {
397 if (launchDiag) {
398 System.out.println("Setting \"" + name + "\" to \"" + value + "\"");
399 }
400 System.setProperty(name, value);
401 }
402
403 private void logPath(String name,File path) {
404 if(launchDiag) {
405 System.out.println(name+"= \""+path+"\"");
406 }
407 }
408 }