1 /*
2  * Copyright (c) 2016, 2017, 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package build.tools.jigsaw;
27 
28 import java.io.IOException;
29 import java.io.OutputStream;
30 import java.io.PrintStream;
31 import java.io.UncheckedIOException;
32 import java.lang.module.ModuleDescriptor;
33 import java.lang.module.ModuleFinder;
34 import java.lang.module.ModuleReference;
35 import java.net.URI;
36 import java.nio.file.FileSystem;
37 import java.nio.file.FileSystems;
38 import java.nio.file.Files;
39 import java.nio.file.Path;
40 import java.nio.file.Paths;
41 import java.nio.file.attribute.BasicFileAttributes;
42 import java.time.LocalDateTime;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.HashMap;
46 import java.util.HashSet;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Set;
50 import java.util.jar.JarEntry;
51 import java.util.jar.JarFile;
52 import java.util.stream.Collectors;
53 import java.util.stream.Stream;
54 
55 /**
56  * Run this tool to generate the JDK internal APIs in the previous releases
57  * including platform-specific internal APIs.
58  */
59 public class ListPackages {
60     // Filter non-interesting JAR files
61     private final static List<String> excludes = Arrays.asList(
62         "deploy.jar",
63         "javaws.jar",
64         "plugin.jar",
65         "cldrdata.jar",
66         "localedata.jar"
67     );
usage()68     private static void usage() {
69         System.out.println("ListPackages [-o <outfile>] [-jdkinternals] <javaHome> [<javaHome>]*");
70     }
71 
72     private static final Set<String> EXPORTED_PACKAGES = new HashSet<>();
73 
main(String... args)74     public static void main(String... args) throws IOException {
75         List<Path> paths = new ArrayList<>();
76         Path outFile = null;
77         boolean jdkinternals = false;
78         int i=0;
79         while (i < args.length) {
80             String arg = args[i++];
81             if (arg.equals("-o")) {
82                 outFile = Paths.get(args[i++]);
83             } else if (arg.equals("-jdkinternals")) {
84                 jdkinternals = true;
85             } else {
86                 Path p = Paths.get(arg);
87                 if (Files.notExists(p))
88                     throw new IllegalArgumentException(p + " not exist");
89                 paths.add(p);
90             }
91         }
92         if (paths.isEmpty()) {
93             usage();
94             System.exit(1);
95         }
96 
97         // Get the exported APIs from the current JDK releases
98         Path javaHome = Paths.get(System.getProperty("java.home"));
99         ModuleFinder.ofSystem().findAll()
100             .stream()
101             .map(ModuleReference::descriptor)
102             .filter(md -> !md.name().equals("jdk.unsupported"))
103             .flatMap(md -> md.exports().stream())
104             .filter(exp -> !exp.isQualified())
105             .map(ModuleDescriptor.Exports::source)
106             .forEach(EXPORTED_PACKAGES::add);
107 
108         ListPackages listPackages = new ListPackages(paths);
109         Stream<String> pkgs = listPackages.packages().stream();
110         if (jdkinternals) {
111             pkgs = pkgs.filter(pn -> !EXPORTED_PACKAGES.contains(pn));
112         }
113         if (outFile != null) {
114             try (OutputStream out = Files.newOutputStream(outFile);
115                  PrintStream pw = new PrintStream(out)) {
116                 write(pw, pkgs);
117             }
118         } else {
119             write(System.out, pkgs);
120         }
121     }
122 
123 
write(PrintStream pw, Stream<String> packages)124     private static void write(PrintStream pw, Stream<String> packages) {
125         pw.println("# This file is auto-generated by ListPackages tool on " +
126                    LocalDateTime.now().toString());
127         packages.sorted().forEach(pw::println);
128     }
129 
130     private final Set<String> packages = new HashSet<>();
ListPackages(List<Path> dirs)131     ListPackages(List<Path> dirs) throws IOException {
132         for (Path p : dirs) {
133             packages.addAll(list(p));
134         }
135     }
136 
packages()137     Set<String> packages() {
138         return packages;
139     }
140 
list(Path javaHome)141     private Set<String> list(Path javaHome) throws IOException {
142         Path jrt = javaHome.resolve("lib").resolve("modules");
143         Path jre = javaHome.resolve("jre");
144 
145         if (Files.exists(jrt)) {
146             return listModularRuntime(javaHome);
147         } else if (Files.exists(jre.resolve("lib").resolve("rt.jar"))) {
148             return listLegacyRuntime(javaHome);
149         }
150         throw new IllegalArgumentException("invalid " + javaHome);
151     }
152 
listModularRuntime(Path javaHome)153     private Set<String> listModularRuntime(Path javaHome) throws IOException {
154         Map<String, String> env = new HashMap<>();
155         env.put("java.home", javaHome.toString());
156         FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), env);
157         Path root = fs.getPath("packages");
158         return Files.walk(root, 1)
159                     .map(Path::getFileName)
160                     .map(Path::toString)
161                     .collect(Collectors.toSet());
162     }
163 
listLegacyRuntime(Path javaHome)164     private Set<String> listLegacyRuntime(Path javaHome) throws IOException {
165         List<Path> dirs = new ArrayList<>();
166         Path jre = javaHome.resolve("jre");
167         Path lib = javaHome.resolve("lib");
168 
169         dirs.add(jre.resolve("lib"));
170         dirs.add(jre.resolve("lib").resolve("ext"));
171         dirs.add(lib.resolve("tools.jar"));
172         dirs.add(lib.resolve("jconsole.jar"));
173         Set<String> packages = new HashSet<>();
174         for (Path d : dirs) {
175             Files.find(d, 1, (Path p, BasicFileAttributes attr)
176                     -> p.getFileName().toString().endsWith(".jar") &&
177                        !excludes.contains(p.getFileName().toString()))
178                  .map(ListPackages::walkJarFile)
179                  .forEach(packages::addAll);
180         }
181         return packages;
182     }
183 
walkJarFile(Path jarfile)184     static Set<String> walkJarFile(Path jarfile) {
185         try (JarFile jf = new JarFile(jarfile.toFile())) {
186             return jf.stream()
187                      .map(JarEntry::getName)
188                      .filter(n -> n.endsWith(".class"))
189                      .map(ListPackages::toPackage)
190                      .collect(Collectors.toSet());
191         } catch (IOException e) {
192             throw new UncheckedIOException(e);
193         }
194     }
195 
toPackage(String name)196     static String toPackage(String name) {
197         int i = name.lastIndexOf('/');
198         if (i < 0) {
199             System.err.format("Warning: unnamed package %s%n", name);
200         }
201         return i >= 0 ? name.substring(0, i).replace("/", ".") : "";
202     }
203 }
204