1 /* 2 * Copyright (c) 2015, 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 jdk.tools.jimage; 26 27 import java.io.File; 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.PrintWriter; 31 import java.nio.file.Files; 32 import java.nio.file.Path; 33 import java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.HashSet; 36 import java.util.LinkedHashMap; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Map.Entry; 40 import java.util.Set; 41 import java.util.function.Consumer; 42 import java.util.stream.Stream; 43 import jdk.internal.jimage.Archive; 44 import jdk.internal.jimage.ImageFileCreator; 45 import jdk.internal.jimage.ImageModuleData; 46 import jdk.internal.jimage.ImageModuleDataWriter; 47 48 /** 49 * 50 * Support for extracted image. 51 */ 52 public final class ExtractedImage { 53 54 /** 55 * An Archive backed by a directory. 56 */ 57 public class DirArchive implements Archive { 58 59 /** 60 * A File located in a Directory. 61 */ 62 private class FileEntry extends Archive.Entry { 63 64 private final long size; 65 private final Path path; 66 67 FileEntry(Path path, String name) { 68 super(DirArchive.this, getPathName(path), name, 69 Archive.Entry.EntryType.CLASS_OR_RESOURCE); 70 this.path = path; 71 try { 72 size = Files.size(path); 73 } catch (IOException ex) { 74 throw new RuntimeException(ex); 75 } 76 } 77 78 /** 79 * Returns the number of bytes of this file. 80 */ 81 @Override 82 public long size() { 83 return size; 84 } 85 86 @Override 87 public InputStream stream() throws IOException { 88 InputStream stream = Files.newInputStream(path); 89 open.add(stream); 90 return stream; 91 } 92 } 93 94 private final Path dirPath; 95 private final String moduleName; 96 private final List<InputStream> open = new ArrayList<>(); 97 private final int chop; 98 99 protected DirArchive(Path dirPath) throws IOException { 100 if (!Files.isDirectory(dirPath)) { 101 throw new IOException("Not a directory"); 102 } 103 chop = dirPath.toString().length() + 1; 104 this.moduleName = dirPath.getFileName().toString(); 105 this.dirPath = dirPath; 106 } 107 108 @Override 109 public String moduleName() { 110 return moduleName; 111 } 112 113 @Override 114 public Stream<Entry> entries() { 115 try { 116 return Files.walk(dirPath).map(this::toEntry).filter(n -> n != null); 117 } catch(IOException ex) { 118 throw new RuntimeException(ex); 119 } 120 } 121 122 private Archive.Entry toEntry(Path p) { 123 if (Files.isDirectory(p)) { 124 return null; 125 } 126 String name = getPathName(p).substring(chop); 127 if (name.startsWith("_")) { 128 return null; 129 } 130 if (verbose) { 131 String verboseName = moduleName + "/" + name; 132 log.println(verboseName); 133 } 134 135 return new FileEntry(p, name); 136 } 137 138 @Override 139 public void close() throws IOException { 140 IOException e = null; 141 for (InputStream stream : open) { 142 try { 143 stream.close(); 144 } catch (IOException ex) { 145 if (e == null) { 146 e = ex; 147 } else { 148 e.addSuppressed(ex); 149 } 150 } 151 } 152 if (e != null) { 153 throw e; 154 } 155 } 156 157 @Override 158 public void open() throws IOException { 159 // NOOP 160 } 161 } 162 private Map<String, Set<String>> modulePackages = new LinkedHashMap<>(); 163 private Set<Archive> archives = new HashSet<>(); 164 private final PrintWriter log; 165 private final boolean verbose; 166 private final String jdataName; 167 ExtractedImage(Path dirPath, PrintWriter log, 168 boolean verbose) throws IOException { 169 if (!Files.isDirectory(dirPath)) { 170 throw new IOException("Not a directory"); 171 } 172 List<String> jdataNameHolder = new ArrayList<>(); 173 Files.walk(dirPath, 1).forEach((p) -> { 174 try { 175 if (!dirPath.equals(p)) { 176 String name = getPathName(p); 177 if (name.endsWith(ImageModuleData.META_DATA_EXTENSION)) { 178 jdataNameHolder.add(p.getFileName().toString()); 179 List<String> lines = Files.readAllLines(p); 180 for (Entry<String, List<String>> entry 181 : ImageModuleDataWriter.toModulePackages(lines).entrySet()) { 182 Set<String> pkgs = new HashSet<>(); 183 pkgs.addAll(entry.getValue()); 184 modulePackages.put(entry.getKey(), pkgs); 185 } 186 modulePackages = Collections.unmodifiableMap(modulePackages); 187 } else { 188 if (Files.isDirectory(p)) { 189 Archive a = new DirArchive(p); 190 archives.add(a); 191 } 192 } 193 } 194 } catch (IOException ex) { 195 throw new RuntimeException(ex); 196 } 197 }); 198 archives = Collections.unmodifiableSet(archives); 199 this.log = log; 200 this.verbose = verbose; 201 if (jdataNameHolder.size() != 1) { 202 throw new IOException("Wrong module information"); 203 } 204 // The name of the metadata resource must be reused in the recreated jimage 205 String name = jdataNameHolder.get(0); 206 // Extension will be added when recreating the jimage 207 if (name.endsWith(ImageModuleData.META_DATA_EXTENSION)) { 208 name = name.substring(0, name.length() 209 - ImageModuleData.META_DATA_EXTENSION.length()); 210 } 211 jdataName = name; 212 } 213 214 void recreateJImage(Path path) throws IOException { 215 216 ImageFileCreator.recreateJimage(path, jdataName, archives, modulePackages); 217 } 218 219 private static String getPathName(Path path) { 220 return path.toString().replace(File.separatorChar, '/'); 221 } 222 }