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 import jdk.internal.jimage.Resource; 30 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.OutputStream; 34 import java.io.UncheckedIOException; 35 import java.nio.file.Files; 36 import java.nio.file.Path; 37 import java.util.function.Consumer; 38 39 /** 40 * An Archive backed by an exploded representation on disk. 41 */ 42 public class ModuleArchive implements Archive { 43 private final Path classes; 44 private final Path cmds; 45 private final Path libs; 46 private final Path configs; 47 private final String moduleName; 48 49 public ModuleArchive(String moduleName, Path classes, Path cmds, 50 Path libs, Path configs) { 51 this.moduleName = moduleName; 52 this.classes = classes; 53 this.cmds = cmds; 54 this.libs = libs; 55 this.configs = configs; 56 } 57 58 @Override 59 public String moduleName() { 60 return moduleName; 61 } 62 63 @Override 64 public void visitResources(Consumer<Resource> consumer) { 65 if (classes == null) 66 return; 67 try{ 68 Files.walk(classes) 69 .sorted() 70 .filter(p -> !Files.isDirectory(p) 71 && !classes.relativize(p).toString().startsWith("_the.") 72 && !classes.relativize(p).toString().equals("javac_state")) 73 .map(this::toResource) 74 .forEach(consumer::accept); 75 } catch (IOException ioe) { 76 throw new UncheckedIOException(ioe); 77 } 78 } 79 80 private Resource toResource(Path path) { 81 try { 82 return new Resource(classes.relativize(path).toString().replace('\\','/'), 83 Files.size(path), 84 0 /* no compression support yet */); 85 } catch (IOException ioe) { 86 throw new UncheckedIOException(ioe); 87 } 88 } 89 90 private enum Section { 91 CLASSES, 92 CMDS, 93 LIBS, 94 CONFIGS 95 } 96 97 @Override 98 public void visitEntries(Consumer<Entry> consumer) { 99 try{ 100 if (classes != null) 101 Files.walk(classes) 102 .sorted() 103 .filter(p -> !Files.isDirectory(p) 104 && !classes.relativize(p).toString().startsWith("_the.") 105 && !classes.relativize(p).toString().equals("javac_state")) 106 .map(p -> toEntry(p, classes, Section.CLASSES)) 107 .forEach(consumer::accept); 108 if (cmds != null) 109 Files.walk(cmds) 110 .filter(p -> !Files.isDirectory(p)) 111 .map(p -> toEntry(p, cmds, Section.CMDS)) 112 .forEach(consumer::accept); 113 if (libs != null) 114 Files.walk(libs) 115 .filter(p -> !Files.isDirectory(p)) 116 .map(p -> toEntry(p, libs, Section.LIBS)) 117 .forEach(consumer::accept); 118 if (configs != null) 119 Files.walk(configs) 120 .filter(p -> !Files.isDirectory(p)) 121 .map(p -> toEntry(p, configs, Section.CONFIGS)) 122 .forEach(consumer::accept); 123 } catch (IOException ioe) { 124 throw new UncheckedIOException(ioe); 125 } 126 } 127 128 private static class FileEntry implements Entry { 129 private final String name; 130 private final InputStream is; 131 private final boolean isDirectory; 132 private final Section section; 133 FileEntry(String name, InputStream is, 134 boolean isDirectory, Section section) { 135 this.name = name; 136 this.is = is; 137 this.isDirectory = isDirectory; 138 this.section = section; 139 } 140 public String getName() { 141 return name; 142 } 143 public Section getSection() { 144 return section; 145 } 146 public InputStream getInputStream() { 147 return is; 148 } 149 public boolean isDirectory() { 150 return isDirectory; 151 } 152 } 153 154 private Entry toEntry(Path entryPath, Path basePath, Section section) { 155 try { 156 return new FileEntry(basePath.relativize(entryPath).toString().replace('\\', '/'), 157 Files.newInputStream(entryPath), false, 158 section); 159 } catch (IOException e) { 160 throw new UncheckedIOException(e); 161 } 162 } 163 164 @Override 165 public Consumer<Entry> defaultImageWriter(Path path, OutputStream out) { 166 return new DefaultEntryWriter(path, out); 167 } 168 169 private static class DefaultEntryWriter implements Consumer<Archive.Entry> { 170 private final Path root; 171 private final OutputStream out; 172 173 DefaultEntryWriter(Path root, OutputStream out) { 174 this.root = root; 175 this.out = out; 176 } 177 178 @Override 179 public void accept(Archive.Entry entry) { 180 try { 181 FileEntry e = (FileEntry)entry; 182 Section section = e.getSection(); 183 String filename = e.getName(); 184 185 try (InputStream in = entry.getInputStream()) { 186 switch (section) { 187 case CLASSES: 188 if (!filename.startsWith("_the.") && !filename.equals("javac_state")) 189 writeEntry(in); 190 break; 191 case LIBS: 192 writeEntry(in, destFile(nativeDir(filename), filename)); 193 break; 194 case CMDS: 195 Path path = destFile("bin", filename); 196 writeEntry(in, path); 197 path.toFile().setExecutable(true, false); 198 break; 199 case CONFIGS: 200 writeEntry(in, destFile("conf", filename)); 201 break; 202 default: 203 throw new InternalError("unexpected entry: " + filename); 204 } 205 } 206 } catch (IOException x) { 207 throw new UncheckedIOException(x); 208 } 209 } 210 211 private Path destFile(String dir, String filename) { 212 return root.resolve(dir).resolve(filename); 213 } 214 215 private static void writeEntry(InputStream in, Path dstFile) throws IOException { 216 if (Files.notExists(dstFile.getParent())) 217 Files.createDirectories(dstFile.getParent()); 218 Files.copy(in, dstFile); 219 } 220 221 private void writeEntry(InputStream in) throws IOException { 222 byte[] buf = new byte[8192]; 223 int n; 224 while ((n = in.read(buf)) > 0) 225 out.write(buf, 0, n); 226 } 227 228 private static String nativeDir(String filename) { 229 if (System.getProperty("os.name").startsWith("Windows")) { 230 if (filename.endsWith(".dll")) 231 return "bin"; 232 else 233 return "lib"; 234 } else { 235 return "lib"; 236 } 237 } 238 } 239 } 240