T8147801.java revision 3903:fed9310b4b93
1214478Srpaulo/*
217680Spst * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
339297Sfenner * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
417680Spst *
517680Spst * This code is free software; you can redistribute it and/or modify it
617680Spst * under the terms of the GNU General Public License version 2 only, as
717680Spst * published by the Free Software Foundation.
817680Spst *
9214478Srpaulo * This code is distributed in the hope that it will be useful, but WITHOUT
10214478Srpaulo * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11214478Srpaulo * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12214478Srpaulo * version 2 for more details (a copy is included in the LICENSE file that
13214478Srpaulo * accompanied this code).
14214478Srpaulo *
15214478Srpaulo * You should have received a copy of the GNU General Public License version
16214478Srpaulo * 2 along with this work; if not, write to the Free Software Foundation,
17214478Srpaulo * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18214478Srpaulo *
19127668Sbms * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2017680Spst * or visit www.oracle.com if you need additional information or have any
2117680Spst * questions.
2298524Sfenner */
2317680Spst
24214478Srpaulo/*
25214478Srpaulo * @test
2617680Spst * @bug 8147801
27111726Sfenner * @summary java.nio.file.ClosedFileSystemException when using Javadoc API's in JDK9
2875115Sfenner * @modules jdk.javadoc/com.sun.tools.javadoc
29214478Srpaulo * @library jarsrc
30214478Srpaulo * @build lib.* p.*
31214478Srpaulo * @run main T8147801
32235530Sdelphij */
33172683Smlaier
34172683Smlaierimport java.io.IOException;
35172683Smlaierimport java.io.PrintWriter;
36190207Srpauloimport java.io.StringWriter;
37172683Smlaierimport java.nio.file.ClosedFileSystemException;
38172683Smlaierimport java.nio.file.Files;
39127668Sbmsimport java.nio.file.Path;
40127668Sbmsimport java.nio.file.Paths;
41146773Ssamimport java.util.ArrayList;
42146773Ssamimport java.util.List;
43146773Ssamimport java.util.jar.JarEntry;
44146773Ssamimport java.util.jar.JarOutputStream;
45146773Ssam
46146773Ssamimport com.sun.javadoc.ClassDoc;
47146773Ssamimport com.sun.javadoc.FieldDoc;
48146773Ssamimport com.sun.javadoc.RootDoc;
49146773Ssam
50146773Ssam/*
51146773Ssam * This test verifies the use of the hidden fileManager.deferClose
52146773Ssam * option, to work around the limitation that javadoc objects
53146773Ssam * (like RootDoc and related types) should cannot normally be used
54146773Ssam * after javadoc exits, closing its file manager (if it opened it.)
55146773Ssam *
56146773Ssam * The test runs javadoc on a chain of classes, 1 in source form,
57146773Ssam * and 2 in a jar file. javadoc/javac will "complete" classes found
58146773Ssam * in source, but will eagerly "classes" in class form.
5917680Spst * The chain is p/Test.java -> lib/Lib1.class -> lib/Lib2.class.
6017680Spst * After javadoc exits, the classes are examined, to finally force
6198524Sfenner * the classes to be completed, possibly causing javac to try and access
6239297Sfenner * references into a .jar file system which has now been closed.
63235530Sdelphij *
64235530Sdelphij * The test runs two test cases -- one without the workaround option,
65235530Sdelphij * to test the validity of the test case, and one with the workaround
66235530Sdelphij * option, to test that it works as expected.
67235530Sdelphij */
68235530Sdelphijpublic class T8147801 {
69235530Sdelphij    public static void main(String... args) throws Exception {
70235530Sdelphij        new T8147801().run();
71235530Sdelphij    }
72235530Sdelphij
73235530Sdelphij    void run() throws Exception {
74235530Sdelphij        initJar();
75235530Sdelphij        test(false);
76235530Sdelphij        test(true);
77235530Sdelphij        if (errors > 0) {
78235530Sdelphij            throw new Exception(errors + " errors occurred");
79235530Sdelphij        }
80235530Sdelphij    }
81235530Sdelphij
82235530Sdelphij    void test(boolean withOption) {
83235530Sdelphij        System.err.println("Testing " + (withOption ? "with" : "without") + " option");
84235530Sdelphij        try {
85235530Sdelphij            String dump = "";
86235530Sdelphij            RootDoc root = getRootDoc(withOption);
87235530Sdelphij            for (ClassDoc cd: root.specifiedClasses()) {
88235530Sdelphij                dump += dump(cd);
89235530Sdelphij            }
9039297Sfenner            if (dump.contains("lib.Lib2.i")) {
9139297Sfenner                if (!withOption) {
9275115Sfenner                    error("control case failed: Lib2 class file was read, unexpectedly, without using option");
9375115Sfenner                }
9475115Sfenner            } else {
9575115Sfenner                if (withOption) {
9675115Sfenner                    error("test case failed: could not read Lib2 class file, using option");
9775115Sfenner                }
9875115Sfenner            }
9975115Sfenner        } catch (ClosedFileSystemException e) {
10039297Sfenner            error("Unexpected exception: " + e);
10175115Sfenner        }
10275115Sfenner        System.err.println();
10375115Sfenner    }
10439297Sfenner
10539297Sfenner    RootDoc getRootDoc(boolean withOption) {
10639297Sfenner        List<String> opts = new ArrayList<>();
10739297Sfenner        if (withOption)
10839297Sfenner            opts.add("-XDfileManager.deferClose=10");
10939297Sfenner        opts.add("-doclet");
11039297Sfenner        opts.add(getClass().getName());
11139297Sfenner        opts.add("-classpath");
11239297Sfenner        opts.add(jarPath.toString());
11356893Sfenner        opts.add(Paths.get(System.getProperty("test.src"), "p", "Test.java").toString());
114214478Srpaulo        System.err.println("javadoc opts: " + opts);
115214478Srpaulo        int rc = com.sun.tools.javadoc.Main.execute(
116214478Srpaulo                "javadoc",
117214478Srpaulo                // by specifying our own class loader, we get the same Class instance as this
118214478Srpaulo                getClass().getClassLoader(),
119214478Srpaulo                opts.toArray(new String[opts.size()]));
12056893Sfenner        if (rc != 0) {
12175115Sfenner            error("unexpected exit from javadoc or doclet: " + rc);
12298524Sfenner        }
12398524Sfenner        return cachedRoot;
12456893Sfenner    }
12556893Sfenner
12656893Sfenner    String dump(ClassDoc cd) {
12756893Sfenner        StringWriter sw = new StringWriter();
12856893Sfenner        PrintWriter pw = new PrintWriter(sw);
12975115Sfenner        dump(pw, "", cd);
13075115Sfenner        String out = sw.toString();
13156893Sfenner        System.err.println(out);
13256893Sfenner        return out;
13375115Sfenner    }
13475115Sfenner
13575115Sfenner    void dump(PrintWriter out, String prefix, ClassDoc cd) {
13675115Sfenner        out.println(prefix + "class: " + cd);
13775115Sfenner        for (FieldDoc fd: cd.fields()) {
13875115Sfenner            out.println(prefix + "  " + fd);
13975115Sfenner            if (fd.type().asClassDoc() != null) {
14075115Sfenner                dump(out, prefix + "    ", fd.type().asClassDoc());
14156893Sfenner            }
14256893Sfenner        }
14356893Sfenner    }
14456893Sfenner
14556893Sfenner    void initJar() throws IOException {
14698524Sfenner        Path testClasses = Paths.get(System.getProperty("test.classes"));
14798524Sfenner        jarPath = Paths.get("lib.jar");
14898524Sfenner        try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(jarPath))) {
14998524Sfenner            String[] classNames = {"Lib1.class", "Lib2.class"};
15098524Sfenner            for (String cn : classNames) {
15198524Sfenner                out.putNextEntry(new JarEntry("lib/" + cn));
15298524Sfenner                Path libClass = testClasses.resolve("jarsrc").resolve("lib").resolve(cn);
15398524Sfenner                out.write(Files.readAllBytes(libClass));
15498524Sfenner            }
15598524Sfenner        }
15675115Sfenner    }
15775115Sfenner
15856893Sfenner    void error(String msg) {
15956893Sfenner        System.err.println("Error: " + msg);
16098524Sfenner        errors++;
161214478Srpaulo    }
16256893Sfenner
16398524Sfenner    Path jarPath;
16498524Sfenner    int errors;
16598524Sfenner
16698524Sfenner    // Bad doclet caches the RootDoc for later use
16798524Sfenner
16898524Sfenner    static RootDoc cachedRoot;
16998524Sfenner
17098524Sfenner    public static boolean start(RootDoc root) {
17198524Sfenner        cachedRoot = root;
17298524Sfenner        return true;
17398524Sfenner    }
17498524Sfenner}
17598524Sfenner