1 /* 2 * Copyright (c) 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 26 package build.tools.module; 27 28 import jdk.internal.jimage.Archive; 29 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.UncheckedIOException; 33 import java.nio.file.Files; 34 import java.nio.file.Path; 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.stream.Collectors; 38 import java.util.stream.Stream; 39 import jdk.internal.jimage.Archive.Entry.EntryType; 40 41 /** 42 * An Archive backed by an exploded representation on disk. 43 */ 44 public class ModuleArchive implements Archive { 45 private final Path classes; 46 private final Path cmds; 47 private final Path libs; 48 private final Path configs; 49 private final String moduleName; 50 51 private final List<InputStream> opened = new ArrayList<>(); 52 53 public ModuleArchive(String moduleName, Path classes, Path cmds, 54 Path libs, Path configs) { 55 this.moduleName = moduleName; 56 this.classes = classes; 57 this.cmds = cmds; 58 this.libs = libs; 59 this.configs = configs; 60 } 61 62 @Override 63 public String moduleName() { 64 return moduleName; 65 } 66 67 @Override 68 public void open() throws IOException { 69 // NOOP 70 } 71 72 @Override 73 public void close() throws IOException { 74 IOException e = null; 75 for (InputStream stream : opened) { 76 try { 77 stream.close(); 78 } catch (IOException ex) { 79 if (e == null) { 80 e = ex; 81 } else { 82 e.addSuppressed(ex); 83 } 84 } 85 } 86 if (e != null) { 87 throw e; 88 } 89 } 90 91 @Override 92 public Stream<Entry> entries() { 93 List<Entry> entries = new ArrayList<>(); 94 try { 95 /* 96 * This code should be revisited to avoid buffering of the entries. 97 * 1) Do we really need sorting classes? This force buffering of entries. 98 * libs, cmds and configs are not sorted. 99 * 2) I/O streams should be concatenated instead of buffering into 100 * entries list. 101 * 3) Close I/O streams in a close handler. 102 */ 103 if (classes != null) { 104 try (Stream<Path> stream = Files.walk(classes)) { 105 entries.addAll(stream 106 .filter(p -> !Files.isDirectory(p) 107 && !classes.relativize(p).toString().startsWith("_the.") 108 && !classes.relativize(p).toString().equals("javac_state")) 109 .sorted() 110 .map(p -> toEntry(p, classes, EntryType.CLASS_OR_RESOURCE)) 111 .collect(Collectors.toList())); 112 } 113 } 114 if (cmds != null) { 115 try (Stream<Path> stream = Files.walk(cmds)) { 116 entries.addAll(stream 117 .filter(p -> !Files.isDirectory(p)) 118 .map(p -> toEntry(p, cmds, EntryType.NATIVE_CMD)) 119 .collect(Collectors.toList())); 120 } 121 } 122 if (libs != null) { 123 try (Stream<Path> stream = Files.walk(libs)) { 124 entries.addAll(stream 125 .filter(p -> !Files.isDirectory(p)) 126 .map(p -> toEntry(p, libs, EntryType.NATIVE_LIB)) 127 .collect(Collectors.toList())); 128 } 129 } 130 if (configs != null) { 131 try (Stream<Path> stream = Files.walk(configs)) { 132 entries.addAll(stream 133 .filter(p -> !Files.isDirectory(p)) 134 .map(p -> toEntry(p, configs, EntryType.CONFIG)) 135 .collect(Collectors.toList())); 136 } 137 } 138 } catch (IOException ioe) { 139 throw new UncheckedIOException(ioe); 140 } 141 return entries.stream(); 142 } 143 144 private class FileEntry extends Entry { 145 private final boolean isDirectory; 146 private final long size; 147 private final Path entryPath; 148 FileEntry(Path entryPath, String path, EntryType type, 149 boolean isDirectory, long size) { 150 super(ModuleArchive.this, path, path, type); 151 this.entryPath = entryPath; 152 this.isDirectory = isDirectory; 153 this.size = size; 154 } 155 156 public boolean isDirectory() { 157 return isDirectory; 158 } 159 160 @Override 161 public long size() { 162 return size; 163 } 164 165 @Override 166 public InputStream stream() throws IOException { 167 InputStream stream = Files.newInputStream(entryPath); 168 opened.add(stream); 169 return stream; 170 } 171 } 172 173 private Entry toEntry(Path entryPath, Path basePath, EntryType section) { 174 try { 175 String path = basePath.relativize(entryPath).toString().replace('\\', '/'); 176 return new FileEntry(entryPath, path, section, 177 false, Files.size(entryPath)); 178 } catch (IOException e) { 179 throw new UncheckedIOException(e); 180 } 181 } 182 } 183