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

     1 /*
     2  * Copyright (c) 2011, 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  */
    24 /*
    25  * @test
    26  * @bug 7092965
    27  * @summary javac should not close processorClassLoader before end of compilation
    28  */
    30 import com.sun.source.util.JavacTask;
    31 import com.sun.source.util.TaskEvent;
    32 import com.sun.source.util.TaskListener;
    33 import com.sun.tools.javac.api.ClientCodeWrapper.Trusted;
    34 import com.sun.tools.javac.api.JavacTool;
    35 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
    36 import com.sun.tools.javac.util.Context;
    37 import java.io.ByteArrayOutputStream;
    38 import java.io.File;
    39 import java.io.IOException;
    40 import java.io.PrintStream;
    41 import java.lang.reflect.Field;
    42 import java.net.URI;
    43 import java.util.ArrayList;
    44 import java.util.Arrays;
    45 import java.util.Collections;
    46 import java.util.List;
    47 import javax.annotation.processing.ProcessingEnvironment;
    48 import javax.tools.JavaFileObject;
    49 import javax.tools.SimpleJavaFileObject;
    50 import javax.tools.StandardJavaFileManager;
    51 import javax.tools.StandardLocation;
    52 import javax.tools.ToolProvider;
    54 /*
    55  * The test compiles an annotation processor and a helper class into a
    56  * custom classes directory.
    57  *
    58  * It then uses them while compiling a dummy file, with the custom classes
    59  * directory on the processor path, thus guaranteeing that references to
    60  * these class are satisfied by the processor class loader.
    61  *
    62  * The annotation processor uses the javac TaskListener to run code
    63  * after annotation processing has completed, to verify that the classloader
    64  * is not closed until the end of the compilation.
    65  */
    67 @Trusted // avoids use of ClientCodeWrapper
    68 public class TestClose implements TaskListener {
    69     public static final String annoProc =
    70         "import java.util.*;\n" +
    71         "import javax.annotation.processing.*;\n" +
    72         "import javax.lang.model.*;\n" +
    73         "import javax.lang.model.element.*;\n" +
    74         "import com.sun.source.util.*;\n" +
    75         "import com.sun.tools.javac.processing.*;\n" +
    76         "import com.sun.tools.javac.util.*;\n" +
    77         "@SupportedAnnotationTypes(\"*\")\n" +
    78         "public class AnnoProc extends AbstractProcessor {\n" +
    79         "    @Override\n" +
    80         "    public SourceVersion getSupportedSourceVersion() {\n" +
    81         "        return SourceVersion.latest();\n" +
    82         "    }\n" +
    83         "    @Override\n" +
    84         "    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n" +
    85         "        System.out.println(\"in AnnoProc.process\");\n" +
    86         "        final ClassLoader cl = getClass().getClassLoader();\n" +
    87         "        if (roundEnv.processingOver()) {\n" +
    88         "            TestClose.add(processingEnv, new Runnable() {\n" +
    89         "                public void run() {\n" +
    90         "                    System.out.println(getClass().getName() + \": run()\");\n" +
    91         "                    try {\n" +
    92         "                    cl.loadClass(\"Callback\")\n" +
    93         "                        .asSubclass(Runnable.class)\n" +
    94         "                        .newInstance()\n" +
    95         "                        .run();\n" +
    96         "                    } catch (ReflectiveOperationException e) {\n" +
    97         "                        throw new Error(e);\n" +
    98         "                    }\n" +
    99         "                }\n" +
   100         "            });\n" +
   101         "        }\n" +
   102         "        return true;\n" +
   103         "    }\n" +
   104         "}\n";
   106     public static final String callback =
   107         "public class Callback implements Runnable {\n" +
   108         "    public void run() {\n" +
   109         "        System.out.println(getClass().getName() + \": run()\");\n" +
   110         "    }\n" +
   111         "}";
   113     public static void main(String... args) throws Exception {
   114         new TestClose().run();
   115     }
   117     void run() throws IOException {
   118         JavacTool tool = (JavacTool) ToolProvider.getSystemJavaCompiler();
   119         StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null);
   121         File classes = new File("classes");
   122         classes.mkdirs();
   123         File extraClasses = new File("extraClasses");
   124         extraClasses.mkdirs();
   126         System.out.println("compiling classes to extraClasses");
   127         {   // setup class in extraClasses
   128             fm.setLocation(StandardLocation.CLASS_OUTPUT,
   129                     Collections.singleton(extraClasses));
   130             List<? extends JavaFileObject> files = Arrays.asList(
   131                     new MemFile("AnnoProc.java", annoProc),
   132                     new MemFile("Callback.java", callback));
   133             JavacTask task = tool.getTask(null, fm, null, null, null, files);
   134             check(task.call());
   135         }
   137         System.out.println("compiling dummy to classes with anno processor");
   138         {   // use that class in a TaskListener after processing has completed
   139             PrintStream prev = System.out;
   140             String out;
   141             ByteArrayOutputStream baos = new ByteArrayOutputStream();
   142             try (PrintStream ps = new PrintStream(baos)) {
   143                 System.setOut(ps);
   144                 File testClasses = new File(System.getProperty("test.classes"));
   145                 fm.setLocation(StandardLocation.CLASS_OUTPUT,
   146                         Collections.singleton(classes));
   147                 fm.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH,
   148                         Arrays.asList(extraClasses, testClasses));
   149                 List<? extends JavaFileObject> files = Arrays.asList(
   150                         new MemFile("my://dummy", "class Dummy { }"));
   151                 List<String> options = Arrays.asList("-processor", "AnnoProc");
   152                 JavacTask task = tool.getTask(null, fm, null, options, null, files);
   153                 task.setTaskListener(this);
   154                 check(task.call());
   155             } finally {
   156                 System.setOut(prev);
   157                 out = baos.toString();
   158                 if (!out.isEmpty())
   159                     System.out.println(out);
   160             }
   161             check(out.contains("AnnoProc$1: run()"));
   162             check(out.contains("Callback: run()"));
   163         }
   164     }
   166     @Override
   167     public void started(TaskEvent e) {
   168         System.out.println("Started: " + e);
   169     }
   171     @Override
   172     public void finished(TaskEvent e) {
   173         System.out.println("Finished: " + e);
   174         if (e.getKind() == TaskEvent.Kind.ANALYZE) {
   175             for (Runnable r: runnables) {
   176                 System.out.println("running " + r);
   177                 r.run();
   178             }
   179         }
   180     }
   182     void check(boolean b) {
   183         if (!b)
   184             throw new AssertionError();
   185     }
   188     public static void add(ProcessingEnvironment env, Runnable r) {
   189         try {
   190             Context c = ((JavacProcessingEnvironment) env).getContext();
   191             Object o = c.get(TaskListener.class);
   192             // The TaskListener is an instanceof TestClose, but when using the
   193             // default class loaders. the taskListener uses a different
   194             // instance of Class<TestClose> than the anno processor.
   195             // If you try to evaluate
   196             //      TestClose tc = (TestClose) (o).
   197             // you get the following somewhat confusing error:
   198             //   java.lang.ClassCastException: TestClose cannot be cast to TestClose
   199             // The workaround is to access the fields of TestClose with reflection.
   200             Field f = o.getClass().getField("runnables");
   201             @SuppressWarnings("unchecked")
   202             List<Runnable> runnables = (List<Runnable>) f.get(o);
   203             runnables.add(r);
   204         } catch (Throwable t) {
   205             System.err.println(t);
   206         }
   207     }
   209     public List<Runnable> runnables = new ArrayList<>();
   211     class MemFile extends SimpleJavaFileObject {
   212         public final String text;
   214         MemFile(String name, String text) {
   215             super(URI.create(name), JavaFileObject.Kind.SOURCE);
   216             this.text = text;
   217         }
   219         @Override
   220         public String getName() {
   221             return uri.toString();
   222         }
   224         @Override
   225         public String getCharContent(boolean ignoreEncodingErrors) {
   226             return text;
   227         }
   228     }
   229 }

mercurial