/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.tools.ant.launch;
import java.io.
File;
import java.net.
MalformedURLException;
import java.net.
URL;
import java.net.
URLClassLoader;
import java.util.
ArrayList;
import java.util.
List;
import java.util.
StringTokenizer;
/**
* This is a launcher for Ant.
*
* @since Ant 1.6
*/
public class
Launcher {
/**
* The Ant Home (installation) Directory property.
* {@value}
*/
public static final
String ANTHOME_PROPERTY = "ant.home";
/**
* The Ant Library Directory property.
* {@value}
*/
public static final
String ANTLIBDIR_PROPERTY = "ant.library.dir";
/**
* The directory name of the per-user ant directory.
* {@value}
*/
public static final
String ANT_PRIVATEDIR = ".ant";
/**
* The name of a per-user library directory.
* {@value}
*/
public static final
String ANT_PRIVATELIB = "lib";
/**
* The location of a per-user library directory.
* <p>It's value is the concatenation of {@link #ANT_PRIVATEDIR}
* with {@link #ANT_PRIVATELIB}, with an appropriate file separator
* in between. For example, on Unix, it's <code>.ant/lib</code>.</p>
*/
public static final
String USER_LIBDIR =
ANT_PRIVATEDIR +
File.
separatorChar +
ANT_PRIVATELIB;
/**
* The startup class that is to be run.
* {@value}
*/
public static final
String MAIN_CLASS = "org.apache.tools.ant.Main";
/**
* System property with user home directory.
* {@value}
*/
public static final
String USER_HOMEDIR = "user.home";
/**
* System property with application classpath.
* {@value}
*/
private static final
String JAVA_CLASS_PATH = "java.class.path";
/**
* Exit code on trouble
*/
protected static final int
EXIT_CODE_ERROR = 2;
/**
* Entry point for starting command line Ant.
*
* @param args commandline arguments
*/
public static void
main(final
String[]
args) {
int
exitCode;
boolean
launchDiag = false;
try {
final
Launcher launcher = new
Launcher();
exitCode =
launcher.
run(
args);
launchDiag =
launcher.
launchDiag;
} catch (final
LaunchException e) {
exitCode =
EXIT_CODE_ERROR;
System.
err.
println(
e.
getMessage());
} catch (final
Throwable t) {
exitCode =
EXIT_CODE_ERROR;
t.
printStackTrace(
System.
err); //NOSONAR
}
if (
exitCode != 0) {
if (
launchDiag) {
System.
out.
println("Exit code: " +
exitCode);
}
System.
exit(
exitCode);
}
}
/**
* launch diagnostics flag; for debugging trouble at launch time.
*/
public boolean
launchDiag = false;
private
Launcher() {
}
/**
* Add a CLASSPATH or -lib to lib path urls.
* Only filesystem resources are supported.
*
* @param path the classpath or lib path to add to the libPathULRLs
* @param getJars if true and a path is a directory, add the jars in
* the directory to the path urls
* @param libPathURLs the list of paths to add to
* @throws MalformedURLException if we can't create a URL
*/
private void
addPath(final
String path, final boolean
getJars, final
List<
URL>
libPathURLs)
throws
MalformedURLException {
final
StringTokenizer tokenizer = new
StringTokenizer(
path,
File.
pathSeparator);
while (
tokenizer.
hasMoreElements()) {
final
String elementName =
tokenizer.
nextToken();
final
File element = new
File(
elementName);
if (
elementName.
indexOf('%') != -1 && !
element.
exists()) {
continue;
}
if (
getJars &&
element.
isDirectory()) {
// add any jars in the directory
final
URL[]
dirURLs =
Locator.
getLocationURLs(
element);
for (int
j = 0;
j <
dirURLs.length; ++
j) {
if (
launchDiag) {
System.
out.
println("adding library JAR: " +
dirURLs[
j]);}
libPathURLs.
add(
dirURLs[
j]);
}
}
final
URL url = new
URL(
element.
toURI().
toASCIIString());
if (
launchDiag) {
System.
out.
println("adding library URL: " +
url);
}
libPathURLs.
add(
url);
}
}
/**
* Run the launcher to launch Ant.
*
* @param args the command line arguments
* @return an exit code. As the normal ant main calls exit when it ends,
* this is for handling failures at bind-time
* @throws MalformedURLException if the URLs required for the classloader
* cannot be created.
* @throws LaunchException for launching problems
*/
private int
run(final
String[]
args)
throws
LaunchException,
MalformedURLException {
final
String antHomeProperty =
System.
getProperty(
ANTHOME_PROPERTY);
File antHome = null;
final
File sourceJar =
Locator.
getClassSource(
getClass());
final
File jarDir =
sourceJar.
getParentFile();
String mainClassname =
MAIN_CLASS;
if (
antHomeProperty != null) {
antHome = new
File(
antHomeProperty);
}
if (
antHome == null || !
antHome.
exists()) {
antHome =
jarDir.
getParentFile();
setProperty(
ANTHOME_PROPERTY,
antHome.
getAbsolutePath());
}
if (!
antHome.
exists()) {
throw new
LaunchException(
"Ant home is set incorrectly or ant could not be located (estimated value="
+
antHome.
getAbsolutePath() + ")");
}
final
List<
String>
libPaths = new
ArrayList<>();
String cpString = null;
final
List<
String>
argList = new
ArrayList<>();
String[]
newArgs;
boolean
noUserLib = false;
boolean
noClassPath = false;
for (int
i = 0;
i <
args.length; ++
i) {
if ("-lib".
equals(
args[
i])) {
if (
i ==
args.length - 1) {
throw new
LaunchException(
"The -lib argument must be followed by a library location");
}
libPaths.
add(
args[++
i]);
} else if ("-cp".
equals(
args[
i])) {
if (
i ==
args.length - 1) {
throw new
LaunchException(
"The -cp argument must be followed by a classpath expression");
}
if (
cpString != null) {
throw new
LaunchException(
"The -cp argument must not be repeated");
}
cpString =
args[++
i];
} else if ("--nouserlib".
equals(
args[
i]) || "-nouserlib".
equals(
args[
i])) {
noUserLib = true;
} else if ("--launchdiag".
equals(
args[
i])) {
launchDiag = true;
} else if ("--noclasspath".
equals(
args[
i]) || "-noclasspath".
equals(
args[
i])) {
noClassPath = true;
} else if ("-main".
equals(
args[
i])) {
if (
i ==
args.length - 1) {
throw new
LaunchException(
"The -main argument must be followed by a library location");
}
mainClassname =
args[++
i];
} else {
argList.
add(
args[
i]);
}
}
logPath("Launcher JAR",
sourceJar);
logPath("Launcher JAR directory",
sourceJar.
getParentFile());
logPath("java.home", new
File(
System.
getProperty("java.home")));
//decide whether to copy the existing arg set, or
//build a new one from the list of all args excluding the special
//operations that only we handle
if (
argList.
size() ==
args.length) {
newArgs =
args;
} else {
newArgs =
argList.
toArray(new
String[
argList.
size()]);
}
final
URL[]
libURLs =
getLibPathURLs(
noClassPath ? null :
cpString,
libPaths);
final
URL[]
systemURLs =
getSystemURLs(
jarDir);
final
URL[]
userURLs =
noUserLib ? new
URL[0] :
getUserURLs();
final
File toolsJAR =
Locator.
getToolsJar();
logPath("tools.jar",
toolsJAR);
final
URL[]
jars =
getJarArray(
libURLs,
userURLs,
systemURLs,
toolsJAR);
// now update the class.path property
final
StringBuilder baseClassPath
= new
StringBuilder(
System.
getProperty(
JAVA_CLASS_PATH));
if (
baseClassPath.
charAt(
baseClassPath.
length() - 1)
==
File.
pathSeparatorChar) {
baseClassPath.
setLength(
baseClassPath.
length() - 1);
}
for (int
i = 0;
i <
jars.length; ++
i) {
baseClassPath.
append(
File.
pathSeparatorChar);
baseClassPath.
append(
Locator.
fromURI(
jars[
i].
toString()));
}
setProperty(
JAVA_CLASS_PATH,
baseClassPath.
toString());
final
URLClassLoader loader = new
URLClassLoader(
jars,
Launcher.class.
getClassLoader());
Thread.
currentThread().
setContextClassLoader(
loader);
Class<? extends
AntMain>
mainClass = null;
int
exitCode = 0;
Throwable thrown = null;
try {
mainClass =
loader.
loadClass(
mainClassname).
asSubclass(
AntMain.class);
final
AntMain main =
mainClass.
newInstance();
main.
startAnt(
newArgs, null, null);
} catch (final
InstantiationException ex) {
System.
err.
println(
"Incompatible version of " +
mainClassname + " detected");
final
File mainJar =
Locator.
getClassSource(
mainClass);
System.
err.
println(
"Location of this class " +
mainJar);
thrown =
ex;
} catch (final
ClassNotFoundException cnfe) {
System.
err.
println(
"Failed to locate" +
mainClassname);
thrown =
cnfe;
} catch (final
Throwable t) {
t.
printStackTrace(
System.
err); //NOSONAR
thrown =
t;
}
if (
thrown != null) {
System.
err.
println(
ANTHOME_PROPERTY + ": " +
antHome.
getAbsolutePath());
System.
err.
println("Classpath: " +
baseClassPath.
toString());
System.
err.
println("Launcher JAR: " +
sourceJar.
getAbsolutePath());
System.
err.
println("Launcher Directory: " +
jarDir.
getAbsolutePath());
exitCode =
EXIT_CODE_ERROR;
}
return
exitCode;
}
/**
* Get the list of -lib entries and -cp entry into
* a URL array.
* @param cpString the classpath string
* @param libPaths the list of -lib entries.
* @return an array of URLs.
* @throws MalformedURLException if the URLs cannot be created.
*/
private
URL[]
getLibPathURLs(final
String cpString, final
List<
String>
libPaths)
throws
MalformedURLException {
final
List<
URL>
libPathURLs = new
ArrayList<>();
if (
cpString != null) {
addPath(
cpString, false,
libPathURLs);
}
for (final
String libPath :
libPaths) {
addPath(
libPath, true,
libPathURLs);
}
return
libPathURLs.
toArray(new
URL[
libPathURLs.
size()]);
}
/**
* Get the jar files in ANT_HOME/lib.
* determine ant library directory for system jars: use property
* or default using location of ant-launcher.jar
* @param antLauncherDir the dir that ant-launcher ran from
* @return the URLs
* @throws MalformedURLException if the URLs cannot be created.
*/
private
URL[]
getSystemURLs(final
File antLauncherDir) throws
MalformedURLException {
File antLibDir = null;
final
String antLibDirProperty =
System.
getProperty(
ANTLIBDIR_PROPERTY);
if (
antLibDirProperty != null) {
antLibDir = new
File(
antLibDirProperty);
}
if ((
antLibDir == null) || !
antLibDir.
exists()) {
antLibDir =
antLauncherDir;
setProperty(
ANTLIBDIR_PROPERTY,
antLibDir.
getAbsolutePath());
}
return
Locator.
getLocationURLs(
antLibDir);
}
/**
* Get the jar files in user.home/.ant/lib
* @return the URLS from the user's lib dir
* @throws MalformedURLException if the URLs cannot be created.
*/
private
URL[]
getUserURLs() throws
MalformedURLException {
final
File userLibDir
= new
File(
System.
getProperty(
USER_HOMEDIR),
USER_LIBDIR);
return
Locator.
getLocationURLs(
userLibDir);
}
/**
* Combine the various jar sources into a single array of jars.
* @param libJars the jars specified in -lib command line options
* @param userJars the jars in ~/.ant/lib
* @param systemJars the jars in $ANT_HOME/lib
* @param toolsJar the tools.jar file
* @return a combined array
* @throws MalformedURLException if there is a problem.
*/
private
URL[]
getJarArray(final
URL[]
libJars, final
URL[]
userJars,
final
URL[]
systemJars, final
File toolsJar)
throws
MalformedURLException {
int
numJars =
libJars.length +
userJars.length +
systemJars.length;
if (
toolsJar != null) {
numJars++;
}
final
URL[]
jars = new
URL[
numJars];
System.
arraycopy(
libJars, 0,
jars, 0,
libJars.length);
System.
arraycopy(
userJars, 0,
jars,
libJars.length,
userJars.length);
System.
arraycopy(
systemJars, 0,
jars,
userJars.length +
libJars.length,
systemJars.length);
if (
toolsJar != null) {
jars[
jars.length - 1] = new
URL(
toolsJar.
toURI().
toASCIIString());
}
return
jars;
}
/**
* set a system property, optionally log what is going on
* @param name property name
* @param value value
*/
private void
setProperty(final
String name, final
String value) {
if (
launchDiag) {
System.
out.
println("Setting \"" +
name + "\" to \"" +
value + "\"");
}
System.
setProperty(
name,
value);
}
private void
logPath(final
String name, final
File path) {
if (
launchDiag) {
System.
out.
println(
name + "= \"" +
path + "\"");
}
}
}