1 /* 2 * Copyright (c) 2015, 2016, 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 jdk.internal.module; 27 28 import java.io.IOException; 29 import java.io.UncheckedIOException; 30 import java.nio.ByteBuffer; 31 import java.nio.channels.FileChannel; 32 import java.nio.file.Path; 33 import java.security.MessageDigest; 34 import java.security.NoSuchAlgorithmException; 35 import java.util.Collections; 36 import java.util.HashMap; 37 import java.util.Map; 38 import java.util.Objects; 39 import java.util.Set; 40 41 /** 42 * The result of hashing the contents of a number of module artifacts. 43 */ 44 45 public final class ModuleHashes { 46 47 /** 48 * A supplier of a message digest. 49 */ 50 public static interface HashSupplier { 51 byte[] generate(String algorithm); 52 } 53 54 private final String algorithm; 55 private final Map<String, byte[]> nameToHash; 56 57 /** 58 * Creates a {@code ModuleHashes}. 59 * 60 * @param algorithm the algorithm used to create the hashes 61 * @param nameToHash the map of module name to hash value 62 */ 63 public ModuleHashes(String algorithm, Map<String, byte[]> nameToHash) { 64 this.algorithm = algorithm; 65 this.nameToHash = Collections.unmodifiableMap(nameToHash); 66 } 67 68 /** 69 * Returns the algorithm used to hash the modules ("SHA-256" for example). 70 */ 71 public String algorithm() { 72 return algorithm; 73 } 74 75 /** 76 * Returns the set of module names for which hashes are recorded. 77 */ 78 public Set<String> names() { 79 return nameToHash.keySet(); 80 } 81 82 /** 83 * Returns the hash for the given module name, {@code null} 84 * if there is no hash recorded for the module. 85 */ 86 public byte[] hashFor(String mn) { 87 return nameToHash.get(mn); 88 } 89 90 /** 91 * Returns unmodifiable map of module name to hash 92 */ 93 public Map<String, byte[]> hashes() { 94 return nameToHash; 95 } 96 97 /** 98 * Computes the hash for the given file with the given message digest 99 * algorithm. 100 * 101 * @throws UncheckedIOException if an I/O error occurs 102 * @throws RuntimeException if the algorithm is not available 103 */ 104 public static byte[] computeHash(Path file, String algorithm) { 105 try { 106 MessageDigest md = MessageDigest.getInstance(algorithm); 107 108 // Ideally we would just mmap the file but this consumes too much 109 // memory when jlink is running concurrently on very large jmods 110 try (FileChannel fc = FileChannel.open(file)) { 111 ByteBuffer bb = ByteBuffer.allocate(32*1024); 112 while (fc.read(bb) > 0) { 113 bb.flip(); 114 md.update(bb); 115 assert bb.remaining() == 0; 116 bb.clear(); 117 } 118 } 119 120 return md.digest(); 121 } catch (NoSuchAlgorithmException e) { 122 throw new RuntimeException(e); 123 } catch (IOException ioe) { 124 throw new UncheckedIOException(ioe); 125 } 126 } 127 128 /** 129 * Computes the hash for every entry in the given map, returning a 130 * {@code ModuleHashes} to encapsulate the result. The map key is 131 * the entry name, typically the module name. The map value is the file 132 * path to the entry (module artifact). 133 * 134 * @return ModuleHashes that encapsulates the hashes 135 */ 136 public static ModuleHashes generate(Map<String, Path> map, String algorithm) { 137 Map<String, byte[]> nameToHash = new HashMap<>(); 138 for (Map.Entry<String, Path> entry: map.entrySet()) { 139 String name = entry.getKey(); 140 Path path = entry.getValue(); 141 nameToHash.put(name, computeHash(path, algorithm)); 142 } 143 return new ModuleHashes(algorithm, nameToHash); 144 } 145 146 /** 147 * This is used by jdk.internal.module.SystemModules class 148 * generated at link time. 149 */ 150 public static class Builder { 151 final String algorithm; 152 final Map<String, byte[]> nameToHash; 153 154 Builder(String algorithm, int initialCapacity) { 155 this.nameToHash = new HashMap<>(initialCapacity); 156 this.algorithm = Objects.requireNonNull(algorithm); 157 } 158 159 /** 160 * Sets the module hash for the given module name 161 */ 162 public Builder hashForModule(String mn, byte[] hash) { 163 nameToHash.put(mn, hash); 164 return this; 165 } 166 167 /** 168 * Builds a {@code ModuleHashes}. 169 */ 170 public ModuleHashes build() { 171 if (!nameToHash.isEmpty()) { 172 return new ModuleHashes(algorithm, nameToHash); 173 } else { 174 return null; 175 } 176 } 177 } 178 }