TestClose.java revision 3294:9adfb22ff08f
1/* 2 * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24/* 25 * @test 26 * @bug 7092965 27 * @summary javac should not close processorClassLoader before end of compilation 28 * @modules jdk.compiler/com.sun.tools.javac.api 29 * jdk.compiler/com.sun.tools.javac.file 30 */ 31 32import com.sun.source.util.JavacTask; 33import com.sun.source.util.TaskEvent; 34import com.sun.source.util.TaskListener; 35import com.sun.tools.javac.api.ClientCodeWrapper.Trusted; 36import com.sun.tools.javac.api.BasicJavacTask; 37import com.sun.tools.javac.api.JavacTool; 38import java.io.ByteArrayOutputStream; 39import java.io.File; 40import java.io.IOException; 41import java.io.PrintStream; 42import java.lang.reflect.Field; 43import java.net.URI; 44import java.util.ArrayList; 45import java.util.Arrays; 46import java.util.Collections; 47import java.util.List; 48import javax.annotation.processing.ProcessingEnvironment; 49import javax.tools.JavaFileObject; 50import javax.tools.SimpleJavaFileObject; 51import javax.tools.StandardJavaFileManager; 52import javax.tools.StandardLocation; 53import javax.tools.ToolProvider; 54 55/* 56 * The test compiles an annotation processor and a helper class into a 57 * custom classes directory. 58 * 59 * It then uses them while compiling a dummy file, with the custom classes 60 * directory on the processor path, thus guaranteeing that references to 61 * these class are satisfied by the processor class loader. 62 * 63 * The annotation processor uses the javac TaskListener to run code 64 * after annotation processing has completed, to verify that the classloader 65 * is not closed until the end of the compilation. 66 */ 67 68@Trusted // avoids use of ClientCodeWrapper 69public class TestClose implements TaskListener { 70 public static final String annoProc = 71 "import java.util.*;\n" + 72 "import javax.annotation.processing.*;\n" + 73 "import javax.lang.model.*;\n" + 74 "import javax.lang.model.element.*;\n" + 75 "import com.sun.source.util.*;\n" + 76 "import com.sun.tools.javac.processing.*;\n" + 77 "import com.sun.tools.javac.util.*;\n" + 78 "@SupportedAnnotationTypes(\"*\")\n" + 79 "public class AnnoProc extends AbstractProcessor {\n" + 80 " @Override\n" + 81 " public SourceVersion getSupportedSourceVersion() {\n" + 82 " return SourceVersion.latest();\n" + 83 " }\n" + 84 " @Override\n" + 85 " public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n" + 86 " System.out.println(\"in AnnoProc.process\");\n" + 87 " final ClassLoader cl = getClass().getClassLoader();\n" + 88 " if (roundEnv.processingOver()) {\n" + 89 " TestClose.add(processingEnv, new Runnable() {\n" + 90 " public void run() {\n" + 91 " System.out.println(getClass().getName() + \": run()\");\n" + 92 " try {\n" + 93 " cl.loadClass(\"Callback\")\n" + 94 " .asSubclass(Runnable.class)\n" + 95 " .newInstance()\n" + 96 " .run();\n" + 97 " } catch (ReflectiveOperationException e) {\n" + 98 " throw new Error(e);\n" + 99 " }\n" + 100 " }\n" + 101 " });\n" + 102 " }\n" + 103 " return true;\n" + 104 " }\n" + 105 "}\n"; 106 107 public static final String callback = 108 "public class Callback implements Runnable {\n" + 109 " public void run() {\n" + 110 " System.out.println(getClass().getName() + \": run()\");\n" + 111 " }\n" + 112 "}"; 113 114 public static void main(String... args) throws Exception { 115 new TestClose().run(); 116 } 117 118 void run() throws IOException { 119 JavacTool tool = (JavacTool) ToolProvider.getSystemJavaCompiler(); 120 try (StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null)) { 121 122 File classes = new File("classes"); 123 classes.mkdirs(); 124 File extraClasses = new File("extraClasses"); 125 extraClasses.mkdirs(); 126 127 System.out.println("compiling classes to extraClasses"); 128 { // setup class in extraClasses 129 fm.setLocation(StandardLocation.CLASS_OUTPUT, 130 Collections.singleton(extraClasses)); 131 List<? extends JavaFileObject> files = Arrays.asList( 132 new MemFile("AnnoProc.java", annoProc), 133 new MemFile("Callback.java", callback)); 134 List<String> options = Arrays.asList( 135 "-XaddExports:" 136 + "jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED," 137 + "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", 138 "-XDaccessInternalAPI"); 139 JavacTask task = tool.getTask(null, fm, null, options, null, files); 140 check(task.call()); 141 } 142 143 System.out.println("compiling dummy to classes with anno processor"); 144 { // use that class in a TaskListener after processing has completed 145 PrintStream prev = System.out; 146 String out; 147 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 148 try (PrintStream ps = new PrintStream(baos)) { 149 System.setOut(ps); 150 File testClasses = new File(System.getProperty("test.classes")); 151 fm.setLocation(StandardLocation.CLASS_OUTPUT, 152 Collections.singleton(classes)); 153 fm.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, 154 Arrays.asList(extraClasses, testClasses)); 155 List<? extends JavaFileObject> files = Arrays.asList( 156 new MemFile("my://dummy", "class Dummy { }")); 157 List<String> options = Arrays.asList("-XDaccessInternalAPI", "-processor", "AnnoProc"); 158 JavacTask task = tool.getTask(null, fm, null, options, null, files); 159 task.setTaskListener(this); 160 check(task.call()); 161 } finally { 162 System.setOut(prev); 163 out = baos.toString(); 164 if (!out.isEmpty()) 165 System.out.println(out); 166 } 167 check(out.contains("AnnoProc$1: run()")); 168 check(out.contains("Callback: run()")); 169 } 170 } 171 } 172 173 @Override 174 public void started(TaskEvent e) { 175 System.out.println("Started: " + e); 176 } 177 178 @Override 179 public void finished(TaskEvent e) { 180 System.out.println("Finished: " + e); 181 if (e.getKind() == TaskEvent.Kind.ANALYZE) { 182 for (Runnable r: runnables) { 183 System.out.println("running " + r); 184 r.run(); 185 } 186 } 187 } 188 189 void check(boolean b) { 190 if (!b) 191 throw new AssertionError(); 192 } 193 194 public static void add(ProcessingEnvironment env, Runnable r) { 195 // ensure this class in this class loader can access javac internals 196 try { 197 JavacTask task = JavacTask.instance(env); 198 TaskListener l = ((BasicJavacTask) task).getTaskListeners().iterator().next(); 199 // The TaskListener is an instanceof TestClose, but when using the 200 // default class loaders. the taskListener uses a different 201 // instance of Class<TestClose> than the anno processor. 202 // If you try to evaluate 203 // TestClose tc = (TestClose) (l). 204 // you get the following somewhat confusing error: 205 // java.lang.ClassCastException: TestClose cannot be cast to TestClose 206 // The workaround is to access the fields of TestClose with reflection. 207 Field f = l.getClass().getField("runnables"); 208 @SuppressWarnings("unchecked") 209 List<Runnable> runnables = (List<Runnable>) f.get(l); 210 runnables.add(r); 211 } catch (Throwable t) { 212 t.printStackTrace(); 213 } 214 } 215 216 public List<Runnable> runnables = new ArrayList<>(); 217 218 class MemFile extends SimpleJavaFileObject { 219 public final String text; 220 221 MemFile(String name, String text) { 222 super(URI.create(name), JavaFileObject.Kind.SOURCE); 223 this.text = text; 224 } 225 226 @Override 227 public String getName() { 228 return uri.toString(); 229 } 230 231 @Override 232 public String getCharContent(boolean ignoreEncodingErrors) { 233 return text; 234 } 235 } 236} 237