/*
 * Copyright 2014-present Facebook, Inc.
 *
 * 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 com.facebook.buck.cli.bootstrapper;

import java.io.File;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.util.Arrays;

/**
 * This class sets up a separate ClassLoader for most of Buck's implementation, leaving only the
 * bare minimum bootstrapping classes (and a few classes for compatibility with library code that is
 * not ClassLoader-aware) in the system ClassLoader. This is done so that annotation processors do
 * not have their classpaths polluted with Buck's dependencies when Buck is compiling Java code
 * in-process.
 *
 * <p>Under JSR-199, when the Java compiler is run in-process it uses a ClassLoader that is a child
 * of the system ClassLoader. In order for annotation processors to access the Compiler Tree API
 * (which lives in tools.jar with the compiler itself), they must be loaded with a ClassLoader
 * descended from the compiler's. If Buck used the system ClassLoader as a normal Java application
 * would, this would result in annotation processors getting Buck's versions of Guava, Jackson, etc.
 * instead of their own.
 */
public final class ClassLoaderBootstrapper {
  private static ClassLoader classLoader;

  private ClassLoaderBootstrapper() {}

  public static void main(String[] args) throws Exception {
    String classPath = System.getenv("BUCK_CLASSPATH");
    if (classPath == null) {
      throw new RuntimeException("BUCK_CLASSPATH not set");
    }

    String mainClassName = args[0];
    String[] remainingArgs = Arrays.copyOfRange(args, 1, args.length);
    classLoader = createClassLoader(classPath);

    // Some things (notably Jetty) use the context class loader to load stuff
    Thread.currentThread().setContextClassLoader(classLoader);

    Class<?> mainClass = classLoader.loadClass(mainClassName);
    Method mainMethod = mainClass.getMethod("main", String[].class);
    mainMethod.invoke(null, (Object) remainingArgs);
  }

  public static Class<?> loadClass(String name) {
    try {
      return classLoader.loadClass(name);
    } catch (ClassNotFoundException e) {
      throw new NoClassDefFoundError(name);
    }
  }

  private static ClassLoader createClassLoader(String classPath) throws MalformedURLException {
    String[] strings = classPath.split(File.pathSeparator);
    URL[] urls = new URL[strings.length];
    for (int i = 0; i < urls.length; i++) {
      urls[i] = Paths.get(strings[i]).toUri().toURL();
    }

    return new URLClassLoader(urls);
  }
}

/*
 * Copyright 2014-present Facebook, Inc.
 *
 * 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 com.facebook.buck.cli.bootstrapper;

import java.io.File;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.util.Arrays;

/**
 * This class sets up a separate ClassLoader for most of Buck's implementation, leaving only the
 * bare minimum bootstrapping classes (and a few classes for compatibility with library code that is
 * not ClassLoader-aware) in the system ClassLoader. This is done so that annotation processors do
 * not have their classpaths polluted with Buck's dependencies when Buck is compiling Java code
 * in-process.
 *
 * <p>Under JSR-199, when the Java compiler is run in-process it uses a ClassLoader that is a child
 * of the system ClassLoader. In order for annotation processors to access the Compiler Tree API
 * (which lives in tools.jar with the compiler itself), they must be loaded with a ClassLoader
 * descended from the compiler's. If Buck used the system ClassLoader as a normal Java application
 * would, this would result in annotation processors getting Buck's versions of Guava, Jackson, etc.
 * instead of their own.
 */
public final class ClassLoaderBootstrapper {
  private static final ClassLoader classLoader = createClassLoader();

  private ClassLoaderBootstrapper() {}

  public static void main(String[] args) throws Exception {
    // Some things (notably Jetty) use the context class loader to load stuff
    Thread.currentThread().setContextClassLoader(classLoader);

    String mainClassName = args[0];
    String[] remainingArgs = Arrays.copyOfRange(args, 1, args.length);

    Class<?> mainClass = classLoader.loadClass(mainClassName);
    Method mainMethod = mainClass.getMethod("main", String[].class);
    mainMethod.invoke(null, (Object) remainingArgs);
  }

  public static Class<?> loadClass(String name) {
    try {
      return classLoader.loadClass(name);
    } catch (ClassNotFoundException e) {
      throw new NoClassDefFoundError(name);
    }
  }

  private static ClassLoader createClassLoader() {
    String classPath = System.getenv("BUCK_CLASSPATH");
    if (classPath == null) {
      throw new RuntimeException("BUCK_CLASSPATH not set");
    }

    String[] strings = classPath.split(File.pathSeparator);
    URL[] urls = new URL[strings.length];
    for (int i = 0; i < urls.length; i++) {
      try {
        urls[i] = Paths.get(strings[i]).toUri().toURL();
      } catch (MalformedURLException e) {
        throw new RuntimeException(e);
      }
    }

    return new URLClassLoader(urls);
  }
}

Create ClassLoader earlier