/*
 * Copyright 2016 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.api.internal.tasks.compile;

import org.gradle.api.JavaVersion;
import org.gradle.internal.classloader.DefaultClassLoaderFactory;
import org.gradle.internal.classloader.FilteringClassLoader;
import org.gradle.internal.classloader.MutableURLClassLoader;
import org.gradle.internal.classpath.DefaultClassPath;
import org.gradle.internal.jvm.JavaInfo;
import org.gradle.internal.jvm.Jvm;
import org.gradle.internal.reflect.DirectInstantiator;

import javax.tools.JavaCompiler;
import java.io.File;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Subset replacement for {@link javax.tools.ToolProvider} that avoids the application class loader.
 */
public class JdkTools {

    // Copied from ToolProvider.defaultJavaCompilerName
    private static final String DEFAULT_COMPILER_IMPL_NAME = "com.sun.tools.javac.api.JavacTool";
    private static final AtomicReference<JdkTools> INSTANCE = new AtomicReference<JdkTools>();

    private final ClassLoader isolatedToolsLoader;

    public static JdkTools current() {
        JdkTools jdkTools = INSTANCE.get();
        if (jdkTools == null) {
            INSTANCE.compareAndSet(null, new JdkTools(Jvm.current()));
            jdkTools = INSTANCE.get();
        }
        return jdkTools;
    }

    JdkTools(JavaInfo javaInfo) {
        DefaultClassLoaderFactory defaultClassLoaderFactory = new DefaultClassLoaderFactory();
        JavaVersion javaVersion = Jvm.current().getJavaVersion();
        FilteringClassLoader filteringClassLoader = defaultClassLoaderFactory.createSystemFilteringClassLoader();
        if (!javaVersion.isJava9Compatible()) {
            File toolsJar = javaInfo.getToolsJar();
            if (toolsJar == null) {
                throw new IllegalStateException("Could not find tools.jar. Please check that "
                                                + javaInfo.getJavaHome().getAbsolutePath()
                                                + " contains a valid JDK installation.");
            }
            DefaultClassPath defaultClassPath = new DefaultClassPath(toolsJar);
            isolatedToolsLoader = new MutableURLClassLoader(filteringClassLoader, defaultClassPath.getAsURLs());
        } else {
            filteringClassLoader.allowPackage("com.sun.tools");
            isolatedToolsLoader = filteringClassLoader;
        }
    }

    public JavaCompiler getSystemJavaCompiler() {
        Class<?> compilerImplClass;
        try {
            compilerImplClass = isolatedToolsLoader.loadClass(DEFAULT_COMPILER_IMPL_NAME);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException("Could not load class '" + DEFAULT_COMPILER_IMPL_NAME);
        }
        return DirectInstantiator.instantiate(compilerImplClass.asSubclass(JavaCompiler.class));
    }
}

@@ -1124,16 +1124,68 @@
 o.File;%0A
+import java.lang.reflect.InvocationTargetException;%0A
 import j
@@ -1663,16 +1663,61 @@
 sLoader;
+%0A    private final boolean isJava9Compatible;
 %0A%0A    pu
@@ -2901,24 +2901,63 @@
 tAsURLs());%0A
+            isJava9Compatible = false;%0A
         %7D el
@@ -3077,24 +3077,62 @@
 lassLoader;%0A
+            isJava9Compatible = true;%0A
         %7D%0A  
@@ -3208,34 +3208,160 @@
 ?%3E c
-ompilerImplClass;%0A
+lazz;%0A        try %7B%0A            if (isJava9Compatible) %7B%0A                clazz = isolatedToolsLoader.loadClass(%22javax.tools.ToolProvider%22);%0A    
         
 try 
@@ -3348,32 +3348,36 @@
 %22);%0A            
+    
 try %7B%0A          
@@ -3378,33 +3378,477 @@
         
+        
-compilerImplClass
+return (JavaCompiler) clazz.getDeclaredMethod(%22getSystemJavaCompiler%22).invoke(null);%0A                %7D catch (IllegalAccessException e) %7B%0A                    cannotCreateJavaCompiler(e);%0A                %7D catch (InvocationTargetException e) %7B%0A                    cannotCreateJavaCompiler(e);%0A                %7D catch (NoSuchMethodException e) %7B%0A                    cannotCreateJavaCompiler(e);%0A                %7D%0A            %7D else %7B%0A                clazz
  = isola
@@ -3893,32 +3893,46 @@
 LER_IMPL_NAME);%0A
+            %7D%0A
         %7D catch 
@@ -4121,24 +4121,12 @@
 te(c
-ompilerImplClass
+lazz
 .asS
@@ -4160,11 +4160,160 @@
 );%0A    %7D
+%0A%0A    private void cannotCreateJavaCompiler(Exception e) %7B%0A        throw new IllegalStateException(%22Could not create system Java compiler%22, e);%0A    %7D
 %0A%7D%0A

Fix illegal access error when trying to instantiate the system compiler under a Jigsaw compatible JDK