Fri, 18 Jul 2014 10:43:41 -0700
8050804: (jdeps) Recommend supported API to replace use of JDK internal API
Reviewed-by: dfuchs
1 /*
2 * Copyright (c) 2012, 2014, 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 package com.sun.tools.jdeps;
27 import com.sun.tools.classfile.Annotation;
28 import com.sun.tools.classfile.ClassFile;
29 import com.sun.tools.classfile.ConstantPool;
30 import com.sun.tools.classfile.ConstantPoolException;
31 import com.sun.tools.classfile.RuntimeAnnotations_attribute;
32 import com.sun.tools.classfile.Dependencies.ClassFileError;
33 import java.io.IOException;
34 import java.nio.file.FileVisitResult;
35 import java.nio.file.Files;
36 import java.nio.file.Path;
37 import java.nio.file.Paths;
38 import java.nio.file.SimpleFileVisitor;
39 import java.nio.file.attribute.BasicFileAttributes;
40 import java.util.*;
42 import static com.sun.tools.classfile.Attribute.*;
44 /**
45 * ClassPath for Java SE and JDK
46 */
47 class PlatformClassPath {
48 private static final List<String> NON_PLATFORM_JARFILES =
49 Arrays.asList("alt-rt.jar", "jfxrt.jar", "ant-javafx.jar", "javafx-mx.jar");
50 private static final List<Archive> javaHomeArchives = init();
52 static List<Archive> getArchives() {
53 return javaHomeArchives;
54 }
56 private static List<Archive> init() {
57 List<Archive> result = new ArrayList<>();
58 Path home = Paths.get(System.getProperty("java.home"));
59 try {
60 if (home.endsWith("jre")) {
61 // jar files in <javahome>/jre/lib
62 result.addAll(addJarFiles(home.resolve("lib")));
63 if (home.getParent() != null) {
64 // add tools.jar and other JDK jar files
65 Path lib = home.getParent().resolve("lib");
66 if (Files.exists(lib)) {
67 result.addAll(addJarFiles(lib));
68 }
69 }
70 } else if (Files.exists(home.resolve("lib"))) {
71 // either a JRE or a jdk build image
72 Path classes = home.resolve("classes");
73 if (Files.isDirectory(classes)) {
74 // jdk build outputdir
75 result.add(new JDKArchive(classes));
76 }
77 // add other JAR files
78 result.addAll(addJarFiles(home.resolve("lib")));
79 } else {
80 throw new RuntimeException("\"" + home + "\" not a JDK home");
81 }
82 return result;
83 } catch (IOException e) {
84 throw new Error(e);
85 }
86 }
88 private static List<Archive> addJarFiles(final Path root) throws IOException {
89 final List<Archive> result = new ArrayList<>();
90 final Path ext = root.resolve("ext");
91 Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
92 @Override
93 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
94 throws IOException
95 {
96 if (dir.equals(root) || dir.equals(ext)) {
97 return FileVisitResult.CONTINUE;
98 } else {
99 // skip other cobundled JAR files
100 return FileVisitResult.SKIP_SUBTREE;
101 }
102 }
103 @Override
104 public FileVisitResult visitFile(Path p, BasicFileAttributes attrs)
105 throws IOException
106 {
107 String fn = p.getFileName().toString();
108 if (fn.endsWith(".jar")) {
109 // JDK may cobundle with JavaFX that doesn't belong to any profile
110 // Treat jfxrt.jar as regular Archive
111 result.add(NON_PLATFORM_JARFILES.contains(fn)
112 ? Archive.getInstance(p)
113 : new JDKArchive(p));
114 }
115 return FileVisitResult.CONTINUE;
116 }
117 });
118 return result;
119 }
121 /**
122 * A JDK archive is part of the JDK containing the Java SE API
123 * or implementation classes (i.e. JDK internal API)
124 */
125 static class JDKArchive extends Archive {
126 private static List<String> PROFILE_JARS = Arrays.asList("rt.jar", "jce.jar");
127 public static boolean isProfileArchive(Archive archive) {
128 if (archive instanceof JDKArchive) {
129 return PROFILE_JARS.contains(archive.getName());
130 }
131 return false;
132 }
134 private final Map<String,Boolean> exportedPackages = new HashMap<>();
135 private final Map<String,Boolean> exportedTypes = new HashMap<>();
136 JDKArchive(Path p) throws IOException {
137 super(p, ClassFileReader.newInstance(p));
138 }
140 /**
141 * Tests if a given fully-qualified name is an exported type.
142 */
143 public boolean isExported(String cn) {
144 int i = cn.lastIndexOf('.');
145 String pn = i > 0 ? cn.substring(0, i) : "";
147 boolean isJdkExported = isExportedPackage(pn);
148 if (exportedTypes.containsKey(cn)) {
149 return exportedTypes.get(cn);
150 }
151 return isJdkExported;
152 }
154 /**
155 * Tests if a given package name is exported.
156 */
157 public boolean isExportedPackage(String pn) {
158 if (Profile.getProfile(pn) != null) {
159 return true;
160 }
161 return exportedPackages.containsKey(pn) ? exportedPackages.get(pn) : false;
162 }
164 private static final String JDK_EXPORTED_ANNOTATION = "Ljdk/Exported;";
165 private Boolean isJdkExported(ClassFile cf) throws ConstantPoolException {
166 RuntimeAnnotations_attribute attr = (RuntimeAnnotations_attribute)
167 cf.attributes.get(RuntimeVisibleAnnotations);
168 if (attr != null) {
169 for (int i = 0; i < attr.annotations.length; i++) {
170 Annotation ann = attr.annotations[i];
171 String annType = cf.constant_pool.getUTF8Value(ann.type_index);
172 if (JDK_EXPORTED_ANNOTATION.equals(annType)) {
173 boolean isJdkExported = true;
174 for (int j = 0; j < ann.num_element_value_pairs; j++) {
175 Annotation.element_value_pair pair = ann.element_value_pairs[j];
176 Annotation.Primitive_element_value ev = (Annotation.Primitive_element_value) pair.value;
177 ConstantPool.CONSTANT_Integer_info info = (ConstantPool.CONSTANT_Integer_info)
178 cf.constant_pool.get(ev.const_value_index);
179 isJdkExported = info.value != 0;
180 }
181 return Boolean.valueOf(isJdkExported);
182 }
183 }
184 }
185 return null;
186 }
188 void processJdkExported(ClassFile cf) throws IOException {
189 try {
190 String cn = cf.getName();
191 String pn = cn.substring(0, cn.lastIndexOf('/')).replace('/', '.');
193 Boolean b = isJdkExported(cf);
194 if (b != null) {
195 exportedTypes.put(cn.replace('/', '.'), b);
196 }
197 if (!exportedPackages.containsKey(pn)) {
198 // check if package-info.class has @jdk.Exported
199 Boolean isJdkExported = null;
200 ClassFile pcf = reader().getClassFile(cn.substring(0, cn.lastIndexOf('/')+1) + "package-info");
201 if (pcf != null) {
202 isJdkExported = isJdkExported(pcf);
203 }
204 if (isJdkExported != null) {
205 exportedPackages.put(pn, isJdkExported);
206 }
207 }
208 } catch (ConstantPoolException e) {
209 throw new ClassFileError(e);
210 }
211 }
212 }
213 }