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 * The location of a per-user library directory.
64 * <p>
65 * It's value is the concatenation of {@link #ANT_PRIVATEDIR}
66 * with {@link #ANT_PRIVATELIB}, with an appropriate file separator
67 * in between. For example, on Unix, it's <code>.ant/lib</code>.
68 */
69 public static final String USER_LIBDIR =
70 ANT_PRIVATEDIR + File.separatorChar + ANT_PRIVATELIB;
71
72 /**
73 * The startup class that is to be run.
74 * {@value}
75 */
76 public static final String MAIN_CLASS = "org.apache.tools.ant.Main";
77
78 /**
79 * System property with user home directory.
80 * {@value}
81 */
82 public static final String USER_HOMEDIR = "user.home";
83
84 /**
85 * System property with application classpath.
86 * {@value}
87 */
88 private static final String JAVA_CLASS_PATH = "java.class.path";
89
90 /**
91 * Exit code on trouble
92 */
93 protected static final int EXIT_CODE_ERROR = 2;
94
95 /**
96 * Entry point for starting command line Ant.
97 *
98 * @param args commandline arguments
99 */
100 public static void main(String[] args) {
101 int exitCode;
102 try {
103 Launcher launcher = new Launcher();
104 exitCode = launcher.run(args);
105 } catch (LaunchException e) {
106 exitCode = EXIT_CODE_ERROR;
107 System.err.println(e.getMessage());
108 } catch (Throwable t) {
109 exitCode = EXIT_CODE_ERROR;
110 t.printStackTrace(System.err);
111 }
112 if (exitCode != 0) {
113 System.exit(exitCode);
114 }
115 }
116
117
118 /**
119 * Add a CLASSPATH or -lib to lib path urls.
120 *
121 * @param path the classpath or lib path to add to the libPathULRLs
122 * @param getJars if true and a path is a directory, add the jars in
123 * the directory to the path urls
124 * @param libPathURLs the list of paths to add to
125 */
126 private void addPath(String path, boolean getJars, List libPathURLs)
127 throws MalformedURLException {
128 StringTokenizer tokenizer = new StringTokenizer(path, File.pathSeparator);
129 while (tokenizer.hasMoreElements()) {
130 String elementName = tokenizer.nextToken();
131 File element = new File(elementName);
132 if (elementName.indexOf("%") != -1 && !element.exists()) {
133 continue;
134 }
135 if (getJars && element.isDirectory()) {
136 // add any jars in the directory
137 URL[] dirURLs = Locator.getLocationURLs(element);
138 for (int j = 0; j < dirURLs.length; ++j) {
139 libPathURLs.add(dirURLs[j]);
140 }
141 }
142
143 libPathURLs.add(Locator.fileToURL(element));
144 }
145 }
146
147 /**
148 * Run the launcher to launch Ant.
149 *
150 * @param args the command line arguments
151 * @return an exit code. As the normal ant main calls exit when it ends,
152 * this is for handling failures at bind-time
153 * @exception MalformedURLException if the URLs required for the classloader
154 * cannot be created.
155 */
156 private int run(String[] args)
157 throws LaunchException, MalformedURLException {
158 String antHomeProperty = System.getProperty(ANTHOME_PROPERTY);
159 File antHome = null;
160
161 File sourceJar = Locator.getClassSource(getClass());
162 File jarDir = sourceJar.getParentFile();
163 String mainClassname = MAIN_CLASS;
164
165 if (antHomeProperty != null) {
166 antHome = new File(antHomeProperty);
167 }
168
169 if (antHome == null || !antHome.exists()) {
170 antHome = jarDir.getParentFile();
171 System.setProperty(ANTHOME_PROPERTY, antHome.getAbsolutePath());
172 }
173
174 if (!antHome.exists()) {
175 throw new LaunchException("Ant home is set incorrectly or "
176 + "ant could not be located");
177 }
178
179 List libPaths = new ArrayList();
180 String cpString = null;
181 List argList = new ArrayList();
182 String[] newArgs;
183 boolean noUserLib = false;
184 boolean noClassPath = false;
185
186 for (int i = 0; i < args.length; ++i) {
187 if (args[i].equals("-lib")) {
188 if (i == args.length - 1) {
189 throw new LaunchException("The -lib argument must "
190 + "be followed by a library location");
191 }
192 libPaths.add(args[++i]);
193 } else if (args[i].equals("-cp")) {
194 if (i == args.length - 1) {
195 throw new LaunchException("The -cp argument must "
196 + "be followed by a classpath expression");
197 }
198 if (cpString != null) {
199 throw new LaunchException("The -cp argument must "
200 + "not be repeated");
201 }
202 cpString = args[++i];
203 } else if (args[i].equals("--nouserlib") || args[i].equals("-nouserlib")) {
204 noUserLib = true;
205 } else if (args[i].equals("--noclasspath") || args[i].equals("-noclasspath")) {
206 noClassPath = true;
207 } else if (args[i].equals("-main")) {
208 if (i == args.length - 1) {
209 throw new LaunchException("The -main argument must "
210 + "be followed by a library location");
211 }
212 mainClassname = args[++i];
213 } else {
214 argList.add(args[i]);
215 }
216 }
217
218 //decide whether to copy the existing arg set, or
219 //build a new one from the list of all args excluding the special
220 //operations that only we handle
221 if (argList.size() == args.length) {
222 newArgs = args;
223 } else {
224 newArgs = (String[]) argList.toArray(new String[argList.size()]);
225 }
226
227 URL[] libURLs = getLibPathURLs(
228 noClassPath ? null : cpString, libPaths);
229 URL[] systemURLs = getSystemURLs(jarDir);
230 URL[] userURLs = noUserLib ? new URL[0] : getUserURLs();
231
232 URL[] jars = getJarArray(
233 libURLs, userURLs, systemURLs, Locator.getToolsJar());
234
235 // now update the class.path property
236 StringBuffer baseClassPath
237 = new StringBuffer(System.getProperty(JAVA_CLASS_PATH));
238 if (baseClassPath.charAt(baseClassPath.length() - 1)
239 == File.pathSeparatorChar) {
240 baseClassPath.setLength(baseClassPath.length() - 1);
241 }
242
243 for (int i = 0; i < jars.length; ++i) {
244 baseClassPath.append(File.pathSeparatorChar);
245 baseClassPath.append(Locator.fromURI(jars[i].toString()));
246 }
247
248 System.setProperty(JAVA_CLASS_PATH, baseClassPath.toString());
249
250 URLClassLoader loader = new URLClassLoader(jars);
251 Thread.currentThread().setContextClassLoader(loader);
252 Class mainClass = null;
253 int exitCode = 0;
254 try {
255 mainClass = loader.loadClass(mainClassname);
256 AntMain main = (AntMain) mainClass.newInstance();
257 main.startAnt(newArgs, null, null);
258 } catch (InstantiationException ex) {
259 System.err.println(
260 "Incompatible version of " + mainClassname + " detected");
261 File mainJar = Locator.getClassSource(mainClass);
262 System.err.println(
263 "Location of this class " + mainJar);
264 exitCode = EXIT_CODE_ERROR;
265 } catch (Throwable t) {
266 t.printStackTrace(System.err);
267 exitCode = EXIT_CODE_ERROR;
268 }
269 return exitCode;
270 }
271
272 /**
273 * Get the list of -lib enties and -cp entry into
274 * a URL array.
275 * @param cpString the classpath string
276 * @param libPaths the list of -lib entries.
277 * @return an array of URLs.
278 */
279 private URL[] getLibPathURLs(String cpString, List libPaths)
280 throws MalformedURLException {
281 List libPathURLs = new ArrayList();
282
283 if (cpString != null) {
284 addPath(cpString, false, libPathURLs);
285 }
286
287 for (Iterator i = libPaths.iterator(); i.hasNext();) {
288 String libPath = (String) i.next();
289 addPath(libPath, true, libPathURLs);
290 }
291
292 return (URL[]) libPathURLs.toArray(new URL[libPathURLs.size()]);
293 }
294
295 /**
296 * Get the jar files in ANT_HOME/lib.
297 * determine ant library directory for system jars: use property
298 * or default using location of ant-launcher.jar
299 */
300 private URL[] getSystemURLs(File antLauncherDir) throws MalformedURLException {
301 File antLibDir = null;
302 String antLibDirProperty = System.getProperty(ANTLIBDIR_PROPERTY);
303 if (antLibDirProperty != null) {
304 antLibDir = new File(antLibDirProperty);
305 }
306 if ((antLibDir == null) || !antLibDir.exists()) {
307 antLibDir = antLauncherDir;
308 System.setProperty(ANTLIBDIR_PROPERTY, antLibDir.getAbsolutePath());
309 }
310 return Locator.getLocationURLs(antLibDir);
311 }
312
313 /**
314 * Get the jar files in user.home/.ant/lib
315 */
316 private URL[] getUserURLs() throws MalformedURLException {
317 File userLibDir
318 = new File(System.getProperty(USER_HOMEDIR), USER_LIBDIR);
319
320 return Locator.getLocationURLs(userLibDir);
321 }
322
323 /**
324 * Combine the various jar sources into a single array of jars.
325 * @param libJars the jars specified in -lib command line options
326 * @param userJars the jars in ~/.ant/lib
327 * @param systemJars the jars in $ANT_HOME/lib
328 * @param toolsJar the tools.jar file
329 * @return a combined array
330 * @throws MalformedURLException if there is a problem.
331 */
332 private URL[] getJarArray (
333 URL[] libJars, URL[] userJars, URL[] systemJars, File toolsJar)
334 throws MalformedURLException {
335 int numJars = libJars.length + userJars.length + systemJars.length;
336 if (toolsJar != null) {
337 numJars++;
338 }
339 URL[] jars = new URL[numJars];
340 System.arraycopy(libJars, 0, jars, 0, libJars.length);
341 System.arraycopy(userJars, 0, jars, libJars.length, userJars.length);
342 System.arraycopy(systemJars, 0, jars, userJars.length + libJars.length,
343 systemJars.length);
344
345 if (toolsJar != null) {
346 jars[jars.length - 1] = Locator.fileToURL(toolsJar);
347 }
348 return jars;
349 }
350 }