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 System.out.println("Module name " + this.moduleName); 106 this.dirPath = dirPath; 107 } 108 109 @Override 110 public String moduleName() { 111 return moduleName; 112 } 113 114 @Override 115 public Stream<Entry> entries() { 116 try { 117 return Files.walk(dirPath).map(this::toEntry).filter(n -> n != null); 118 } catch(IOException ex) { 119 throw new RuntimeException(ex); 120 } 121 } 122 123 private Archive.Entry toEntry(Path p) { 124 if (Files.isDirectory(p)) { 125 return null; 126 } 127 String name = getPathName(p).substring(chop); 128 if (name.startsWith("_")) { 129 return null; 130 } 131 if (verbose) { 132 String verboseName = moduleName + "/" + name; 133 log.println(verboseName); 134 } 135 136 return new FileEntry(p, name); 137 } 138 139 @Override 140 public void close() throws IOException { 141 IOException e = null; 142 for (InputStream stream : open) { 143 try { 144 stream.close(); 145 } catch (IOException ex) { 146 if (e == null) { 147 e = ex; 148 } else { 149 e.addSuppressed(ex); 150 } 151 } 152 } 153 if (e != null) { 154 throw e; 155 } 156 } 157 158 @Override 159 public void open() throws IOException { 160 // NOOP 161 } 162 } 163 private Map<String, Set<String>> modulePackages = new LinkedHashMap<>(); 164 private Set<Archive> archives = new HashSet<>(); 165 private final PrintWriter log; 166 private final boolean verbose; 167 private final String jdataName; 168 ExtractedImage(Path dirPath, PrintWriter log, 169 boolean verbose) throws IOException { 170 if (!Files.isDirectory(dirPath)) { 171 throw new IOException("Not a directory"); 172 } 173 List<String> jdataNameHolder = new ArrayList<>(); 174 Files.walk(dirPath, 1).forEach((p) -> { 175 try { 176 if (!dirPath.equals(p)) { 177 String name = getPathName(p); 178 if (name.endsWith(ImageModuleData.META_DATA_EXTENSION)) { 179 jdataNameHolder.add(p.getFileName().toString()); 180 List<String> lines = Files.readAllLines(p); 181 for (Entry<String, List<String>> entry 182 : ImageModuleDataWriter.toModulePackages(lines).entrySet()) { 183 Set<String> pkgs = new HashSet<>(); 184 pkgs.addAll(entry.getValue()); 185 modulePackages.put(entry.getKey(), pkgs); 186 } 187 modulePackages = Collections.unmodifiableMap(modulePackages); 188 } else { 189 if (Files.isDirectory(p)) { 190 Archive a = new DirArchive(p); 191 archives.add(a); 192 } 193 } 194 } 195 } catch (IOException ex) { 196 throw new RuntimeException(ex); 197 } 198 }); 199 archives = Collections.unmodifiableSet(archives); 200 this.log = log; 201 this.verbose = verbose; 202 if (jdataNameHolder.size() != 1) { 203 throw new IOException("Wrong module information"); 204 } 205 // The name of the metadata resource must be reused in the recreated jimage 206 String name = jdataNameHolder.get(0); 207 // Extension will be added when recreating the jimage 208 if (name.endsWith(ImageModuleData.META_DATA_EXTENSION)) { 209 name = name.substring(0, name.length() 210 - ImageModuleData.META_DATA_EXTENSION.length()); 211 } 212 jdataName = name; 213 } 214 215 void recreateJImage(Path path) throws IOException { 216 217 ImageFileCreator.recreateJimage(path, jdataName, archives, modulePackages); 218 } 219 220 private static String getPathName(Path path) { 221 return path.toString().replace(File.separatorChar, '/'); 222 } 223 }