Path: blob/master/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java
41159 views
/*1* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.internal.module;2627import java.io.IOException;28import java.io.InputStream;29import java.io.UncheckedIOException;30import java.lang.module.ModuleReader;31import java.lang.module.ModuleReference;32import java.nio.charset.StandardCharsets;33import java.security.MessageDigest;34import java.security.NoSuchAlgorithmException;35import java.util.Arrays;36import java.util.Collections;37import java.util.HashMap;38import java.util.Map;39import java.util.Objects;40import java.util.Set;41import java.util.TreeMap;42import java.util.function.Supplier;4344/**45* The result of hashing the contents of a number of module artifacts.46*/4748public final class ModuleHashes {4950/**51* A supplier of a message digest.52*/53public static interface HashSupplier {54byte[] generate(String algorithm);55}5657private final String algorithm;58private final Map<String, byte[]> nameToHash;5960/**61* Creates a {@code ModuleHashes}.62*63* @param algorithm the algorithm used to create the hashes64* @param nameToHash the map of module name to hash value65*/66ModuleHashes(String algorithm, Map<String, byte[]> nameToHash) {67this.algorithm = Objects.requireNonNull(algorithm);68this.nameToHash = Collections.unmodifiableMap(nameToHash);69}7071/**72* Returns the algorithm used to hash the modules ("SHA-256" for example).73*/74public String algorithm() {75return algorithm;76}7778/**79* Returns the set of module names for which hashes are recorded.80*/81public Set<String> names() {82return nameToHash.keySet();83}8485/**86* Returns the hash for the given module name, {@code null}87* if there is no hash recorded for the module.88*/89public byte[] hashFor(String mn) {90return nameToHash.get(mn);91}9293/**94* Returns unmodifiable map of module name to hash95*/96public Map<String, byte[]> hashes() {97return nameToHash;98}99100/**101* Computes a hash from the names and content of a module.102*103* @param reader the module reader to access the module content104* @param algorithm the name of the message digest algorithm to use105* @return the hash106* @throws IllegalArgumentException if digest algorithm is not supported107* @throws UncheckedIOException if an I/O error occurs108*/109private static byte[] computeHash(ModuleReader reader, String algorithm) {110MessageDigest md;111try {112md = MessageDigest.getInstance(algorithm);113} catch (NoSuchAlgorithmException e) {114throw new IllegalArgumentException(e);115}116try {117byte[] buf = new byte[32*1024];118reader.list().sorted().forEach(rn -> {119md.update(rn.getBytes(StandardCharsets.UTF_8));120try (InputStream in = reader.open(rn).orElseThrow()) {121int n;122while ((n = in.read(buf)) > 0) {123md.update(buf, 0, n);124}125} catch (IOException ioe) {126throw new UncheckedIOException(ioe);127}128});129} catch (IOException ioe) {130throw new UncheckedIOException(ioe);131}132return md.digest();133}134135/**136* Computes a hash from the names and content of a module.137*138* @param supplier supplies the module reader to access the module content139* @param algorithm the name of the message digest algorithm to use140* @return the hash141* @throws IllegalArgumentException if digest algorithm is not supported142* @throws UncheckedIOException if an I/O error occurs143*/144static byte[] computeHash(Supplier<ModuleReader> supplier, String algorithm) {145try (ModuleReader reader = supplier.get()) {146return computeHash(reader, algorithm);147} catch (IOException ioe) {148throw new UncheckedIOException(ioe);149}150}151152/**153* Computes the hash from the names and content of a set of modules. Returns154* a {@code ModuleHashes} to encapsulate the result.155*156* @param mrefs the set of modules157* @param algorithm the name of the message digest algorithm to use158* @return ModuleHashes that encapsulates the hashes159* @throws IllegalArgumentException if digest algorithm is not supported160* @throws UncheckedIOException if an I/O error occurs161*/162static ModuleHashes generate(Set<ModuleReference> mrefs, String algorithm) {163Map<String, byte[]> nameToHash = new TreeMap<>();164for (ModuleReference mref : mrefs) {165try (ModuleReader reader = mref.open()) {166byte[] hash = computeHash(reader, algorithm);167nameToHash.put(mref.descriptor().name(), hash);168} catch (IOException ioe) {169throw new UncheckedIOException(ioe);170}171}172return new ModuleHashes(algorithm, nameToHash);173}174175@Override176public int hashCode() {177int h = algorithm.hashCode();178for (Map.Entry<String, byte[]> e : nameToHash.entrySet()) {179h = h * 31 + e.getKey().hashCode();180h = h * 31 + Arrays.hashCode(e.getValue());181}182return h;183}184185@Override186public boolean equals(Object obj) {187if (!(obj instanceof ModuleHashes))188return false;189ModuleHashes other = (ModuleHashes) obj;190if (!algorithm.equals(other.algorithm)191|| nameToHash.size() != other.nameToHash.size())192return false;193for (Map.Entry<String, byte[]> e : nameToHash.entrySet()) {194String name = e.getKey();195byte[] hash = e.getValue();196if (!Arrays.equals(hash, other.nameToHash.get(name)))197return false;198}199return true;200}201202@Override203public String toString() {204StringBuilder sb = new StringBuilder(algorithm);205sb.append(" ");206nameToHash.entrySet()207.stream()208.sorted(Map.Entry.comparingByKey())209.forEach(e -> {210sb.append(e.getKey());211sb.append("=");212byte[] ba = e.getValue();213for (byte b : ba) {214sb.append(String.format("%02x", b & 0xff));215}216});217return sb.toString();218}219220/**221* This is used by jdk.internal.module.SystemModules class222* generated at link time.223*/224public static class Builder {225final String algorithm;226final Map<String, byte[]> nameToHash;227228Builder(String algorithm, int initialCapacity) {229this.nameToHash = new HashMap<>(initialCapacity);230this.algorithm = Objects.requireNonNull(algorithm);231}232233/**234* Sets the module hash for the given module name235*/236public Builder hashForModule(String mn, byte[] hash) {237nameToHash.put(mn, hash);238return this;239}240241/**242* Builds a {@code ModuleHashes}.243*/244public ModuleHashes build() {245if (!nameToHash.isEmpty()) {246return new ModuleHashes(algorithm, nameToHash);247} else {248return null;249}250}251}252}253254255