Thu, 27 Apr 2017 16:18:18 -0700
8176329: jdeps to detect MR jar file and output a warning
Reviewed-by: mchung
1 /*
2 * Copyright (c) 2012, 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 package com.sun.tools.jdeps;
27 import com.sun.tools.classfile.ClassFile;
28 import com.sun.tools.classfile.ConstantPoolException;
29 import com.sun.tools.classfile.Dependencies.ClassFileError;
30 import java.io.*;
31 import java.nio.file.FileVisitResult;
32 import java.nio.file.Files;
33 import java.nio.file.Path;
34 import java.nio.file.SimpleFileVisitor;
35 import java.nio.file.attribute.BasicFileAttributes;
36 import java.util.*;
37 import java.util.jar.Attributes;
38 import java.util.jar.JarEntry;
39 import java.util.jar.JarFile;
40 import java.util.jar.Manifest;
42 /**
43 * ClassFileReader reads ClassFile(s) of a given path that can be
44 * a .class file, a directory, or a JAR file.
45 */
46 public class ClassFileReader {
47 /**
48 * Returns a ClassFileReader instance of a given path.
49 */
50 public static ClassFileReader newInstance(Path path) throws IOException {
51 if (!Files.exists(path)) {
52 throw new FileNotFoundException(path.toString());
53 }
55 if (Files.isDirectory(path)) {
56 return new DirectoryReader(path);
57 } else if (path.getFileName().toString().endsWith(".jar")) {
58 return new JarFileReader(path);
59 } else {
60 return new ClassFileReader(path);
61 }
62 }
64 /**
65 * Returns a ClassFileReader instance of a given JarFile.
66 */
67 public static ClassFileReader newInstance(Path path, JarFile jf) throws IOException {
68 return new JarFileReader(path, jf);
69 }
71 protected final Path path;
72 protected final String baseFileName;
73 protected final List<String> skippedEntries = new ArrayList<>();
74 protected ClassFileReader(Path path) {
75 this.path = path;
76 this.baseFileName = path.getFileName() != null
77 ? path.getFileName().toString()
78 : path.toString();
79 }
81 public String getFileName() {
82 return baseFileName;
83 }
85 public List<String> skippedEntries() {
86 return skippedEntries;
87 }
89 /**
90 * Returns the ClassFile matching the given binary name
91 * or a fully-qualified class name.
92 */
93 public ClassFile getClassFile(String name) throws IOException {
94 if (name.indexOf('.') > 0) {
95 int i = name.lastIndexOf('.');
96 String pathname = name.replace('.', File.separatorChar) + ".class";
97 if (baseFileName.equals(pathname) ||
98 baseFileName.equals(pathname.substring(0, i) + "$" +
99 pathname.substring(i+1, pathname.length()))) {
100 return readClassFile(path);
101 }
102 } else {
103 if (baseFileName.equals(name.replace('/', File.separatorChar) + ".class")) {
104 return readClassFile(path);
105 }
106 }
107 return null;
108 }
110 public Iterable<ClassFile> getClassFiles() throws IOException {
111 return new Iterable<ClassFile>() {
112 public Iterator<ClassFile> iterator() {
113 return new FileIterator();
114 }
115 };
116 }
118 protected ClassFile readClassFile(Path p) throws IOException {
119 InputStream is = null;
120 try {
121 is = Files.newInputStream(p);
122 return ClassFile.read(is);
123 } catch (ConstantPoolException e) {
124 throw new ClassFileError(e);
125 } finally {
126 if (is != null) {
127 is.close();
128 }
129 }
130 }
132 class FileIterator implements Iterator<ClassFile> {
133 int count;
134 FileIterator() {
135 this.count = 0;
136 }
137 public boolean hasNext() {
138 return count == 0 && baseFileName.endsWith(".class");
139 }
141 public ClassFile next() {
142 if (!hasNext()) {
143 throw new NoSuchElementException();
144 }
145 try {
146 ClassFile cf = readClassFile(path);
147 count++;
148 return cf;
149 } catch (IOException e) {
150 throw new ClassFileError(e);
151 }
152 }
154 public void remove() {
155 throw new UnsupportedOperationException("Not supported yet.");
156 }
157 }
159 public boolean isMultiReleaseJar() throws IOException { return false; }
161 public String toString() {
162 return path.toString();
163 }
165 private static class DirectoryReader extends ClassFileReader {
166 DirectoryReader(Path path) throws IOException {
167 super(path);
168 }
170 public ClassFile getClassFile(String name) throws IOException {
171 if (name.indexOf('.') > 0) {
172 int i = name.lastIndexOf('.');
173 String pathname = name.replace('.', File.separatorChar) + ".class";
174 Path p = path.resolve(pathname);
175 if (!Files.exists(p)) {
176 p = path.resolve(pathname.substring(0, i) + "$" +
177 pathname.substring(i+1, pathname.length()));
178 }
179 if (Files.exists(p)) {
180 return readClassFile(p);
181 }
182 } else {
183 Path p = path.resolve(name + ".class");
184 if (Files.exists(p)) {
185 return readClassFile(p);
186 }
187 }
188 return null;
189 }
191 public Iterable<ClassFile> getClassFiles() throws IOException {
192 final Iterator<ClassFile> iter = new DirectoryIterator();
193 return new Iterable<ClassFile>() {
194 public Iterator<ClassFile> iterator() {
195 return iter;
196 }
197 };
198 }
200 private List<Path> walkTree(Path dir) throws IOException {
201 final List<Path> files = new ArrayList<Path>();
202 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
203 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
204 throws IOException {
205 if (file.getFileName().toString().endsWith(".class")) {
206 files.add(file);
207 }
208 return FileVisitResult.CONTINUE;
209 }
210 });
211 return files;
212 }
214 class DirectoryIterator implements Iterator<ClassFile> {
215 private List<Path> entries;
216 private int index = 0;
217 DirectoryIterator() throws IOException {
218 entries = walkTree(path);
219 index = 0;
220 }
222 public boolean hasNext() {
223 return index != entries.size();
224 }
226 public ClassFile next() {
227 if (!hasNext()) {
228 throw new NoSuchElementException();
229 }
230 Path path = entries.get(index++);
231 try {
232 return readClassFile(path);
233 } catch (IOException e) {
234 throw new ClassFileError(e);
235 }
236 }
238 public void remove() {
239 throw new UnsupportedOperationException("Not supported yet.");
240 }
241 }
242 }
244 static class JarFileReader extends ClassFileReader {
245 private final JarFile jarfile;
246 JarFileReader(Path path) throws IOException {
247 this(path, new JarFile(path.toFile(), false));
248 }
250 JarFileReader(Path path, JarFile jf) throws IOException {
251 super(path);
252 this.jarfile = jf;
253 }
255 public ClassFile getClassFile(String name) throws IOException {
256 if (name.indexOf('.') > 0) {
257 int i = name.lastIndexOf('.');
258 String entryName = name.replace('.', '/') + ".class";
259 JarEntry e = jarfile.getJarEntry(entryName);
260 if (e == null) {
261 e = jarfile.getJarEntry(entryName.substring(0, i) + "$"
262 + entryName.substring(i + 1, entryName.length()));
263 }
264 if (e != null) {
265 return readClassFile(jarfile, e);
266 }
267 } else {
268 JarEntry e = jarfile.getJarEntry(name + ".class");
269 if (e != null) {
270 return readClassFile(jarfile, e);
271 }
272 }
273 return null;
274 }
276 protected ClassFile readClassFile(JarFile jarfile, JarEntry e) throws IOException {
277 InputStream is = null;
278 try {
279 is = jarfile.getInputStream(e);
280 return ClassFile.read(is);
281 } catch (ConstantPoolException ex) {
282 throw new ClassFileError(ex);
283 } finally {
284 if (is != null)
285 is.close();
286 }
287 }
289 public Iterable<ClassFile> getClassFiles() throws IOException {
290 final Iterator<ClassFile> iter = new JarFileIterator(this, jarfile);
291 return new Iterable<ClassFile>() {
292 public Iterator<ClassFile> iterator() {
293 return iter;
294 }
295 };
296 }
298 @Override
299 public boolean isMultiReleaseJar() throws IOException {
300 Manifest mf = this.jarfile.getManifest();
301 if (mf != null) {
302 Attributes atts = mf.getMainAttributes();
303 return "true".equalsIgnoreCase(atts.getValue("Multi-Release"));
304 }
305 return false;
306 }
307 }
309 class JarFileIterator implements Iterator<ClassFile> {
310 protected final JarFileReader reader;
311 protected Enumeration<JarEntry> entries;
312 protected JarFile jf;
313 protected JarEntry nextEntry;
314 protected ClassFile cf;
315 JarFileIterator(JarFileReader reader) {
316 this(reader, null);
317 }
318 JarFileIterator(JarFileReader reader, JarFile jarfile) {
319 this.reader = reader;
320 setJarFile(jarfile);
321 }
323 void setJarFile(JarFile jarfile) {
324 if (jarfile == null) return;
326 this.jf = jarfile;
327 this.entries = jf.entries();
328 this.nextEntry = nextEntry();
329 }
331 public boolean hasNext() {
332 if (nextEntry != null && cf != null) {
333 return true;
334 }
335 while (nextEntry != null) {
336 try {
337 cf = reader.readClassFile(jf, nextEntry);
338 return true;
339 } catch (ClassFileError | IOException ex) {
340 skippedEntries.add(nextEntry.getName());
341 }
342 nextEntry = nextEntry();
343 }
344 return false;
345 }
347 public ClassFile next() {
348 if (!hasNext()) {
349 throw new NoSuchElementException();
350 }
351 ClassFile classFile = cf;
352 cf = null;
353 nextEntry = nextEntry();
354 return classFile;
355 }
357 protected JarEntry nextEntry() {
358 while (entries.hasMoreElements()) {
359 JarEntry e = entries.nextElement();
360 String name = e.getName();
361 if (name.endsWith(".class")) {
362 return e;
363 }
364 }
365 return null;
366 }
368 public void remove() {
369 throw new UnsupportedOperationException("Not supported yet.");
370 }
371 }
372 }