提交 75da6813 编写于 作者: C coffeys

8189969: Manifest better manifest entries

Reviewed-by: weijun, igerasim
上级 1a4abce2
......@@ -26,8 +26,10 @@
package sun.security.util;
import java.security.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.io.ByteArrayOutputStream;
import java.util.List;
/**
* This class is used to compute digests on sections of the Manifest.
......@@ -39,7 +41,7 @@ public class ManifestDigester {
/** the raw bytes of the manifest */
private byte rawBytes[];
/** the offset/length pair for a section */
/** the entries grouped by names */
private HashMap<String, Entry> entries; // key is a UTF-8 string
/** state returned by findSection */
......@@ -120,8 +122,8 @@ public class ManifestDigester {
return; // XXX: exception?
// create an entry for main attributes
entries.put(MF_MAIN_ATTRS,
new Entry(0, pos.endOfSection + 1, pos.startOfNext, rawBytes));
entries.put(MF_MAIN_ATTRS, new Entry().addSection(
new Section(0, pos.endOfSection + 1, pos.startOfNext, rawBytes)));
int start = pos.startOfNext;
while(findSection(start, pos)) {
......@@ -167,9 +169,10 @@ public class ManifestDigester {
}
}
entries.put(nameBuf.toString(),
new Entry(start, sectionLen, sectionLenWithBlank,
rawBytes));
entries.computeIfAbsent(nameBuf.toString(),
dummy -> new Entry())
.addSection(new Section(start, sectionLen,
sectionLenWithBlank, rawBytes));
} catch (java.io.UnsupportedEncodingException uee) {
throw new IllegalStateException(
......@@ -192,13 +195,52 @@ public class ManifestDigester {
}
public static class Entry {
// One Entry for one name, and one name can have multiple sections.
// According to the JAR File Specification: "If there are multiple
// individual sections for the same file entry, the attributes in
// these sections are merged."
private List<Section> sections = new ArrayList<>();
boolean oldStyle;
private Entry addSection(Section sec)
{
sections.add(sec);
return this;
}
public byte[] digest(MessageDigest md)
{
md.reset();
for (Section sec : sections) {
if (oldStyle) {
Section.doOldStyle(md, sec.rawBytes, sec.offset, sec.lengthWithBlankLine);
} else {
md.update(sec.rawBytes, sec.offset, sec.lengthWithBlankLine);
}
}
return md.digest();
}
/** Netscape doesn't include the new line. Intel and JavaSoft do */
public byte[] digestWorkaround(MessageDigest md)
{
md.reset();
for (Section sec : sections) {
md.update(sec.rawBytes, sec.offset, sec.length);
}
return md.digest();
}
}
private static class Section {
int offset;
int length;
int lengthWithBlankLine;
byte[] rawBytes;
boolean oldStyle;
public Entry(int offset, int length,
public Section(int offset, int length,
int lengthWithBlankLine, byte[] rawBytes)
{
this.offset = offset;
......@@ -207,18 +249,7 @@ public class ManifestDigester {
this.rawBytes = rawBytes;
}
public byte[] digest(MessageDigest md)
{
md.reset();
if (oldStyle) {
doOldStyle(md,rawBytes, offset, lengthWithBlankLine);
} else {
md.update(rawBytes, offset, lengthWithBlankLine);
}
return md.digest();
}
private void doOldStyle(MessageDigest md,
private static void doOldStyle(MessageDigest md,
byte[] bytes,
int offset,
int length)
......@@ -242,16 +273,6 @@ public class ManifestDigester {
}
md.update(bytes, start, i-start);
}
/** Netscape doesn't include the new line. Intel and JavaSoft do */
public byte[] digestWorkaround(MessageDigest md)
{
md.reset();
md.update(rawBytes, offset, length);
return md.digest();
}
}
public Entry get(String name, boolean oldStyle) {
......
......@@ -23,7 +23,6 @@
*/
import jdk.testlibrary.ProcessTools;
import jdk.testlibrary.JarUtils;
import javax.security.auth.Subject;
import javax.security.auth.x500.X500Principal;
......@@ -101,7 +100,7 @@ public class NestedActions {
public static void main(String[] args) throws IOException {
if (args.length > 0) {
if ("jar".equals(args[0]) && args.length > 2) {
JarUtils.createJar(args[1], Paths.get(TEST_CLASSES + FS),
createJar(args[1],
Arrays.copyOfRange(args, 2, args.length));
} else {
runJava(args);
......@@ -111,6 +110,21 @@ public class NestedActions {
}
}
static void createJar(String dest, String... files) throws IOException {
System.out.println("Create " + dest + " with the following content:");
try (JarOutputStream jos = new JarOutputStream(
new FileOutputStream(dest), new Manifest())) {
for (String file : files) {
System.out.println(" " + file);
jos.putNextEntry(new JarEntry(file));
try (FileInputStream fis = new FileInputStream(
TEST_CLASSES + FS + file)) {
jdk.testlibrary.Utils.transferTo(fis, jos);
}
}
}
}
static void runJava(String[] args) {
......
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
......@@ -23,15 +23,17 @@
package jdk.testlibrary;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
......@@ -42,45 +44,30 @@ import java.util.jar.Manifest;
*/
public final class JarUtils {
/**
* Create jar file with specified files. If a specified file does not exist,
* a new jar entry will be created with the file name itself as the content.
*/
public static void createJar(String dest, Path filesLocation,
String... fileNames) throws IOException {
public static void createJar(String dest, String... files)
throws IOException {
try (JarOutputStream jos = new JarOutputStream(
new FileOutputStream(dest), new Manifest())) {
for (String fileName : fileNames) {
for (String file : files) {
System.out.println(String.format("Adding %s to %s",
fileName, dest));
file, dest));
// add an archive entry, and write a file
jos.putNextEntry(new JarEntry(fileName));
File file;
if (filesLocation != null) {
file = filesLocation.resolve(fileName).toFile();
} else {
file = new File(fileName);
}
jos.putNextEntry(new JarEntry(file));
try (FileInputStream fis = new FileInputStream(file)) {
Utils.transferBetweenStreams(fis, jos);
Utils.transferTo(fis, jos);
} catch (FileNotFoundException e) {
jos.write(fileName.getBytes());
jos.write(file.getBytes());
}
}
}
System.out.println();
}
/**
* Create jar file with specified files from current directory.
*/
public static void createJar(String dest, String... files)
throws IOException {
createJar(dest, null, files);
}
/**
* Add or remove specified files to existing jar file. If a specified file
* to be updated or added does not exist, the jar entry will be created
......@@ -96,70 +83,93 @@ public final class JarUtils {
*/
public static void updateJar(String src, String dest, String... files)
throws IOException {
Map<String,Object> changes = new HashMap<>();
boolean update = true;
for (String file : files) {
if (file.equals("-")) {
update = false;
} else if (update) {
try {
Path p = Paths.get(file);
if (Files.exists(p)) {
changes.put(file, p);
} else {
changes.put(file, file);
}
} catch (InvalidPathException e) {
// Fallback if file not a valid Path.
changes.put(file, file);
}
} else {
changes.put(file, Boolean.FALSE);
}
}
updateJar(src, dest, changes);
}
/**
* Update content of a jar file.
*
* @param src the original jar file name
* @param dest the new jar file name
* @param changes a map of changes, key is jar entry name, value is content.
* Value can be Path, byte[] or String. If key exists in
* src but value is Boolean FALSE. The entry is removed.
* Existing entries in src not a key is unmodified.
* @throws IOException
*/
public static void updateJar(String src, String dest,
Map<String,Object> changes)
throws IOException {
// What if input changes is immutable?
changes = new HashMap<>(changes);
System.out.printf("Creating %s from %s...\n", dest, src);
try (JarOutputStream jos = new JarOutputStream(
new FileOutputStream(dest))) {
// copy each old entry into destination unless the entry name
// is in the updated list
List<String> updatedFiles = new ArrayList<>();
try (JarFile srcJarFile = new JarFile(src)) {
Enumeration<JarEntry> entries = srcJarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String name = entry.getName();
boolean found = false;
boolean update = true;
for (String file : files) {
if (file.equals("-")) {
update = false;
} else if (name.equals(file)) {
updatedFiles.add(file);
found = true;
break;
}
}
if (found) {
if (update) {
System.out.println(String.format("Updating %s with %s",
dest, name));
jos.putNextEntry(new JarEntry(name));
try (FileInputStream fis = new FileInputStream(name)) {
Utils.transferBetweenStreams(fis, jos);
} catch (FileNotFoundException e) {
jos.write(name.getBytes());
}
} else {
System.out.println(String.format("Removing %s from %s",
name, dest));
}
if (changes.containsKey(name)) {
System.out.println(String.format("- Update %s", name));
updateEntry(jos, name, changes.get(name));
changes.remove(name);
} else {
System.out.println(String.format("Copying %s to %s",
name, dest));
System.out.println(String.format("- Copy %s", name));
jos.putNextEntry(entry);
Utils.transferBetweenStreams(srcJarFile.
getInputStream(entry), jos);
Utils.transferTo(srcJarFile.getInputStream(entry), jos);
}
}
}
// append new files
for (String file : files) {
if (file.equals("-")) {
break;
}
if (!updatedFiles.contains(file)) {
System.out.println(String.format("Adding %s with %s",
dest, file));
jos.putNextEntry(new JarEntry(file));
try (FileInputStream fis = new FileInputStream(file)) {
Utils.transferBetweenStreams(fis, jos);
} catch (FileNotFoundException e) {
jos.write(file.getBytes());
}
}
for (Map.Entry<String, Object> e : changes.entrySet()) {
System.out.println(String.format("- Add %s", e.getKey()));
updateEntry(jos, e.getKey(), e.getValue());
}
}
System.out.println();
}
}
\ No newline at end of file
private static void updateEntry(JarOutputStream jos, String name, Object content)
throws IOException {
if (content instanceof Boolean) {
if (((Boolean) content).booleanValue()) {
throw new RuntimeException("Boolean value must be FALSE");
}
} else {
jos.putNextEntry(new JarEntry(name));
if (content instanceof Path) {
Utils.transferTo(Files.newInputStream((Path) content), jos);
} else if (content instanceof byte[]) {
jos.write((byte[]) content);
} else if (content instanceof String) {
jos.write(((String) content).getBytes());
} else {
throw new RuntimeException("Unknown type " + content.getClass());
}
}
}
}
......@@ -364,6 +364,21 @@ public final class OutputAnalyzer {
return this;
}
/**
* Verify the exit value of the process
*
* @param notExpectedExitValue Unexpected exit value from process
* @throws RuntimeException If the exit value from the process did match the expected value
*/
public OutputAnalyzer shouldNotHaveExitValue(int notExpectedExitValue) {
if (getExitValue() == notExpectedExitValue) {
reportDiagnosticSummary();
throw new RuntimeException("Unexpected to get exit value of ["
+ notExpectedExitValue + "]\n");
}
return this;
}
/**
* Report summary that will help to diagnose the problem Currently includes:
* - standard input produced by the process under test - standard output -
......
......@@ -38,6 +38,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.concurrent.TimeUnit;
......@@ -80,6 +81,9 @@ public final class Utils {
*/
public static final long DEFAULT_TEST_TIMEOUT = TimeUnit.SECONDS.toMillis(120);
private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
private static final int DEFAULT_BUFFER_SIZE = 8192;
private Utils() {
// Private constructor to prevent class instantiation
}
......@@ -291,6 +295,40 @@ public final class Utils {
return output;
}
/**
* Helper method to read all bytes from InputStream
*
* @param is InputStream to read from
* @return array of bytes
* @throws IOException
*/
public static byte[] readAllBytes(InputStream is) throws IOException {
byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
int capacity = buf.length;
int nread = 0;
int n;
for (;;) {
// read to EOF which may read more or less than initial buffer size
while ((n = is.read(buf, nread, capacity - nread)) > 0)
nread += n;
// if the last call to read returned -1, then we're done
if (n < 0)
break;
// need to allocate a larger buffer
if (capacity <= MAX_BUFFER_SIZE - capacity) {
capacity = capacity << 1;
} else {
if (capacity == MAX_BUFFER_SIZE)
throw new OutOfMemoryError("Required array size too large");
capacity = MAX_BUFFER_SIZE;
}
buf = Arrays.copyOf(buf, capacity);
}
return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
}
/**
* Adjusts the provided timeout value for the TIMEOUT_FACTOR
* @param tOut the timeout value to be adjusted
......@@ -362,7 +400,7 @@ public final class Utils {
* @throws NullPointerException if {@code in} or {@code out} is {@code null}
*
*/
public static long transferBetweenStreams(InputStream in, OutputStream out)
public static long transferTo(InputStream in, OutputStream out)
throws IOException {
long transferred = 0;
byte[] buffer = new byte[BUFFER_SIZE];
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册