/*
 * 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);
  }
}

@@ -1835,32 +1835,38 @@
  private static 
+final 
 ClassLoader clas
@@ -1872,16 +1872,38 @@
 ssLoader
+ = createClassLoader()
 ;%0A%0A  pri
@@ -2005,316 +2005,8 @@
     
-String classPath = System.getenv(%22BUCK_CLASSPATH%22);%0A    if (classPath == null) %7B%0A      throw new RuntimeException(%22BUCK_CLASSPATH not set%22);%0A    %7D%0A%0A    String mainClassName = args%5B0%5D;%0A    String%5B%5D remainingArgs = Arrays.copyOfRange(args, 1, args.length);%0A    classLoader = createClassLoader(classPath);%0A%0A    
 // S
@@ -2135,16 +2135,124 @@
 ssLoader
+);%0A%0A    String mainClassName = args%5B0%5D;%0A    String%5B%5D remainingArgs = Arrays.copyOfRange(args, 1, args.length
 );%0A%0A    
@@ -2682,16 +2682,24 @@
 sLoader(
+) %7B%0A    
 String c
@@ -2710,40 +2710,139 @@
 Path
-) throws MalformedURLException %7B
+ = System.getenv(%22BUCK_CLASSPATH%22);%0A    if (classPath == null) %7B%0A      throw new RuntimeException(%22BUCK_CLASSPATH not set%22);%0A    %7D%0A
 %0A   
@@ -2980,24 +2980,38 @@
 gth; i++) %7B%0A
+      try %7B%0A  
       urls%5Bi
@@ -3053,16 +3053,105 @@
 oURL();%0A
+      %7D catch (MalformedURLException e) %7B%0A        throw new RuntimeException(e);%0A      %7D%0A
     %7D%0A%0A 

Create ClassLoader earlier