1 /* 2 * Copyright (c) 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. 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.listjdkinternals; 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 54 /** 55 * Run this tool to generate the JDK internal APIs in the previous releases 56 * including platform-specific internal APIs. 57 */ 58 public class ListJDKInternals { 59 // Filter non-interesting JAR files 60 private final static List<String> excludes = Arrays.asList( 61 "deploy.jar", 62 "javaws.jar", 63 "plugin.jar", 64 "cldrdata.jar", 65 "localedata.jar" 66 ); 67 private static void usage() { 68 System.out.println("ListJDKInternals [-o <outfile>] <javaHome> [<javaHome>]*"); 69 } 70 71 private static final Set<String> EXPORTED_PACKAGES = new HashSet<>(); 72 73 public static void main(String... args) throws IOException { 74 List<Path> paths = new ArrayList<>(); 75 Path outFile = null; 76 int i=0; 77 while (i < args.length) { 78 String arg = args[i++]; 79 if (arg.equals("-o")) { 80 outFile = Paths.get(args[i++]); 81 } else { 82 Path p = Paths.get(arg); 83 if (Files.notExists(p)) 84 throw new IllegalArgumentException(p + " not exist"); 85 paths.add(p); 86 } 87 } 88 if (paths.isEmpty()) { 89 usage(); 90 System.exit(1); 91 } 92 93 // Get the exported APIs from the current JDK releases 94 Path javaHome = Paths.get(System.getProperty("java.home")); 95 ModuleFinder.ofSystem().findAll() 96 .stream() 97 .map(ModuleReference::descriptor) 98 .filter(md -> !md.name().equals("jdk.unsupported")) 99 .map(ModuleDescriptor::exports) 100 .flatMap(Set::stream) 101 .filter(exp -> !exp.isQualified()) 102 .map(ModuleDescriptor.Exports::source) 103 .forEach(EXPORTED_PACKAGES::add); 104 105 ListJDKInternals listJDKInternals = new ListJDKInternals(paths); 106 if (outFile != null) { 107 try (OutputStream out = Files.newOutputStream(outFile); 108 PrintStream pw = new PrintStream(out)) { 109 listJDKInternals.write(pw); 110 } 111 } else { 112 listJDKInternals.write(System.out); 113 } 114 } 115 116 private final Set<String> packages = new HashSet<>(); 117 ListJDKInternals(List<Path> dirs) throws IOException { 118 for (Path p : dirs) { 119 packages.addAll(list(p)); 120 } 121 } 122 123 private void write(PrintStream pw) { 124 pw.println("# This file is auto-generated by ListJDKInternals tool on " + 125 LocalDateTime.now().toString()); 126 packages.stream().sorted() 127 .forEach(pw::println); 128 } 129 130 private Set<String> list(Path javaHome) throws IOException { 131 Path jrt = javaHome.resolve("lib").resolve("modules"); 132 Path jre = javaHome.resolve("jre"); 133 134 if (Files.exists(jrt)) { 135 return listModularRuntime(javaHome); 136 } else if (Files.exists(jre.resolve("lib").resolve("rt.jar"))) { 137 return listLegacyRuntime(javaHome); 138 } 139 throw new IllegalArgumentException("invalid " + javaHome); 140 } 141 142 private Set<String> listModularRuntime(Path javaHome) throws IOException { 143 Map<String, String> env = new HashMap<>(); 144 env.put("java.home", javaHome.toString()); 145 FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), env); 146 Path root = fs.getPath("packages"); 147 return Files.walk(root, 1) 148 .map(Path::getFileName) 149 .map(Path::toString) 150 .filter(pn -> !EXPORTED_PACKAGES.contains(pn)) 151 .collect(Collectors.toSet()); 152 } 153 154 private Set<String> listLegacyRuntime(Path javaHome) throws IOException { 155 List<Path> dirs = new ArrayList<>(); 156 Path jre = javaHome.resolve("jre"); 157 Path lib = javaHome.resolve("lib"); 158 159 dirs.add(jre.resolve("lib")); 160 dirs.add(jre.resolve("lib").resolve("ext")); 161 dirs.add(lib.resolve("tools.jar")); 162 dirs.add(lib.resolve("jconsole.jar")); 163 Set<String> packages = new HashSet<>(); 164 for (Path d : dirs) { 165 Files.find(d, 1, (Path p, BasicFileAttributes attr) 166 -> p.getFileName().toString().endsWith(".jar") && 167 !excludes.contains(p.getFileName().toString())) 168 .map(ListJDKInternals::walkJarFile) 169 .flatMap(Set::stream) 170 .filter(pn -> !EXPORTED_PACKAGES.contains(pn)) 171 .forEach(packages::add); 172 } 173 return packages; 174 } 175 176 static Set<String> walkJarFile(Path jarfile) { 177 try (JarFile jf = new JarFile(jarfile.toFile())) { 178 return jf.stream() 179 .map(JarEntry::getName) 180 .filter(n -> n.endsWith(".class")) 181 .map(ListJDKInternals::toPackage) 182 .collect(Collectors.toSet()); 183 } catch (IOException e) { 184 throw new UncheckedIOException(e); 185 } 186 } 187 188 static String toPackage(String name) { 189 int i = name.lastIndexOf('/'); 190 if (i < 0) { 191 System.err.format("Warning: unnamed package %s%n", name); 192 } 193 return i >= 0 ? name.substring(0, i).replace("/", ".") : ""; 194 } 195 }