Thu, 26 Aug 2010 15:17:17 -0700
6604599: ToolProvider should be less compiler-specific
Reviewed-by: darcy
1.1 --- a/src/share/classes/javax/tools/ToolProvider.java Wed Aug 25 15:31:46 2010 -0700 1.2 +++ b/src/share/classes/javax/tools/ToolProvider.java Thu Aug 26 15:17:17 2010 -0700 1.3 @@ -26,10 +26,14 @@ 1.4 package javax.tools; 1.5 1.6 import java.io.File; 1.7 +import java.lang.ref.Reference; 1.8 +import java.lang.ref.WeakReference; 1.9 import java.net.URL; 1.10 import java.net.URLClassLoader; 1.11 import java.net.MalformedURLException; 1.12 +import java.util.HashMap; 1.13 import java.util.Locale; 1.14 +import java.util.Map; 1.15 import java.util.logging.Logger; 1.16 import java.util.logging.Level; 1.17 import static java.util.logging.Level.*; 1.18 @@ -44,8 +48,6 @@ 1.19 */ 1.20 public class ToolProvider { 1.21 1.22 - private ToolProvider() {} 1.23 - 1.24 private static final String propertyName = "sun.tools.ToolProvider"; 1.25 private static final String loggerName = "javax.tools"; 1.26 1.27 @@ -87,6 +89,9 @@ 1.28 return null; 1.29 } 1.30 1.31 + private static final String defaultJavaCompilerName 1.32 + = "com.sun.tools.javac.api.JavacTool"; 1.33 + 1.34 /** 1.35 * Gets the Java™ programming language compiler provided 1.36 * with this platform. 1.37 @@ -94,13 +99,7 @@ 1.38 * {@code null} if no compiler is provided 1.39 */ 1.40 public static JavaCompiler getSystemJavaCompiler() { 1.41 - if (Lazy.compilerClass == null) 1.42 - return trace(WARNING, "Lazy.compilerClass == null"); 1.43 - try { 1.44 - return Lazy.compilerClass.newInstance(); 1.45 - } catch (Throwable e) { 1.46 - return trace(WARNING, e); 1.47 - } 1.48 + return instance().getSystemTool(JavaCompiler.class, defaultJavaCompilerName); 1.49 } 1.50 1.51 /** 1.52 @@ -113,63 +112,109 @@ 1.53 * or {@code null} if no tools are provided 1.54 */ 1.55 public static ClassLoader getSystemToolClassLoader() { 1.56 - if (Lazy.compilerClass == null) 1.57 - return trace(WARNING, "Lazy.compilerClass == null"); 1.58 - return Lazy.compilerClass.getClassLoader(); 1.59 + try { 1.60 + Class<? extends JavaCompiler> c = 1.61 + instance().getSystemToolClass(JavaCompiler.class, defaultJavaCompilerName); 1.62 + return c.getClassLoader(); 1.63 + } catch (Throwable e) { 1.64 + return trace(WARNING, e); 1.65 + } 1.66 } 1.67 1.68 - /** 1.69 - * This class will not be initialized until one of the above 1.70 - * methods are called. This ensures that searching for the 1.71 - * compiler does not affect platform start up. 1.72 - */ 1.73 - static class Lazy { 1.74 - private static final String defaultJavaCompilerName 1.75 - = "com.sun.tools.javac.api.JavacTool"; 1.76 - private static final String[] defaultToolsLocation 1.77 - = { "lib", "tools.jar" }; 1.78 - static final Class<? extends JavaCompiler> compilerClass; 1.79 - static { 1.80 - Class<? extends JavaCompiler> c = null; 1.81 + 1.82 + private static ToolProvider instance; 1.83 + 1.84 + private static synchronized ToolProvider instance() { 1.85 + if (instance == null) 1.86 + instance = new ToolProvider(); 1.87 + return instance; 1.88 + } 1.89 + 1.90 + // Cache for tool classes. 1.91 + // Use weak references to avoid keeping classes around unnecessarily 1.92 + private Map<String, Reference<Class<?>>> toolClasses = new HashMap<String, Reference<Class<?>>>(); 1.93 + 1.94 + // Cache for tool classloader. 1.95 + // Use a weak reference to avoid keeping it around unnecessarily 1.96 + private Reference<ClassLoader> refToolClassLoader = null; 1.97 + 1.98 + 1.99 + private ToolProvider() { } 1.100 + 1.101 + private <T> T getSystemTool(Class<T> clazz, String name) { 1.102 + Class<? extends T> c = getSystemToolClass(clazz, name); 1.103 + try { 1.104 + return c.asSubclass(clazz).newInstance(); 1.105 + } catch (Throwable e) { 1.106 + trace(WARNING, e); 1.107 + return null; 1.108 + } 1.109 + } 1.110 + 1.111 + private <T> Class<? extends T> getSystemToolClass(Class<T> clazz, String name) { 1.112 + Reference<Class<?>> refClass = toolClasses.get(name); 1.113 + Class<?> c = (refClass == null ? null : refClass.get()); 1.114 + if (c == null) { 1.115 try { 1.116 - c = findClass().asSubclass(JavaCompiler.class); 1.117 - } catch (Throwable t) { 1.118 - trace(WARNING, t); 1.119 + c = findSystemToolClass(name); 1.120 + } catch (Throwable e) { 1.121 + return trace(WARNING, e); 1.122 } 1.123 - compilerClass = c; 1.124 + toolClasses.put(name, new WeakReference<Class<?>>(c)); 1.125 + } 1.126 + return c.asSubclass(clazz); 1.127 + } 1.128 + 1.129 + private static final String[] defaultToolsLocation = { "lib", "tools.jar" }; 1.130 + 1.131 + private Class<?> findSystemToolClass(String toolClassName) 1.132 + throws MalformedURLException, ClassNotFoundException 1.133 + { 1.134 + // try loading class directly, in case tool is on the bootclasspath 1.135 + try { 1.136 + return enableAsserts(Class.forName(toolClassName, false, null)); 1.137 + } catch (ClassNotFoundException e) { 1.138 + trace(FINE, e); 1.139 + 1.140 + // if tool not on bootclasspath, look in default tools location (tools.jar) 1.141 + ClassLoader cl = (refToolClassLoader == null ? null : refToolClassLoader.get()); 1.142 + if (cl == null) { 1.143 + File file = new File(System.getProperty("java.home")); 1.144 + if (file.getName().equalsIgnoreCase("jre")) 1.145 + file = file.getParentFile(); 1.146 + for (String name : defaultToolsLocation) 1.147 + file = new File(file, name); 1.148 + 1.149 + // if tools not found, no point in trying a URLClassLoader 1.150 + // so rethrow the original exception. 1.151 + if (!file.exists()) 1.152 + throw e; 1.153 + 1.154 + URL[] urls = { file.toURI().toURL() }; 1.155 + trace(FINE, urls[0].toString()); 1.156 + 1.157 + cl = URLClassLoader.newInstance(urls); 1.158 + cl.setPackageAssertionStatus("com.sun.tools.javac", true); 1.159 + refToolClassLoader = new WeakReference<ClassLoader>(cl); 1.160 + } 1.161 + 1.162 + return Class.forName(toolClassName, false, cl); 1.163 } 1.164 1.165 - private static Class<?> findClass() 1.166 - throws MalformedURLException, ClassNotFoundException 1.167 - { 1.168 - try { 1.169 - return enableAsserts(Class.forName(defaultJavaCompilerName, false, null)); 1.170 - } catch (ClassNotFoundException e) { 1.171 - trace(FINE, e); 1.172 - } 1.173 - File file = new File(System.getProperty("java.home")); 1.174 - if (file.getName().equalsIgnoreCase("jre")) 1.175 - file = file.getParentFile(); 1.176 - for (String name : defaultToolsLocation) 1.177 - file = new File(file, name); 1.178 - URL[] urls = {file.toURI().toURL()}; 1.179 - trace(FINE, urls[0].toString()); 1.180 - ClassLoader cl = URLClassLoader.newInstance(urls); 1.181 - cl.setPackageAssertionStatus("com.sun.tools.javac", true); 1.182 - return Class.forName(defaultJavaCompilerName, false, cl); 1.183 + } 1.184 + 1.185 + private static Class<?> enableAsserts(Class<?> cls) { 1.186 + try { 1.187 + ClassLoader loader = cls.getClassLoader(); 1.188 + if (loader != null) 1.189 + loader.setPackageAssertionStatus("com.sun.tools.javac", true); 1.190 + else 1.191 + trace(FINE, "loader == null"); 1.192 + } catch (SecurityException ex) { 1.193 + trace(FINE, ex); 1.194 } 1.195 + return cls; 1.196 + } 1.197 1.198 - private static Class<?> enableAsserts(Class<?> cls) { 1.199 - try { 1.200 - ClassLoader loader = cls.getClassLoader(); 1.201 - if (loader != null) 1.202 - loader.setPackageAssertionStatus("com.sun.tools.javac", true); 1.203 - else 1.204 - trace(FINE, "loader == null"); 1.205 - } catch (SecurityException ex) { 1.206 - trace(FINE, ex); 1.207 - } 1.208 - return cls; 1.209 - } 1.210 - } 1.211 + 1.212 }
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/test/tools/javac/api/ToolProvider/HelloWorldTest.java Thu Aug 26 15:17:17 2010 -0700 2.3 @@ -0,0 +1,83 @@ 2.4 +/* 2.5 + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 2.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.7 + * 2.8 + * This code is free software; you can redistribute it and/or modify it 2.9 + * under the terms of the GNU General Public License version 2 only, as 2.10 + * published by the Free Software Foundation. 2.11 + * 2.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 2.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 2.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 2.15 + * version 2 for more details (a copy is included in the LICENSE file that 2.16 + * accompanied this code). 2.17 + * 2.18 + * You should have received a copy of the GNU General Public License version 2.19 + * 2 along with this work; if not, write to the Free Software Foundation, 2.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2.21 + * 2.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2.23 + * or visit www.oracle.com if you need additional information or have any 2.24 + * questions. 2.25 + */ 2.26 + 2.27 +/* 2.28 + * @test 2.29 + * @bug 6604599 2.30 + * @summary ToolProvider should be less compiler-specific 2.31 + */ 2.32 + 2.33 +import java.io.*; 2.34 +import java.util.*; 2.35 + 2.36 +// verify that running a simple program, such as this one, does not trigger 2.37 +// the loading of ToolProvider or any com.sun.tools.javac class 2.38 +public class HelloWorldTest { 2.39 + public static void main(String... args) throws Exception { 2.40 + if (args.length > 0) { 2.41 + System.err.println(Arrays.asList(args)); 2.42 + return; 2.43 + } 2.44 + 2.45 + new HelloWorldTest().run(); 2.46 + } 2.47 + 2.48 + void run() throws Exception { 2.49 + File javaHome = new File(System.getProperty("java.home")); 2.50 + if (javaHome.getName().equals("jre")) 2.51 + javaHome = javaHome.getParentFile(); 2.52 + File javaExe = new File(new File(javaHome, "bin"), "java"); 2.53 + String classpath = System.getProperty("java.class.path"); 2.54 + 2.55 + String[] cmd = { 2.56 + javaExe.getPath(), 2.57 + "-verbose:class", 2.58 + "-classpath", classpath, 2.59 + HelloWorldTest.class.getName(), 2.60 + "Hello", "World" 2.61 + }; 2.62 + 2.63 + ProcessBuilder pb = new ProcessBuilder(cmd).redirectErrorStream(true); 2.64 + Process p = pb.start(); 2.65 + BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream())); 2.66 + String line; 2.67 + while ((line = r.readLine()) != null) { 2.68 + System.err.println(line); 2.69 + if (line.contains("javax.tools.ToolProvider") || line.contains("com.sun.tools.javac.")) 2.70 + error(">>> " + line); 2.71 + } 2.72 + int rc = p.waitFor(); 2.73 + if (rc != 0) 2.74 + error("Unexpected exit code: " + rc); 2.75 + 2.76 + if (errors > 0) 2.77 + throw new Exception(errors + " errors occurred"); 2.78 + } 2.79 + 2.80 + void error(String msg) { 2.81 + System.err.println(msg); 2.82 + errors++; 2.83 + } 2.84 + 2.85 + int errors; 2.86 +}
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/test/tools/javac/api/ToolProvider/ToolProviderTest1.java Thu Aug 26 15:17:17 2010 -0700 3.3 @@ -0,0 +1,82 @@ 3.4 +/* 3.5 + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.7 + * 3.8 + * This code is free software; you can redistribute it and/or modify it 3.9 + * under the terms of the GNU General Public License version 2 only, as 3.10 + * published by the Free Software Foundation. 3.11 + * 3.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 3.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.15 + * version 2 for more details (a copy is included in the LICENSE file that 3.16 + * accompanied this code). 3.17 + * 3.18 + * You should have received a copy of the GNU General Public License version 3.19 + * 2 along with this work; if not, write to the Free Software Foundation, 3.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.21 + * 3.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 3.23 + * or visit www.oracle.com if you need additional information or have any 3.24 + * questions. 3.25 + */ 3.26 + 3.27 +/* 3.28 + * @test 3.29 + * @bug 6604599 3.30 + * @summary ToolProvider should be less compiler-specific 3.31 + */ 3.32 + 3.33 +import java.io.*; 3.34 + 3.35 +// verify that running accessing ToolProvider by itself does not 3.36 +// trigger loading com.sun.tools.javac.* 3.37 +public class ToolProviderTest1 { 3.38 + public static void main(String... args) throws Exception { 3.39 + if (args.length > 0) { 3.40 + System.err.println(Class.forName(args[0], true, null)); 3.41 + return; 3.42 + } 3.43 + 3.44 + new ToolProviderTest1().run(); 3.45 + } 3.46 + 3.47 + void run() throws Exception { 3.48 + File javaHome = new File(System.getProperty("java.home")); 3.49 + if (javaHome.getName().equals("jre")) 3.50 + javaHome = javaHome.getParentFile(); 3.51 + File javaExe = new File(new File(javaHome, "bin"), "java"); 3.52 + String classpath = System.getProperty("java.class.path"); 3.53 + 3.54 + String[] cmd = { 3.55 + javaExe.getPath(), 3.56 + "-verbose:class", 3.57 + "-classpath", classpath, 3.58 + ToolProviderTest1.class.getName(), 3.59 + "javax.tools.ToolProvider" 3.60 + }; 3.61 + 3.62 + ProcessBuilder pb = new ProcessBuilder(cmd).redirectErrorStream(true); 3.63 + Process p = pb.start(); 3.64 + BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream())); 3.65 + String line; 3.66 + while ((line = r.readLine()) != null) { 3.67 + System.err.println(line); 3.68 + if (line.contains("com.sun.tools.javac.")) 3.69 + error(">>> " + line); 3.70 + } 3.71 + int rc = p.waitFor(); 3.72 + if (rc != 0) 3.73 + error("Unexpected exit code: " + rc); 3.74 + 3.75 + if (errors > 0) 3.76 + throw new Exception(errors + " errors occurred"); 3.77 + } 3.78 + 3.79 + void error(String msg) { 3.80 + System.err.println(msg); 3.81 + errors++; 3.82 + } 3.83 + 3.84 + int errors; 3.85 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/tools/javac/api/ToolProvider/ToolProviderTest2.java Thu Aug 26 15:17:17 2010 -0700 4.3 @@ -0,0 +1,87 @@ 4.4 +/* 4.5 + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 + * 4.8 + * This code is free software; you can redistribute it and/or modify it 4.9 + * under the terms of the GNU General Public License version 2 only, as 4.10 + * published by the Free Software Foundation. 4.11 + * 4.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 4.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.15 + * version 2 for more details (a copy is included in the LICENSE file that 4.16 + * accompanied this code). 4.17 + * 4.18 + * You should have received a copy of the GNU General Public License version 4.19 + * 2 along with this work; if not, write to the Free Software Foundation, 4.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.21 + * 4.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.23 + * or visit www.oracle.com if you need additional information or have any 4.24 + * questions. 4.25 + */ 4.26 + 4.27 +/* 4.28 + * @test 4.29 + * @bug 6604599 4.30 + * @summary ToolProvider should be less compiler-specific 4.31 + */ 4.32 + 4.33 +import java.io.*; 4.34 +import javax.tools.*; 4.35 + 4.36 +// control for ToolProviderTest1 -- verify that using ToolProvider to 4.37 +// access the compiler does trigger loading com.sun.tools.javac.* 4.38 +public class ToolProviderTest2 { 4.39 + public static void main(String... args) throws Exception { 4.40 + if (args.length > 0) { 4.41 + System.err.println(ToolProvider.getSystemJavaCompiler()); 4.42 + return; 4.43 + } 4.44 + 4.45 + new ToolProviderTest2().run(); 4.46 + } 4.47 + 4.48 + void run() throws Exception { 4.49 + File javaHome = new File(System.getProperty("java.home")); 4.50 + if (javaHome.getName().equals("jre")) 4.51 + javaHome = javaHome.getParentFile(); 4.52 + File javaExe = new File(new File(javaHome, "bin"), "java"); 4.53 + String classpath = System.getProperty("java.class.path"); 4.54 + 4.55 + String[] cmd = { 4.56 + javaExe.getPath(), 4.57 + "-verbose:class", 4.58 + "-classpath", classpath, 4.59 + ToolProviderTest2.class.getName(), 4.60 + "javax.tools.ToolProvider" 4.61 + }; 4.62 + 4.63 + ProcessBuilder pb = new ProcessBuilder(cmd).redirectErrorStream(true); 4.64 + Process p = pb.start(); 4.65 + BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream())); 4.66 + String line; 4.67 + boolean found = false; 4.68 + while ((line = r.readLine()) != null) { 4.69 + System.err.println(line); 4.70 + if (line.contains("com.sun.tools.javac.")) 4.71 + found = true; 4.72 + } 4.73 + int rc = p.waitFor(); 4.74 + if (rc != 0) 4.75 + error("Unexpected exit code: " + rc); 4.76 + 4.77 + if (!found) 4.78 + System.err.println("expected class name not found"); 4.79 + 4.80 + if (errors > 0) 4.81 + throw new Exception(errors + " errors occurred"); 4.82 + } 4.83 + 4.84 + void error(String msg) { 4.85 + System.err.println(msg); 4.86 + errors++; 4.87 + } 4.88 + 4.89 + int errors; 4.90 +}