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