langtools/src/java.compiler/share/classes/javax/tools/ToolProvider.java
changeset 36526 3b41f1c69604
parent 36156 9ff93012d1e3
child 37759 f0b5daef41b6
--- a/langtools/src/java.compiler/share/classes/javax/tools/ToolProvider.java	Tue Mar 15 13:48:30 2016 -0700
+++ b/langtools/src/java.compiler/share/classes/javax/tools/ToolProvider.java	Thu Mar 17 19:04:28 2016 +0000
@@ -25,10 +25,11 @@
 
 package javax.tools;
 
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
-import java.util.HashMap;
-import java.util.Map;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
 
 /**
  * Provides methods for locating tool providers, for example,
@@ -40,8 +41,8 @@
  */
 public class ToolProvider {
 
-    private static final String systemJavaCompilerName
-        = "com.sun.tools.javac.api.JavacTool";
+    private static final String systemJavaCompilerModule = "jdk.compiler";
+    private static final String systemJavaCompilerName   = "com.sun.tools.javac.api.JavacTool";
 
     /**
      * Returns the Java™ programming language compiler provided
@@ -52,13 +53,17 @@
      * {@linkplain java.nio.file.FileSystem filesystem}.</p>
      * @return the compiler provided with this platform or
      * {@code null} if no compiler is provided
+     * @implNote This implementation returns the compiler provided
+     * by the {@code jdk.compiler} module if that module is available,
+     * and null otherwise.
      */
     public static JavaCompiler getSystemJavaCompiler() {
-        return instance().getSystemTool(JavaCompiler.class, systemJavaCompilerName);
+        return getSystemTool(JavaCompiler.class,
+                systemJavaCompilerModule, systemJavaCompilerName);
     }
 
-    private static final String systemDocumentationToolName
-        = "jdk.javadoc.internal.api.JavadocTool";
+    private static final String systemDocumentationToolModule = "jdk.javadoc";
+    private static final String systemDocumentationToolName = "jdk.javadoc.internal.api.JavadocTool";
 
     /**
      * Returns the Java&trade; programming language documentation tool provided
@@ -69,9 +74,13 @@
      * {@linkplain java.nio.file.FileSystem filesystem}.</p>
      * @return the documentation tool provided with this platform or
      * {@code null} if no documentation tool is provided
+     * @implNote This implementation returns the tool provided
+     * by the {@code jdk.javadoc} module if that module is available,
+     * and null otherwise.
      */
     public static DocumentationTool getSystemDocumentationTool() {
-        return instance().getSystemTool(DocumentationTool.class, systemDocumentationToolName);
+        return getSystemTool(DocumentationTool.class,
+                systemDocumentationToolModule, systemDocumentationToolName);
     }
 
     /**
@@ -87,41 +96,70 @@
         return ClassLoader.getSystemClassLoader();
     }
 
-
-    private static ToolProvider instance;
+    private static final boolean useLegacy;
 
-    private static synchronized ToolProvider instance() {
-        if (instance == null)
-            instance = new ToolProvider();
-        return instance;
+    static {
+        Class<?> c = null;
+        try {
+            c = Class.forName("java.lang.reflect.Module");
+        } catch (Throwable t) {
+        }
+        useLegacy = (c == null);
     }
 
-    // Cache for tool classes.
-    // Use weak references to avoid keeping classes around unnecessarily
-    private final Map<String, Reference<Class<?>>> toolClasses = new HashMap<>();
-
-    private ToolProvider() { }
+    /**
+     * Get an instance of a system tool using the service loader.
+     * @implNote         By default, this returns the implementation in the specified module.
+     *                   For limited backward compatibility, if this code is run on an older version
+     *                   of the Java platform that does not support modules, this method will
+     *                   try and create an instance of the named class. Note that implies the
+     *                   class must be available on the system class path.
+     * @param <T>        the interface of the tool
+     * @param clazz      the interface of the tool
+     * @param moduleName the name of the module containing the desired implementation
+     * @param className  the class name of the desired implementation
+     * @return the specified implementation of the tool
+     */
+    private static <T> T getSystemTool(Class<T> clazz, String moduleName, String className) {
+        if (useLegacy) {
+            try {
+                return Class.forName(className, true, ClassLoader.getSystemClassLoader()).asSubclass(clazz).newInstance();
+            } catch (ReflectiveOperationException e) {
+                throw new Error(e);
+            }
+        }
 
-    private <T> T getSystemTool(Class<T> clazz, String name) {
-        Class<? extends T> c = getSystemToolClass(clazz, name);
         try {
-            return c.asSubclass(clazz).newInstance();
-        } catch (InstantiationException | IllegalAccessException | RuntimeException | Error e) {
+            ServiceLoader<T> sl = ServiceLoader.load(clazz, ClassLoader.getSystemClassLoader());
+            for (Iterator<T> iter = sl.iterator(); iter.hasNext(); ) {
+                T tool = iter.next();
+                if (matches(tool, moduleName))
+                    return tool;
+            }
+        } catch (ServiceConfigurationError e) {
             throw new Error(e);
         }
+        return null;
     }
 
-    private <T> Class<? extends T> getSystemToolClass(Class<T> clazz, String name) {
-        Reference<Class<?>> refClass = toolClasses.get(name);
-        Class<?> c = (refClass == null ? null : refClass.get());
-        if (c == null) {
-            try {
-                c = Class.forName(name, false, ClassLoader.getSystemClassLoader());
-            } catch (ClassNotFoundException | RuntimeException | Error e) {
-                throw new Error(e);
-            }
-            toolClasses.put(name, new WeakReference<>(c));
+    /**
+     * Determine if this is tho desired tool instance.
+     * @param <T>        the interface of the tool
+     * @param tool       the instance of the tool
+     * @param moduleName the name of the module containing the desired implementation
+     * @return true if and only if the tool matches the specified criteria
+     */
+    private static <T> boolean matches(T tool, String moduleName) {
+        // for now, use reflection to implement
+        //      return moduleName.equals(tool.getClass().getModule().getName());
+        try {
+            Method getModuleMethod = Class.class.getDeclaredMethod("getModule");
+            Object toolModule = getModuleMethod.invoke(tool.getClass());
+            Method getNameMethod = toolModule.getClass().getDeclaredMethod("getName");
+            String toolModuleName = (String) getNameMethod.invoke(toolModule);
+            return moduleName.equals(toolModuleName);
+        } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
+            return false;
         }
-        return c.asSubclass(clazz);
     }
 }