/*
* Copyright 2009 the original author or authors.
*
* Licensed 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.gradle.foundation;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
This handles the work of loading gradle dynamically. Due to jar version issues,
you can't just load all jar files.
This does NOT require any system or environment variables to be set.
To use this, instantiate this, then call one of the initialize functions.
Now you can get the class loader to load whatever classes you like.
@author mhunsicker
*/
public class BootstrapLoader
{
private URLClassLoader libClassLoader;
public void initialize(File gradleHome, boolean bootStrapDebug) throws Exception {
initialize(ClassLoader.getSystemClassLoader().getParent(), gradleHome, false, true, bootStrapDebug);
}
/*
Call this to initialize gradle.
@param parentClassloader a parent class loader. Probably whatever class loader
is used by the caller.
@param gradleHome the root directory where gradle is installed. This
directory should have a 'bin' child directory.
@param useParentLastClassLoader true to use a class loader that will delegate
to the parent only if it can't find it locally. This
should only be true if you're trying to load gradle
dynamically from another application.
@param loadOpenAPI True to load the gradle open API, false not to.
If you're calling this from a tool using the OpenAPI,
then you've probably already loaded it, so pass in false
here, otherwise, pass in true.
@param bootStrapDebug true to output debug information about the loading
process.
@throws Exception if something goes wrong.
@author mhunsicker
*/
public void initialize(ClassLoader parentClassloader, File gradleHome, boolean useParentLastClassLoader,
boolean loadOpenAPI, boolean bootStrapDebug) throws Exception {
if (gradleHome == null || !gradleHome.exists()) {
throw new RuntimeException("Gradle home not defined!");
}
if (bootStrapDebug) {
System.out.println(
"Gradle Home is declared by system property gradle.home to: " + gradleHome.getAbsolutePath());
}
System.setProperty("gradle.home", gradleHome.getAbsolutePath());
List<URL> loggingJars = toUrl(getLoggingJars());
List<File> nonLoggingJarFiles = getNonLoggingJars();
removeUnwantedJarFiles(nonLoggingJarFiles, loadOpenAPI);
List<URL> nonLoggingJars = toUrl(nonLoggingJarFiles);
if (bootStrapDebug) {
System.out.println("Parent Classloader of new context classloader is: " + parentClassloader);
System.out.println("Adding the following files to new logging classloader: " + loggingJars);
System.out.println("Adding the following files to new lib classloader: " + nonLoggingJars);
}
URLClassLoader loggingClassLoader = new URLClassLoader(loggingJars.toArray(new URL[loggingJars.size()]),
parentClassloader);
if (useParentLastClassLoader) {
libClassLoader = new ParentLastClassLoader(nonLoggingJars.toArray(new URL[nonLoggingJars.size()]),
loggingClassLoader);
} else {
libClassLoader = new URLClassLoader(nonLoggingJars.toArray(new URL[nonLoggingJars.size()]),
loggingClassLoader);
}
if (bootStrapDebug) {
System.out.println("Logging class loader: " + loggingClassLoader);
System.out.println("Lib class loader: " + libClassLoader);
}
}
public static File[] getGradleHomeLibClasspath() {
File gradleHomeLib = new File(System.getProperty("gradle.home") + "/lib");
if (gradleHomeLib.isDirectory()) {
return gradleHomeLib.listFiles();
}
return new File[0];
}
public static List<File> getNonLoggingJars() {
List<File> pathElements = new ArrayList<File>();
for (File file : getGradleClasspath()) {
if (!isLogLib(file)) {
pathElements.add(file);
}
}
return pathElements;
}
public static List<File> getLoggingJars() {
List<File> pathElements = new ArrayList<File>();
for (File file : getGradleClasspath()) {
if (isLogLib(file)) {
pathElements.add(file);
}
}
return pathElements;
}
private static boolean isLogLib(File file) {
return file.getName().startsWith("logback") || file.getName().startsWith("slf4j");
}
public static List<File> getGradleClasspath() {
File customGradleBin = null;
List<File> pathElements = new ArrayList<File>();
if (System.getProperty("gradle.bootstrap.gradleBin") != null) {
customGradleBin = new File(System.getProperty("gradle.bootstrap.gradleBin"));
pathElements.add(customGradleBin);
}
for (File homeLibFile : getGradleHomeLibClasspath()) {
if (homeLibFile.isFile() && !(customGradleBin != null && homeLibFile.getName().startsWith("gradle-"))) {
pathElements.add(homeLibFile);
}
}
return pathElements;
}
/*
This removes unwanted jar files. At the time of this writing, we're only
interested in the open api jar.
@param nonLoggingJarFiles a list of jar files
@param loadOpenAPI true to keep the open api jar, false to remove it.
@author mhunsicker
*/
private void removeUnwantedJarFiles(List<File> nonLoggingJarFiles, boolean loadOpenAPI) {
if (loadOpenAPI) {
return;
}
Iterator<File> iterator = nonLoggingJarFiles.iterator();
while (iterator.hasNext()) {
File file = iterator.next();
if (file.getName().startsWith("gradle-open-api-")) {
iterator.remove();
}
}
}
/*
Call this to get the class loader you can use to load gradle classes.
@return a URLClassLoader
@author mhunsicker
*/
public URLClassLoader getClassLoader() { return libClassLoader; }
public Class load(String classPath) throws Exception {
return libClassLoader.loadClass(classPath);
}
private static List<URL> toUrl(List<File> files) throws MalformedURLException {
List<URL> result = new ArrayList<URL>();
for (File file : files) {
result.add(file.toURI().toURL());
}
return result;
}
}