test/tools/javac/processing/loader/testClose/TestClose.java

Wed, 21 Sep 2011 21:56:53 -0700

author
jjg
date
Wed, 21 Sep 2011 21:56:53 -0700
changeset 1096
b0d5f00e69f7
child 1210
62e611704863
permissions
-rw-r--r--

7092965: javac should not close processorClassLoader before end of compilation
Reviewed-by: darcy

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

mercurial