/*
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.module;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Supplier;
The result of hashing the contents of a number of module artifacts.
/**
* The result of hashing the contents of a number of module artifacts.
*/
public final class ModuleHashes {
A supplier of a message digest.
/**
* A supplier of a message digest.
*/
public static interface HashSupplier {
byte[] generate(String algorithm);
}
private final String algorithm;
private final Map<String, byte[]> nameToHash;
Creates a ModuleHashes
. Params: - algorithm – the algorithm used to create the hashes
- nameToHash – the map of module name to hash value
/**
* Creates a {@code ModuleHashes}.
*
* @param algorithm the algorithm used to create the hashes
* @param nameToHash the map of module name to hash value
*/
ModuleHashes(String algorithm, Map<String, byte[]> nameToHash) {
this.algorithm = Objects.requireNonNull(algorithm);
this.nameToHash = Collections.unmodifiableMap(nameToHash);
}
Returns the algorithm used to hash the modules ("SHA-256" for example).
/**
* Returns the algorithm used to hash the modules ("SHA-256" for example).
*/
public String algorithm() {
return algorithm;
}
Returns the set of module names for which hashes are recorded.
/**
* Returns the set of module names for which hashes are recorded.
*/
public Set<String> names() {
return nameToHash.keySet();
}
Returns the hash for the given module name, null
if there is no hash recorded for the module. /**
* Returns the hash for the given module name, {@code null}
* if there is no hash recorded for the module.
*/
public byte[] hashFor(String mn) {
return nameToHash.get(mn);
}
Returns unmodifiable map of module name to hash
/**
* Returns unmodifiable map of module name to hash
*/
public Map<String, byte[]> hashes() {
return nameToHash;
}
Computes a hash from the names and content of a module.
Params: - reader – the module reader to access the module content
- algorithm – the name of the message digest algorithm to use
Throws: - IllegalArgumentException – if digest algorithm is not supported
- UncheckedIOException – if an I/O error occurs
Returns: the hash
/**
* Computes a hash from the names and content of a module.
*
* @param reader the module reader to access the module content
* @param algorithm the name of the message digest algorithm to use
* @return the hash
* @throws IllegalArgumentException if digest algorithm is not supported
* @throws UncheckedIOException if an I/O error occurs
*/
private static byte[] computeHash(ModuleReader reader, String algorithm) {
MessageDigest md;
try {
md = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e);
}
try {
byte[] buf = new byte[32*1024];
reader.list().sorted().forEach(rn -> {
md.update(rn.getBytes(StandardCharsets.UTF_8));
try (InputStream in = reader.open(rn).orElseThrow()) {
int n;
while ((n = in.read(buf)) > 0) {
md.update(buf, 0, n);
}
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
});
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
return md.digest();
}
Computes a hash from the names and content of a module.
Params: - supplier – supplies the module reader to access the module content
- algorithm – the name of the message digest algorithm to use
Throws: - IllegalArgumentException – if digest algorithm is not supported
- UncheckedIOException – if an I/O error occurs
Returns: the hash
/**
* Computes a hash from the names and content of a module.
*
* @param supplier supplies the module reader to access the module content
* @param algorithm the name of the message digest algorithm to use
* @return the hash
* @throws IllegalArgumentException if digest algorithm is not supported
* @throws UncheckedIOException if an I/O error occurs
*/
static byte[] computeHash(Supplier<ModuleReader> supplier, String algorithm) {
try (ModuleReader reader = supplier.get()) {
return computeHash(reader, algorithm);
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
Computes the hash from the names and content of a set of modules. Returns a ModuleHashes
to encapsulate the result. Params: - mrefs – the set of modules
- algorithm – the name of the message digest algorithm to use
Throws: - IllegalArgumentException – if digest algorithm is not supported
- UncheckedIOException – if an I/O error occurs
Returns: ModuleHashes that encapsulates the hashes
/**
* Computes the hash from the names and content of a set of modules. Returns
* a {@code ModuleHashes} to encapsulate the result.
*
* @param mrefs the set of modules
* @param algorithm the name of the message digest algorithm to use
* @return ModuleHashes that encapsulates the hashes
* @throws IllegalArgumentException if digest algorithm is not supported
* @throws UncheckedIOException if an I/O error occurs
*/
static ModuleHashes generate(Set<ModuleReference> mrefs, String algorithm) {
Map<String, byte[]> nameToHash = new TreeMap<>();
for (ModuleReference mref : mrefs) {
try (ModuleReader reader = mref.open()) {
byte[] hash = computeHash(reader, algorithm);
nameToHash.put(mref.descriptor().name(), hash);
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
return new ModuleHashes(algorithm, nameToHash);
}
@Override
public int hashCode() {
int h = algorithm.hashCode();
for (Map.Entry<String, byte[]> e : nameToHash.entrySet()) {
h = h * 31 + e.getKey().hashCode();
h = h * 31 + Arrays.hashCode(e.getValue());
}
return h;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ModuleHashes))
return false;
ModuleHashes other = (ModuleHashes) obj;
if (!algorithm.equals(other.algorithm)
|| nameToHash.size() != other.nameToHash.size())
return false;
for (Map.Entry<String, byte[]> e : nameToHash.entrySet()) {
String name = e.getKey();
byte[] hash = e.getValue();
if (!Arrays.equals(hash, other.nameToHash.get(name)))
return false;
}
return true;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(algorithm);
sb.append(" ");
nameToHash.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.forEach(e -> {
sb.append(e.getKey());
sb.append("=");
byte[] ba = e.getValue();
for (byte b : ba) {
sb.append(String.format("%02x", b & 0xff));
}
});
return sb.toString();
}
This is used by jdk.internal.module.SystemModules class
generated at link time.
/**
* This is used by jdk.internal.module.SystemModules class
* generated at link time.
*/
public static class Builder {
final String algorithm;
final Map<String, byte[]> nameToHash;
Builder(String algorithm, int initialCapacity) {
this.nameToHash = new HashMap<>(initialCapacity);
this.algorithm = Objects.requireNonNull(algorithm);
}
Sets the module hash for the given module name
/**
* Sets the module hash for the given module name
*/
public Builder hashForModule(String mn, byte[] hash) {
nameToHash.put(mn, hash);
return this;
}
Builds a ModuleHashes
. /**
* Builds a {@code ModuleHashes}.
*/
public ModuleHashes build() {
if (!nameToHash.isEmpty()) {
return new ModuleHashes(algorithm, nameToHash);
} else {
return null;
}
}
}
}