8378111: Migrate java/util/jar tests to JUnit

Reviewed-by: lancea
This commit is contained in:
Justin Lu 2026-03-02 17:22:48 +00:00
parent f2a52b7a06
commit 0baeccddff
34 changed files with 990 additions and 811 deletions

View File

@ -1,5 +1,6 @@
/*
* Copyright 2014 Google, Inc. All Rights Reserved.
* Copyright (c) 2026, 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
@ -24,15 +25,26 @@
/* @test
* @bug 8062194
* @summary Ensure Attribute iteration order is the insertion order.
* @run junit IterationOrder
*/
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.Arrays;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.fail;
public class IterationOrder {
static void checkOrder(Attributes.Name k0, String v0,
@ParameterizedTest
@MethodSource
void checkOrderTest(Attributes.Name k0, String v0,
Attributes.Name k1, String v1,
Attributes.Name k2, String v2) {
Attributes x = new Attributes();
@ -48,7 +60,7 @@ public class IterationOrder {
&& entries[1].getValue() == v1
&& entries[2].getKey() == k2
&& entries[2].getValue() == v2)) {
throw new AssertionError(Arrays.toString(entries));
fail(Arrays.toString(entries));
}
Object[] keys = x.keySet().toArray();
@ -56,19 +68,21 @@ public class IterationOrder {
&& keys[0] == k0
&& keys[1] == k1
&& keys[2] == k2)) {
throw new AssertionError(Arrays.toString(keys));
fail(Arrays.toString(keys));
}
}
public static void main(String[] args) throws Exception {
static Stream<Arguments> checkOrderTest() {
Attributes.Name k0 = Name.MANIFEST_VERSION;
Attributes.Name k1 = Name.MAIN_CLASS;
Attributes.Name k2 = Name.SEALED;
String v0 = "42.0";
String v1 = "com.google.Hello";
String v2 = "yes";
checkOrder(k0, v0, k1, v1, k2, v2);
checkOrder(k1, v1, k0, v0, k2, v2);
checkOrder(k2, v2, k1, v1, k0, v0);
return Stream.of(
Arguments.of(k0, v0, k1, v1, k2, v2),
Arguments.of(k1, v1, k0, v0, k2, v2),
Arguments.of(k2, v2, k1, v1, k0, v0)
);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2026, 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
@ -25,17 +25,20 @@
@bug 4199981
@summary Make sure empty string is not a valid
Attributes name.
*/
@run junit Name
*/
import org.junit.jupiter.api.Test;
import java.util.jar.Attributes;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class Name {
public static void main(String[] args) throws Exception {
try {
Attributes.Name name = new Attributes.Name("");
throw new Exception("empty string should be rejected");
} catch (IllegalArgumentException e) {
}
@Test
void emptyStringTest() {
assertThrows(IllegalArgumentException.class, () -> new Attributes.Name(""),
"empty string should be rejected");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2026, 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
@ -21,8 +21,6 @@
* questions.
*/
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@ -31,14 +29,16 @@ import java.util.jar.Manifest;
import java.util.jar.Attributes.Name;
import java.lang.reflect.Field;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
import org.junit.jupiter.api.Test;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.*;
/**
* @test
* @bug 8066619
* @modules java.base/java.util.jar:+open
* @run testng/othervm --enable-final-field-mutation=ALL-UNNAMED NullAndEmptyKeysAndValues
* @run junit/othervm --enable-final-field-mutation=ALL-UNNAMED NullAndEmptyKeysAndValues
* @summary Tests manifests with {@code null} and empty string {@code ""}
* values as section name, header name, or value in both main and named
* attributes sections.
@ -108,7 +108,7 @@ public class NullAndEmptyKeysAndValues {
attr.set(mf, mainAtts);
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
mf = writeAndRead(mf);
assertEquals(mf.getMainAttributes().getValue(SOME_KEY), NULL_TEXT);
assertEquals(NULL_TEXT, mf.getMainAttributes().getValue(SOME_KEY));
}
@Test
@ -122,7 +122,7 @@ public class NullAndEmptyKeysAndValues {
attr.set(mf, mainAtts);
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
mf = writeAndRead(mf);
assertEquals(mf.getMainAttributes().getValue(SOME_KEY), EMPTY_STR);
assertEquals(EMPTY_STR, mf.getMainAttributes().getValue(SOME_KEY));
}
@Test
@ -171,8 +171,7 @@ public class NullAndEmptyKeysAndValues {
map.put(new Name(SOME_KEY), null);
}});
mf = writeAndRead(mf);
assertEquals(mf.getEntries().get(SOME_KEY).getValue(SOME_KEY),
NULL_TEXT);
assertEquals(NULL_TEXT, mf.getEntries().get(SOME_KEY).getValue(SOME_KEY));
}
@Test
@ -183,8 +182,7 @@ public class NullAndEmptyKeysAndValues {
map.put(new Name(SOME_KEY), EMPTY_STR);
}});
mf = writeAndRead(mf);
assertEquals(mf.getEntries().get(SOME_KEY).getValue(SOME_KEY),
EMPTY_STR);
assertEquals(EMPTY_STR, mf.getEntries().get(SOME_KEY).getValue(SOME_KEY));
}
static Manifest writeAndRead(Manifest mf) throws IOException {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 1999, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2026, 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
@ -25,31 +25,27 @@
@bug 4165833 4167600
@summary Test if put and putAll will test for illegal
arguments.
*/
@run junit PutAndPutAll
*/
import org.junit.jupiter.api.Test;
import java.util.jar.Attributes;
import java.util.HashMap;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class PutAndPutAll {
public static void main(String[] args) throws Exception {
@Test
void classCastTest() {
Attributes at = new Attributes();
try{
at.put("this is not an Attributes.Name", "value");
throw new Exception("put should check for non Attributes.Name names");
} catch (ClassCastException e) {
}
try{
at.put(new Attributes.Name("name"), new Integer(0));
throw new Exception("put should check for non String values");
} catch (ClassCastException e) {
}
try {
at.putAll(new HashMap());
throw new Exception("putAll should check for non Attributes maps");
} catch (ClassCastException e) {
}
assertThrows(ClassCastException.class,
() -> at.put("this is not an Attributes.Name", "value"), "put should check for non Attributes.Name names");
assertThrows(ClassCastException.class,
() -> at.put(new Attributes.Name("name"), new Integer(0)), "put should check for non String values");
assertThrows(ClassCastException.class,
() -> at.putAll(new HashMap()), "putAll should check for non Attributes maps");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2026, 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
@ -24,19 +24,28 @@
/* @test
* @bug 8200530
* @summary Test Attributes newline
* @run junit TestAttrsNL
*/
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.util.jar.Manifest;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.io.ByteArrayInputStream;
import java.util.Map;
import java.util.stream.Stream;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestAttrsNL {
public static void main(String[] args) throws Throwable {
static Stream<Arguments> newLineAttributesTest() throws IOException {
String manifestStr =
"Manifest-Version: 1.0\r\n" +
@ -68,16 +77,16 @@ public class TestAttrsNL {
new Name("key44"), "value44"
);
test(new Manifest(new ByteArrayInputStream(manifestStr.getBytes(UTF_8))),
mainAttrsExped, attrsExped);
var normal = Arguments.of(new Manifest(new ByteArrayInputStream(manifestStr.getBytes(UTF_8))),
mainAttrsExped, attrsExped);
test(new Manifest(new ByteArrayInputStream(
manifestStr.replaceAll("\r\n", "\r").getBytes(UTF_8))),
mainAttrsExped, attrsExped);
var carriage = Arguments.of(new Manifest(new ByteArrayInputStream(
manifestStr.replaceAll("\r\n", "\r").getBytes(UTF_8))),
mainAttrsExped, attrsExped);
test(new Manifest(new ByteArrayInputStream(
manifestStr.replaceAll("\r\n", "\n").getBytes(UTF_8))),
mainAttrsExped, attrsExped);
var newLine = Arguments.of(new Manifest(new ByteArrayInputStream(
manifestStr.replaceAll("\r\n", "\n").getBytes(UTF_8))),
mainAttrsExped, attrsExped);
// mixed
manifestStr =
@ -93,31 +102,33 @@ public class TestAttrsNL {
"key22: value22\n END\r\n" +
"key33: value33\r \n" +
"key44: value44\n";
test(new Manifest(new ByteArrayInputStream(manifestStr.getBytes(UTF_8))),
var mixed = Arguments.of(new Manifest(new ByteArrayInputStream(manifestStr.getBytes(UTF_8))),
mainAttrsExped, attrsExped);
return Stream.of(normal, carriage, newLine, mixed);
}
private static void test(Manifest m,
@ParameterizedTest
@MethodSource
void newLineAttributesTest(Manifest m,
Map<Name, String> mainAttrsExped,
Map<Name, String> attrsExped) {
Attributes mainAttrs = m.getMainAttributes();
mainAttrsExped.forEach( (k, v) -> {
if (!mainAttrs.containsKey(k) || !mainAttrs.get(k).equals(v)) {
System.out.printf(" containsKey(%s) : %b%n", k, mainAttrs.containsKey(k));
System.out.printf(" get(%s) : %s%n", k, mainAttrs.get(k));
throw new RuntimeException("expected attr: k=<" + k + ">, v=<" + v + ">");
}
var expectedMsg = "expected attr: k=<" + k + ">, v=<" + v + ">";
assertTrue(mainAttrs.containsKey(k),
" containsKey(%s) : %b%n%s".formatted(k, mainAttrs.containsKey(k), expectedMsg));
assertEquals(v, mainAttrs.get(k),
" get(%s) : %s%n%s".formatted(k, mainAttrs.get(k), expectedMsg));
});
Attributes attrs = m.getAttributes("Hello");
attrs.forEach( (k, v) -> {
if (!attrs.containsKey(k) || !attrs.get(k).equals(v)) {
System.out.printf(" containsKey(%s) : %b%n", k, attrs.containsKey(k));
System.out.printf(" get(%s) : %s%n", k, attrs.get(k));
throw new RuntimeException("expected attr: k=<" + k + ">, v=<" + v + ">");
}
var expectedMsg = "expected attr: k=<" + k + ">, v=<" + v + ">";
assertTrue(attrs.containsKey(k),
" containsKey(%s) : %b%n%s".formatted(k, attrs.containsKey(k), expectedMsg));
assertEquals(v, attrs.get(k),
" get(%s) : %s%n%s".formatted(k, attrs.get(k), expectedMsg));
});
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2026, 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
@ -26,20 +26,28 @@
* @bug 6337925
* @summary Ensure that callers cannot modify the internal JarEntry cert and
* codesigner arrays.
* @author Sean Mullan
* @run junit GetMethodsReturnClones
*/
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.security.CodeSigner;
import java.security.cert.Certificate;
import java.util.*;
import java.util.jar.*;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class GetMethodsReturnClones {
private static final String BASE = System.getProperty("test.src", ".") +
System.getProperty("file.separator");
private static List<JarEntry> jarEntries;
public static void main(String[] args) throws Exception {
@BeforeAll()
static void setupEntries() throws IOException {
List<JarEntry> entries = new ArrayList<>();
try (JarFile jf = new JarFile(BASE + "test.jar", true)) {
byte[] buffer = new byte[8192];
@ -55,23 +63,29 @@ public class GetMethodsReturnClones {
}
}
}
jarEntries = entries;
}
for (JarEntry je : entries) {
@Test
void certsTest() {
for (JarEntry je : jarEntries) {
Certificate[] certs = je.getCertificates();
CodeSigner[] signers = je.getCodeSigners();
if (certs != null) {
certs[0] = null;
certs = je.getCertificates();
if (certs[0] == null) {
throw new Exception("Modified internal certs array");
}
assertNotNull(certs[0], "Modified internal certs array");
}
}
}
@Test
void signersTest() {
for (JarEntry je : jarEntries) {
CodeSigner[] signers = je.getCodeSigners();
if (signers != null) {
signers[0] = null;
signers = je.getCodeSigners();
if (signers[0] == null) {
throw new Exception("Modified internal codesigners array");
}
assertNotNull(signers[0], "Modified internal codesigners array");
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -21,55 +21,45 @@
* questions.
*/
/**
/*
* @test
* @bug 4842702 8211765
* @summary Check that constructors throw specified exceptions
* @author Martin Buchholz
* @run junit Constructor
*/
import org.junit.jupiter.api.Test;
import java.util.jar.JarFile;
import java.io.File;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class Constructor {
private static void Unreached (Object o)
throws Exception
{
// Should never get here
throw new Exception ("Expected exception was not thrown");
}
public static void main(String[] args)
throws Exception
{
try { Unreached (new JarFile ((File) null, true, JarFile.OPEN_READ)); }
catch (NullPointerException e) {}
@Test
void constructorTest() {
try { Unreached (new JarFile ((File) null, true)); }
catch (NullPointerException e) {}
assertThrows(NullPointerException.class, () -> new JarFile ((File) null, true, JarFile.OPEN_READ));
try { Unreached (new JarFile ((File) null)); }
catch (NullPointerException e) {}
assertThrows(NullPointerException.class, () -> new JarFile ((File) null, true));
try { Unreached (new JarFile ((String) null, true)); }
catch (NullPointerException e) {}
assertThrows(NullPointerException.class, () -> new JarFile ((File) null));
try { Unreached (new JarFile ((String) null)); }
catch (NullPointerException e) {}
assertThrows(NullPointerException.class, () -> new JarFile ((String) null, true));
try { Unreached (new JarFile ("NoSuchJar.jar")); }
catch (IOException e) {}
assertThrows(NullPointerException.class, () -> new JarFile ((String) null));
try { Unreached (new JarFile (new File ("NoSuchJar.jar"))); }
catch (IOException e) {}
assertThrows(IOException.class, () -> new JarFile ("NoSuchJar.jar"));
assertThrows(IOException.class, () -> new JarFile (new File ("NoSuchJar.jar")));
// Test that an IOExcception is thrown when an invalid charater
// is part of the path on Windows and Unix
final String invalidOSPath = System.getProperty("os.name")
.startsWith("Windows") ? "C:\\*" : "foo\u0000bar";
try { Unreached (new JarFile (invalidOSPath)); }
catch (IOException e) {}
assertThrows(IOException.class, () -> new JarFile (invalidOSPath));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2026, 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
@ -21,7 +21,7 @@
* questions.
*/
/**
/*
* @test
* @bug 8300140
* @summary Make sure signature related files in subdirectories of META-INF are not considered for verification
@ -29,12 +29,14 @@
* @modules java.base/sun.security.util
* @modules java.base/sun.security.tools.keytool
* @modules jdk.jartool/sun.security.tools.jarsigner
* @run main/othervm IgnoreUnrelatedSignatureFiles
* @run junit/othervm IgnoreUnrelatedSignatureFiles
*/
import jdk.internal.access.JavaUtilZipFileAccess;
import jdk.internal.access.SharedSecrets;
import jdk.security.jarsigner.JarSigner;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import sun.security.tools.jarsigner.Main;
import sun.security.util.SignatureFileVerifier;
@ -62,6 +64,10 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class IgnoreUnrelatedSignatureFiles {
private static final JavaUtilZipFileAccess JUZA = SharedSecrets.getJavaUtilZipFileAccess();
@ -69,56 +75,76 @@ public class IgnoreUnrelatedSignatureFiles {
// This path resides in a subdirectory of META-INF, so it should not be considered signature related
public static final String SUBDIR_SF_PATH = "META-INF/subdirectory/META-INF/SIGNER.SF";
// Jars used for testing. See `setupJars` below for setup
static Path j;
static Path s;
static Path m;
static Path sm;
static Path ca;
static Path cas;
public static void main(String[] args) throws Exception {
@BeforeAll
static void setupJars() throws Exception {
// Regular signed JAR
Path j = createJarFile();
Path s = signJarFile(j, "SIGNER1", "signed");
j = createJarFile();
s = signJarFile(j, "SIGNER1", "signed");
// Singed JAR with unrelated signature files
Path m = moveSignatureRelated(s);
Path sm = signJarFile(m, "SIGNER2", "modified-signed");
m = moveSignatureRelated(s);
sm = signJarFile(m, "SIGNER2", "modified-signed");
// Signed JAR with custom SIG-* files
Path ca = createCustomAlgJar();
Path cas = signJarFile(ca, "SIGNER1", "custom-signed");
ca = createCustomAlgJar();
cas = signJarFile(ca, "SIGNER1", "custom-signed");
}
// 0: Sanity check that the basic signed JAR verifies
// Sanity check that the basic signed JAR verifies
@Test
void signedJarVerifyTest() throws IOException {
try (JarFile jf = new JarFile(s.toFile(), true)) {
Map<String, Attributes> entries = jf.getManifest().getEntries();
if (entries.size() != 1) {
throw new Exception("Expected a single manifest entry for the digest of a.txt, instead found entries: " + entries.keySet());
}
assertEquals(1, entries.size(),
"Expected a single manifest entry for the digest of a.txt, instead found entries: " + entries.keySet());
JarEntry entry = jf.getJarEntry("a.txt");
try (InputStream in = jf.getInputStream(entry)) {
in.transferTo(OutputStream.nullOutputStream());
}
}
// 1: Check ZipFile.Source.isSignatureRelated
}
// Check ZipFile.Source.isSignatureRelated
@Test
void zipFileSourceIsSignatureRelatedTest() throws IOException {
try (JarFile jarFile = new JarFile(m.toFile())) {
List<String> manifestAndSignatureRelatedFiles = JUZA.getManifestAndSignatureRelatedFiles(jarFile);
for (String signatureRelatedFile : manifestAndSignatureRelatedFiles) {
String dir = signatureRelatedFile.substring(0, signatureRelatedFile.lastIndexOf("/"));
if (!"META-INF".equals(dir)) {
throw new Exception("Signature related file does not reside directly in META-INF/ : " + signatureRelatedFile);
}
assertEquals("META-INF", dir,
"Signature related file does not reside directly in META-INF/ : " + signatureRelatedFile);
}
}
}
// 2: Check SignatureFileVerifier.isSigningRelated
if (SignatureFileVerifier.isSigningRelated(SUBDIR_SF_PATH)) {
throw new Exception("Signature related file does not reside directly in META-INF/ : " + SUBDIR_SF_PATH);
}
// Check SignatureFileVerifier.isSigningRelated
@Test
void sigFileVerifierIsSigningRelatedTest() {
assertFalse(SignatureFileVerifier.isSigningRelated(SUBDIR_SF_PATH),
"Signature related file does not reside directly in META-INF/ : " + SUBDIR_SF_PATH);
}
// 3: Check JarInputStream with doVerify = true
// Check JarInputStream with doVerify = true
@Test
void jarIStreamDoVerifyTest() throws IOException {
try (JarInputStream in = new JarInputStream(Files.newInputStream(m), true)) {
while (in.getNextEntry() != null) {
while (in.getNextEntry() != null) {
in.transferTo(OutputStream.nullOutputStream());
}
}
}
// 4: Check that a JAR containing unrelated .SF, .RSA files is signed as-if it is unsigned
// Check that a JAR containing unrelated .SF, .RSA files is signed as-if it is unsigned
@Test
void unrelatedFilesUnsignedTest() throws IOException {
try (ZipFile zf = new ZipFile(sm.toFile())) {
ZipEntry mf = zf.getEntry("META-INF/MANIFEST.MF");
try (InputStream stream = zf.getInputStream(mf)) {
@ -126,19 +152,24 @@ public class IgnoreUnrelatedSignatureFiles {
// When JarSigner considers a jar to not be already signed,
// the 'Manifest-Version' attributed name will be case-normalized
// Assert that manifest-version is not in lowercase
if (manifest.startsWith("manifest-version")) {
throw new Exception("JarSigner unexpectedly treated unsigned jar as signed");
}
assertFalse(manifest.startsWith("manifest-version"),
"JarSigner unexpectedly treated unsigned jar as signed");
}
}
}
// 5: Check that a JAR containing non signature related .SF, .RSA files can be signed
// Check that a JAR containing non signature related .SF, .RSA files can be signed
@Test
void nonSigFileIsSignableTest() throws Exception {
try (JarFile jf = new JarFile(sm.toFile(), true)) {
checkSignedBy(jf, "a.txt", "CN=SIGNER2");
checkSignedBy(jf, "META-INF/subdirectory/META-INF/SIGNER1.SF", "CN=SIGNER2");
}
}
// 6: Check that JarSigner does not move unrelated [SF,RSA] files to the beginning of signed JARs
// Check that JarSigner does not move unrelated [SF,RSA] files to the beginning of signed JARs
@Test
void jarSignerDoesNotMoveUnrelatedTest() throws IOException {
try (JarFile zf = new JarFile(sm.toFile())) {
List<String> actualOrder = zf.stream().map(ZipEntry::getName).toList();
@ -154,23 +185,25 @@ public class IgnoreUnrelatedSignatureFiles {
"META-INF/subdirectory2/META-INF/SIGNER1.RSA"
);
if (!expectedOrder.equals(actualOrder)) {
String msg = ("""
assertEquals(expectedOrder, actualOrder, ("""
Unexpected file order in JAR with unrelated SF,RSA files
Expected order: %s
Actual order: %s""")
.formatted(expectedOrder, actualOrder);
throw new Exception(msg);
}
.formatted(expectedOrder, actualOrder));
}
}
// 7: Check that jarsigner ignores unrelated signature files
// Check that jarsigner ignores unrelated signature files
@Test
void jarSignerIgnoresUnrelatedTest() throws Exception {
String message = jarSignerVerify(m);
if (message.contains("WARNING")) {
throw new Exception("jarsigner output contains unexpected warning: " +message);
}
assertFalse(message.contains("WARNING"),
"jarsigner output contains unexpected warning: " + message);
}
// 8: Check that SignatureFileVerifier.isSigningRelated handles custom SIG-* files correctly
// Check that SignatureFileVerifier.isSigningRelated handles custom SIG-* files correctly
@Test
void customSIGFilesTest() throws IOException {
try (JarFile jf = new JarFile(cas.toFile(), true)) {
// These files are not signature-related and should be signed
@ -185,10 +218,9 @@ public class IgnoreUnrelatedSignatureFiles {
Set<String> actualSigned = jf.getManifest().getEntries().keySet();
if (!expectedSigned.equals(actualSigned)) {
throw new Exception("Unexpected MANIFEST entries. Expected %s, got %s"
.formatted(expectedSigned, actualSigned));
}
assertEquals(expectedSigned, actualSigned,
"Unexpected MANIFEST entries. Expected %s, got %s"
.formatted(expectedSigned, actualSigned));
}
}
@ -220,22 +252,17 @@ public class IgnoreUnrelatedSignatureFiles {
// Verify that the entry is signed
CodeSigner[] signers = je.getCodeSigners();
if (signers == null) {
throw new Exception(String.format("Expected %s to be signed", name));
}
assertNotNull(signers, "Expected %s to be signed".formatted(name));
// There should be a single signer
if (signers.length != 1) {
throw new Exception(String.format("Expected %s to be signed by exactly one signer", name));
}
assertEquals(1, signers.length,
"Expected %s to be signed by exactly one signer".formatted(name));
String actualSigner = ((X509Certificate) signers[0]
.getSignerCertPath().getCertificates().get(0))
.getIssuerX500Principal().getName();
if (!actualSigner.equals(expectedSigner)) {
throw new Exception(String.format("Expected %s to be signed by %s, was signed by %s", name, expectedSigner, actualSigner));
}
assertEquals(expectedSigner, actualSigner,
"Expected %s to be signed by %s, was signed by %s".formatted(name, expectedSigner, actualSigner));
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2026, 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
@ -27,7 +27,7 @@
* @summary Make sure scanning manifest doesn't throw AIOOBE on certain strings containing backticks.
* @library /test/lib/
* @build jdk.test.lib.util.JarBuilder
* @run testng JarBacktickManifest
* @run junit JarBacktickManifest
*/
import java.io.File;
@ -35,19 +35,20 @@ import java.io.IOException;
import java.nio.file.Files;
import java.util.jar.JarFile;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import jdk.test.lib.util.JarBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
public class JarBacktickManifest {
public static final String VERIFY_MANIFEST_JAR = "verifyManifest.jar";
@BeforeClass
public void initialize() throws Exception {
@BeforeAll
public static void initialize() throws Exception {
JarBuilder jb = new JarBuilder(VERIFY_MANIFEST_JAR);
jb.addAttribute("Test", " Class-`Path` ");
jb.addAttribute("Test2", " Multi-`Release ");
@ -55,14 +56,14 @@ public class JarBacktickManifest {
}
@Test
public void test() throws Exception {
public void backtickTest() throws Exception {
try (JarFile jf = new JarFile(VERIFY_MANIFEST_JAR)) { // do not set runtime versioning
Assert.assertFalse(jf.isMultiRelease(), "Shouldn't be multi-release");
assertFalse(jf.isMultiRelease(), "Shouldn't be multi-release");
}
}
@AfterClass
public void close() throws IOException {
@AfterAll
public static void close() throws IOException {
Files.delete(new File(VERIFY_MANIFEST_JAR).toPath());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2026, 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
@ -24,18 +24,25 @@
/* @test
@bug 4771616
@summary JarFile.maybeInstantiateVerifier must check for absence of manifest
@run junit JarNoManifest
*/
import org.junit.jupiter.api.Test;
import java.io.*;
import java.util.jar.*;
import java.util.zip.*;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class JarNoManifest {
public static void main(String[] args) throws Exception {
File f = new File(System.getProperty("test.src","."), "no-manifest.jar");
JarFile jar = new JarFile(f);
ZipEntry entry = jar.getEntry("JarNoManifest.java");
// The following throws a NullPointerException when the bug is present
InputStream in = jar.getInputStream(entry);
}
@Test
void absentManifestTest() throws IOException {
File f = new File(System.getProperty("test.src", "."), "no-manifest.jar");
JarFile jar = new JarFile(f);
ZipEntry entry = jar.getEntry("JarNoManifest.java");
// The following throws a NullPointerException when the bug is present
assertDoesNotThrow(() -> jar.getInputStream(entry));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2026, 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
@ -24,22 +24,27 @@
/* @test
* @bug 7023056
* @summary NPE from sun.security.util.ManifestEntryVerifier.verify during Maven build
* @run junit MevNPE
*/
import org.junit.jupiter.api.Test;
import java.io.*;
import java.util.jar.*;
public class MevNPE {
public static void main(String[] args) throws Exception {
@Test
void noNpeTest() throws IOException {
File f = new File(System.getProperty("test.src", "."), "Signed.jar");
try (JarFile jf = new JarFile(f, true)) {
try (InputStream s1 = jf.getInputStream(
jf.getJarEntry(JarFile.MANIFEST_NAME))) {
s1.read(new byte[10000]);
};
}
try (InputStream s2 = jf.getInputStream(
jf.getJarEntry(JarFile.MANIFEST_NAME))) {
s2.read(new byte[10000]);
};
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -21,29 +21,35 @@
* questions.
*/
/**
/*
* @test
* @bug 4953126
* @summary Check that a signed JAR file containing an unsupported signer info
* attribute can be parsed successfully.
* @run junit ScanSignedJar
*/
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.cert.Certificate;
import java.util.Enumeration;
import java.util.jar.*;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ScanSignedJar {
public static void main(String[] args) throws Exception {
@Test
void unsupportedSignerTest() throws IOException {
boolean isSigned = false;
try (JarFile file = new JarFile(new File(System.getProperty("test.src","."),
"bogus-signerinfo-attr.jar"))) {
"bogus-signerinfo-attr.jar"))) {
byte[] buffer = new byte[8192];
for (Enumeration entries = file.entries(); entries.hasMoreElements();) {
JarEntry entry = (JarEntry) entries.nextElement();
for (Enumeration<JarEntry> entries = file.entries(); entries.hasMoreElements();) {
JarEntry entry = entries.nextElement();
try (InputStream jis = file.getInputStream(entry)) {
while (jis.read(buffer, 0, buffer.length) != -1) {
// read the jar entry
@ -53,14 +59,9 @@ public class ScanSignedJar {
isSigned = true;
}
System.out.println((isSigned ? "[signed] " : "\t ") +
entry.getName());
entry.getName());
}
}
if (isSigned) {
System.out.println("\nJAR file has signed entries");
} else {
throw new Exception("Failed to detect that the JAR file is signed");
}
assertTrue(isSigned, "Failed to detect that the JAR file is signed");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2004, 2026, 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
@ -24,45 +24,38 @@
/* @test
* @bug 4845692 8206863
* @summary JarFile.getInputStream should not throw when jar file is signed
* @author Martin Buchholz
* @run junit SignedJarFileGetInputStream
*/
import org.junit.jupiter.api.Test;
import java.io.*;
import java.util.*;
import java.util.jar.*;
import java.util.zip.*;
public class SignedJarFileGetInputStream {
public static void main(String args[]) throws Throwable {
JarFile jar = new JarFile(
new File(System.getProperty("test.src", "."), "Signed.jar"));
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class SignedJarFileGetInputStream {
@Test
void signedJarTest() throws IOException {
JarFile jar = new JarFile(
new File(System.getProperty("test.src", "."), "Signed.jar"));
for (Enumeration e = jar.entries(); e.hasMoreElements();) {
JarEntry entry = (JarEntry) e.nextElement();
InputStream is = jar.getInputStream(new ZipEntry(entry.getName()));
InputStream is = assertDoesNotThrow(() -> jar.getInputStream(new ZipEntry(entry.getName())));
is.close();
}
// read(), available() on closed stream should throw IOException
InputStream is = jar.getInputStream(new ZipEntry("Test.class"));
is.close();
byte[] buffer = new byte[1];
try {
is.read();
throw new AssertionError("Should have thrown IOException");
} catch (IOException success) {}
try {
is.read(buffer);
throw new AssertionError("Should have thrown IOException");
} catch (IOException success) {}
try {
is.read(buffer, 0, buffer.length);
throw new AssertionError("Should have thrown IOException");
} catch (IOException success) {}
try {
is.available();
throw new AssertionError("Should have thrown IOException");
} catch (IOException success) {}
assertThrows(IOException.class, () -> is.read());
assertThrows(IOException.class, () -> is.read(buffer));
assertThrows(IOException.class, () -> is.read(buffer, 0, buffer.length));
assertThrows(IOException.class, () -> is.available());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2026, 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
@ -21,15 +21,21 @@
* questions.
*/
/**
/*
* @test
* @modules java.base/sun.security.tools.keytool
* @summary JARs with pending block files (where .RSA comes before .SF) should verify correctly
* @run junit SignedJarPendingBlock
*/
import jdk.security.jarsigner.JarSigner;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.FieldSource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
@ -42,32 +48,47 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class SignedJarPendingBlock {
public static void main(String[] args) throws Exception {
static Path signed;
static Path pendingBlocks;
static Path invalid;
// Construct the test data
@BeforeAll
static void setup() throws Exception {
Path jar = createJarFile();
Path signed = signJarFile(jar);
Path pendingBlocks = moveBlockFirst(signed);
Path invalid = invalidate(pendingBlocks);
// 1: Regular signed JAR with no pending blocks should verify
checkSigned(signed);
// 2: Signed jar with pending blocks should verify
checkSigned(pendingBlocks);
// 3: Invalid signed jar with pending blocks should throw SecurityException
try {
checkSigned(invalid);
throw new Exception("Expected invalid digest to be detected");
} catch (SecurityException se) {
// Ignore
}
signed = signJarFile(jar);
pendingBlocks = moveBlockFirst(signed);
invalid = invalidate(pendingBlocks);
}
private static void checkSigned(Path b) throws Exception {
try (JarFile jf = new JarFile(b.toFile(), true)) {
// Regular signed JAR with no pending blocks should verify
@Test
void checkValidSignedJar() {
assertDoesNotThrow(() -> checkSigned(signed),
"Valid digest should not fail");
}
// Signed jar with pending blocks should verify
@Test
void checkValidSignedPendingJar() {
assertDoesNotThrow(() -> checkSigned(pendingBlocks),
"Valid digest should not fail");
}
// Invalid signed jar with pending blocks should throw SecurityException
@Test
void checkInvalidSignedJar() {
assertThrows(SecurityException.class, () -> checkSigned(invalid),
"Expected invalid digest to be detected");
}
private static void checkSigned(Path b) throws IOException {
try (JarFile jf = new JarFile(b.toFile(), true)) {
JarEntry je = jf.getJarEntry("a.txt");
try (InputStream in = jf.getInputStream(je)) {
in.transferTo(OutputStream.nullOutputStream());

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -24,43 +24,49 @@
/* @test
* @bug 4910572
* @summary Accessing a closed jar file should generate IllegalStateException.
* @author Martin Buchholz
* @run junit SorryClosed
*/
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.File;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class SorryClosed {
public static void main(String args[]) throws IOException {
File file = new File(System.getProperty("test.src","."), "test.jar");
String testEntryName = "test.class";
private static final File file = new File(System.getProperty("test.src", "."), "test.jar");
private static final String testEntryName = "test.class";
try {
JarFile f = new JarFile(file);
ZipEntry e = f.getEntry(testEntryName);
f.close();
f.getInputStream(e);
} catch (IllegalStateException e) {} // OK
@Test
void getInputStreamTest() throws IOException {
JarFile f = new JarFile(file);
ZipEntry e = f.getEntry(testEntryName);
f.close();
assertThrows(IllegalStateException.class, () -> f.getInputStream(e));
}
try {
JarFile f = new JarFile(file);
f.close();
f.getEntry(testEntryName);
} catch (IllegalStateException e) {} // OK
@Test
void getEntryTest() throws IOException {
JarFile f = new JarFile(file);
f.close();
assertThrows(IllegalStateException.class, () -> f.getEntry(testEntryName));
}
try {
JarFile f = new JarFile(file);
f.close();
f.getJarEntry(testEntryName);
} catch (IllegalStateException e) {} // OK
@Test
void getJarEntryTest() throws IOException {
JarFile f = new JarFile(file);
f.close();
assertThrows(IllegalStateException.class, () -> f.getJarEntry(testEntryName));
}
try {
JarFile f = new JarFile(file);
f.close();
f.getManifest();
} catch (IllegalStateException e) {} // OK
@Test
void getManifestTest() throws IOException {
JarFile f = new JarFile(file);
f.close();
assertThrows(IllegalStateException.class, f::getManifest);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2026, 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
@ -21,38 +21,35 @@
* questions.
*/
/**
/*
* @test
* @bug 4624534
* @summary Make sure jar certificates work for Turkish locale
* @author kladko
* @run junit/othervm -Duser.language=tr -Duser.country=TR TurkCert
*/
import org.junit.jupiter.api.Test;
import java.util.*;
import java.util.jar.*;
import java.security.cert.*;
import java.io.*;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class TurkCert {
public static void main(String[] args) throws Exception{
Locale reservedLocale = Locale.getDefault();
try {
Locale.setDefault(Locale.of("tr", "TR"));
File f = new File(System.getProperty("test.src","."), "test.jar");
try (JarFile jf = new JarFile(f, true)) {
JarEntry je = (JarEntry)jf.getEntry("test.class");
try (InputStream is = jf.getInputStream(je)) {
byte[] b = new byte[1024];
while (is.read(b) != -1) {
}
}
if (je.getCertificates() == null) {
throw new Exception("Null certificate for test.class.");
@Test
void turkishLocaleTest() throws IOException {
File f = new File(System.getProperty("test.src", "."), "test.jar");
try (JarFile jf = new JarFile(f, true)) {
JarEntry je = (JarEntry)jf.getEntry("test.class");
try (InputStream is = jf.getInputStream(je)) {
byte[] b = new byte[1024];
while (is.read(b) != -1) {
}
}
} finally {
// restore the default locale
Locale.setDefault(reservedLocale);
assertNotNull(je.getCertificates(), "Null certificate for test.class.");
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2026, 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
@ -21,18 +21,20 @@
* questions.
*/
/**
/*
* @test
* @library /test/lib
* @modules java.base/sun.security.x509
* @modules java.base/sun.security.tools.keytool
* @bug 4419266 4842702
* @summary Make sure verifying signed Jar doesn't throw SecurityException
* @run junit VerifySignedJar
*/
import jdk.security.jarsigner.JarSigner;
import org.junit.jupiter.api.Test;
import sun.security.tools.keytool.CertAndKeyGen;
import sun.security.x509.X500Name;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
@ -40,21 +42,24 @@ import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static jdk.test.lib.Utils.runAndCheckException;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
public class VerifySignedJar {
public static void main(String[] args) throws Exception {
@Test
void signedJarSecurityExceptionTest() throws Exception {
Path j = createJar();
Path s = signJar(j, keyEntry("cn=duke"));
@ -70,38 +75,30 @@ public class VerifySignedJar {
}
// Read ZIP and JAR entries by name
Objects.requireNonNull(jf.getEntry("getprop.class"));
Objects.requireNonNull(jf.getJarEntry("getprop.class"));
assertNotNull(jf.getEntry("getprop.class"));
assertNotNull(jf.getJarEntry("getprop.class"));
// Make sure we throw NPE on null parameters
runAndCheckException(() -> jf.getEntry(null), NullPointerException.class);
runAndCheckException(() -> jf.getJarEntry(null), NullPointerException.class);
runAndCheckException(() -> jf.getInputStream(null), NullPointerException.class);
assertThrows(NullPointerException.class, () -> jf.getEntry(null));
assertThrows(NullPointerException.class, () -> jf.getJarEntry(null));
assertThrows(NullPointerException.class, () -> jf.getInputStream(null));
} catch (SecurityException se) {
throw new Exception("Got SecurityException when verifying signed " +
"jar:" + se);
fail("Got SecurityException when verifying signed jar:" + se);
}
}
// Check that a JAR entry is signed by an expected DN
private static void checkSignedBy(JarEntry e, String expectedDn) throws Exception {
private static void checkSignedBy(JarEntry e, String expectedDn) {
Certificate[] certs = e.getCertificates();
if (certs == null || certs.length == 0) {
throw new Exception("JarEntry has no certificates: " + e.getName());
}
if (certs[0] instanceof X509Certificate x) {
String name = x.getSubjectX500Principal().getName();
if (!name.equalsIgnoreCase(expectedDn)) {
throw new Exception("Expected entry signed by %s, was %s".formatted(name, expectedDn));
}
} else {
throw new Exception("Expected JarEntry.getCertificate to return X509Certificate");
}
assertNotNull(certs, "JarEntry has no certificates: " + e.getName());
assertNotEquals(0, certs.length, "JarEntry has no certificates: " + e.getName());
var x = assertInstanceOf(X509Certificate.class, certs[0], "Expected JarEntry.getCertificate to return X509Certificate");
String name = x.getSubjectX500Principal().getName();
assertTrue(name.equalsIgnoreCase(expectedDn), "Expected entry signed by %s, was %s".formatted(name, expectedDn));
}
private static Path createJar() throws Exception {
private static Path createJar() throws IOException {
Path j = Path.of("unsigned.jar");
try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(j))){
out.putNextEntry(new JarEntry("getprop.class"));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2026, 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
@ -33,8 +33,7 @@
* jdk.test.lib.JDKToolLauncher
* MultiThreadLoad FooService
* @modules java.base/jdk.internal.access:+open
* @run main MultiProviderTest
* @run main MultiProviderTest sign
* @run junit MultiProviderTest
*/
import java.io.File;
@ -51,27 +50,33 @@ import jdk.test.lib.compiler.CompilerUtils;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.util.JarUtils;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class MultiProviderTest {
private static final String METAINFO = "META-INF/services/FooService";
private static String COMBO_CP = Utils.TEST_CLASS_PATH + File.pathSeparator;
private static String TEST_CLASS_PATH = System.getProperty("test.classes", ".");
private static boolean signJars = false;
static final int NUM_JARS = 5;
// Reset per each test run under JUnit default lifecycle
private boolean signJars = false;
private String COMBO_CP = Utils.TEST_CLASS_PATH + File.pathSeparator;
private static final String KEYSTORE = "keystore.jks";
private static final String ALIAS = "JavaTest";
private static final String STOREPASS = "changeit";
private static final String KEYPASS = "changeit";
public static void main(String[] args) throws Throwable {
signJars = args.length >=1 && args[0].equals("sign");
@ParameterizedTest
@ValueSource(booleans = {true, false})
void classLoadingTest(boolean sign) throws Throwable {
signJars = sign;
initialize();
List<String> cmds = new ArrayList<>();
cmds.add(JDKToolFinder.getJDKTool("java"));
@ -86,18 +91,16 @@ public class MultiProviderTest {
"MultiThreadLoad",
TEST_CLASS_PATH));
try {
assertDoesNotThrow(() -> {
OutputAnalyzer outputAnalyzer = ProcessTools.executeCommand(cmds.stream()
.filter(t -> !t.isEmpty())
.toArray(String[]::new))
.filter(t -> !t.isEmpty())
.toArray(String[]::new))
.shouldHaveExitValue(0);
System.out.println("Output:" + outputAnalyzer.getOutput());
} catch (Throwable t) {
throw new RuntimeException("Unexpected fail.", t);
}
});
}
public static void initialize() throws Throwable {
public void initialize() throws Throwable {
if (signJars) {
genKey();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2026, 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
@ -31,7 +31,7 @@
* CreateMultiReleaseTestJars
* jdk.test.lib.compiler.Compiler
* jdk.test.lib.util.JarBuilder
* @run testng MultiReleaseJarAPI
* @run junit MultiReleaseJarAPI
*/
import java.io.File;
@ -44,37 +44,41 @@ import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.jar.JarFile;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import jdk.test.lib.RandomFactory;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;
public class MultiReleaseJarAPI {
private static final Random RANDOM = RandomFactory.getRandom();
String userdir = System.getProperty("user.dir",".");
CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
File unversioned = new File(userdir, "unversioned.jar");
File multirelease = new File(userdir, "multi-release.jar");
File signedmultirelease = new File(userdir, "signed-multi-release.jar");
private static final String userdir = System.getProperty("user.dir", ".");
private static final CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
private static final File unversioned = new File(userdir, "unversioned.jar");
private static final File multirelease = new File(userdir, "multi-release.jar");
private static final File signedmultirelease = new File(userdir, "signed-multi-release.jar");
@BeforeClass
public void initialize() throws Exception {
@BeforeAll
public static void initialize() throws Exception {
creator.compileEntries();
creator.buildUnversionedJar();
creator.buildMultiReleaseJar();
creator.buildSignedMultiReleaseJar();
}
@AfterClass
public void close() throws IOException {
@AfterAll
public static void close() throws IOException {
Files.delete(unversioned.toPath());
Files.delete(multirelease.toPath());
Files.delete(signedmultirelease.toPath());
@ -83,19 +87,19 @@ public class MultiReleaseJarAPI {
@Test
public void isMultiReleaseJar() throws Exception {
try (JarFile jf = new JarFile(unversioned)) {
Assert.assertFalse(jf.isMultiRelease());
assertFalse(jf.isMultiRelease());
}
try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, Runtime.version())) {
Assert.assertFalse(jf.isMultiRelease());
assertFalse(jf.isMultiRelease());
}
try (JarFile jf = new JarFile(multirelease)) {
Assert.assertTrue(jf.isMultiRelease());
assertTrue(jf.isMultiRelease());
}
try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Runtime.version())) {
Assert.assertTrue(jf.isMultiRelease());
assertTrue(jf.isMultiRelease());
}
testCustomMultiReleaseValue("true", true);
@ -155,45 +159,46 @@ public class MultiReleaseJarAPI {
creator.buildCustomMultiReleaseJar(fileName, value, extraAttributes);
File custom = new File(userdir, fileName);
try (JarFile jf = new JarFile(custom, true, ZipFile.OPEN_READ, Runtime.version())) {
Assert.assertEquals(jf.isMultiRelease(), expected);
assertEquals(expected, jf.isMultiRelease());
}
Files.delete(custom.toPath());
}
@DataProvider(name = "versions")
public Object[][] createVersionData() throws Exception {
return new Object[][]{
{JarFile.baseVersion(), 8},
{JarFile.runtimeVersion(), Runtime.version().major()},
{Runtime.version(), Runtime.version().major()},
{Runtime.Version.parse("7.1"), JarFile.baseVersion().major()},
{Runtime.Version.parse("9"), 9},
{Runtime.Version.parse("9.1.5-ea+200"), 9}
};
public static Stream<Arguments> createVersionData() {
return Stream.of(
Arguments.of(JarFile.baseVersion(), 8),
Arguments.of(JarFile.runtimeVersion(), Runtime.version().major()),
Arguments.of(Runtime.version(), Runtime.version().major()),
Arguments.of(Runtime.Version.parse("7.1"), JarFile.baseVersion().major()),
Arguments.of(Runtime.Version.parse("9"), 9),
Arguments.of(Runtime.Version.parse("9.1.5-ea+200"), 9)
);
}
@Test(dataProvider="versions")
@ParameterizedTest
@MethodSource("createVersionData")
public void testVersioning(Runtime.Version value, int xpected) throws Exception {
Runtime.Version expected = Runtime.Version.parse(String.valueOf(xpected));
Runtime.Version base = JarFile.baseVersion();
// multi-release jar, opened as unversioned
try (JarFile jar = new JarFile(multirelease)) {
Assert.assertEquals(jar.getVersion(), base);
assertEquals(base, jar.getVersion());
}
System.err.println("test versioning for Release " + value);
try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, value)) {
Assert.assertEquals(jf.getVersion(), expected);
assertEquals(expected, jf.getVersion());
}
// regular, unversioned, jar
try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, value)) {
Assert.assertEquals(jf.getVersion(), base);
assertEquals(base, jf.getVersion());
}
}
@Test(dataProvider="versions")
@ParameterizedTest
@MethodSource("createVersionData")
public void testAliasing(Runtime.Version version, int xpected) throws Exception {
int n = Math.max(version.major(), JarFile.baseVersion().major());
Runtime.Version value = Runtime.Version.parse(String.valueOf(n));
@ -231,7 +236,7 @@ public class MultiReleaseJarAPI {
}
assert versionedBytes.length > 0;
Assert.assertTrue(Arrays.equals(baseBytes, versionedBytes));
assertTrue(Arrays.equals(baseBytes, versionedBytes));
}
@Test
@ -243,11 +248,11 @@ public class MultiReleaseJarAPI {
try (JarFile jf = new JarFile(multirelease)) {
ze1 = jf.getEntry(vname);
}
Assert.assertEquals(ze1.getName(), vname);
assertEquals(vname, ze1.getName());
try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Runtime.Version.parse("9"))) {
ze2 = jf.getEntry(rname);
}
Assert.assertEquals(ze2.getName(), rname);
Assert.assertNotEquals(ze1.getName(), ze2.getName());
assertEquals(rname, ze2.getName());
assertNotEquals(ze2.getName(), ze1.getName());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2026, 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
@ -31,17 +31,17 @@
* @build CreateMultiReleaseTestJars
* jdk.test.lib.compiler.Compiler
* jdk.test.lib.util.JarBuilder
* @run testng MultiReleaseJarHttpProperties
* @run testng/othervm -Djdk.util.jar.version=0 MultiReleaseJarHttpProperties
* @run testng/othervm -Djdk.util.jar.version=8 MultiReleaseJarHttpProperties
* @run testng/othervm -Djdk.util.jar.version=9 MultiReleaseJarHttpProperties
* @run testng/othervm -Djdk.util.jar.version=100 MultiReleaseJarHttpProperties
* @run testng/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties
* @run testng/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties
* @run testng/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties
* @run testng/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties
* @run testng/othervm -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties
* @run testng/othervm -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties
* @run junit MultiReleaseJarHttpProperties
* @run junit/othervm -Djdk.util.jar.version=0 MultiReleaseJarHttpProperties
* @run junit/othervm -Djdk.util.jar.version=8 MultiReleaseJarHttpProperties
* @run junit/othervm -Djdk.util.jar.version=9 MultiReleaseJarHttpProperties
* @run junit/othervm -Djdk.util.jar.version=100 MultiReleaseJarHttpProperties
* @run junit/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties
* @run junit/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties
* @run junit/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties
* @run junit/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties
* @run junit/othervm -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties
* @run junit/othervm -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties
*/
import java.io.IOException;
@ -56,16 +56,19 @@ import java.util.concurrent.Executors;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.SimpleFileServer;
import jdk.test.lib.net.URIBuilder;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MultiReleaseJarHttpProperties extends MultiReleaseJarProperties {
private HttpServer server;
private ExecutorService executor;
static final String TESTCONTEXT = "/multi-release.jar"; //mapped to local file path
@BeforeClass
@BeforeAll
public void initialize() throws Exception {
server = SimpleFileServer.createFileServer(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0),
Path.of(System.getProperty("user.dir", ".")), SimpleFileServer.OutputLevel.INFO);
@ -86,7 +89,7 @@ public class MultiReleaseJarHttpProperties extends MultiReleaseJarProperties {
rootClass = cldr.loadClass("version.Main");
}
@AfterClass
@AfterAll
public void close() throws IOException {
// Windows requires server to stop before file is deleted
if (server != null) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2026, 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
@ -29,17 +29,17 @@
* @build CreateMultiReleaseTestJars
* jdk.test.lib.compiler.Compiler
* jdk.test.lib.util.JarBuilder
* @run testng MultiReleaseJarProperties
* @run testng/othervm -Djdk.util.jar.version=0 MultiReleaseJarProperties
* @run testng/othervm -Djdk.util.jar.version=8 MultiReleaseJarProperties
* @run testng/othervm -Djdk.util.jar.version=9 MultiReleaseJarProperties
* @run testng/othervm -Djdk.util.jar.version=100 MultiReleaseJarProperties
* @run testng/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties
* @run testng/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties
* @run testng/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties
* @run testng/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties
* @run testng/othervm -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties
* @run testng/othervm -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties
* @run junit MultiReleaseJarProperties
* @run junit/othervm -Djdk.util.jar.version=0 MultiReleaseJarProperties
* @run junit/othervm -Djdk.util.jar.version=8 MultiReleaseJarProperties
* @run junit/othervm -Djdk.util.jar.version=9 MultiReleaseJarProperties
* @run junit/othervm -Djdk.util.jar.version=100 MultiReleaseJarProperties
* @run junit/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties
* @run junit/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties
* @run junit/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties
* @run junit/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties
* @run junit/othervm -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties
* @run junit/othervm -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties
*/
import java.io.File;
@ -54,12 +54,15 @@ import java.nio.file.Files;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import static org.junit.jupiter.api.Assertions.*;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MultiReleaseJarProperties {
final static int BASE_VERSION = JarFile.baseVersion().major();
@ -70,7 +73,7 @@ public class MultiReleaseJarProperties {
protected ClassLoader cldr;
protected Class<?> rootClass;
@BeforeClass
@BeforeAll
public void initialize() throws Exception {
CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
creator.compileEntries();
@ -97,7 +100,7 @@ public class MultiReleaseJarProperties {
rootClass = cldr.loadClass("version.Main");
}
@AfterClass
@AfterAll
public void close() throws IOException {
((URLClassLoader) cldr).close();
Files.delete(multirelease.toPath());
@ -115,7 +118,7 @@ public class MultiReleaseJarProperties {
protected void invokeMethod(Class<?> vcls, int expected) throws Throwable {
MethodType mt = MethodType.methodType(int.class);
MethodHandle mh = MethodHandles.lookup().findVirtual(vcls, "getVersion", mt);
Assert.assertEquals(expected, (int) mh.invoke(vcls.newInstance()));
assertEquals(expected, (int) mh.invoke(vcls.newInstance()));
}
/*
@ -177,7 +180,7 @@ public class MultiReleaseJarProperties {
resource = new String(bytes);
}
String match = "return " + rtVersion + ";";
Assert.assertTrue(resource.contains(match));
assertTrue(resource.contains(match));
}
@Test
@ -194,6 +197,6 @@ public class MultiReleaseJarProperties {
resource = new String(bytes);
}
String match = "return " + rtVersion + ";";
Assert.assertTrue(resource.contains(match));
assertTrue(resource.contains(match));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2026, 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
@ -29,7 +29,7 @@
* @build CreateMultiReleaseTestJars
* jdk.test.lib.compiler.Compiler
* jdk.test.lib.util.JarBuilder
* @run testng MultiReleaseJarSecurity
* @run junit MultiReleaseJarSecurity
*/
import java.io.File;
@ -38,45 +38,45 @@ import java.io.InputStream;
import java.nio.file.Files;
import java.security.CodeSigner;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipFile;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class MultiReleaseJarSecurity {
static final int MAJOR_VERSION = Runtime.version().major();
String userdir = System.getProperty("user.dir",".");
File multirelease = new File(userdir, "multi-release.jar");
File signedmultirelease = new File(userdir, "signed-multi-release.jar");
static final String USER_DIR = System.getProperty("user.dir", ".");
static final File MULTI_RELEASE = new File(USER_DIR, "multi-release.jar");
static final File SIGNED_MULTI_RELEASE = new File(USER_DIR, "signed-multi-release.jar");
@BeforeClass
public void initialize() throws Exception {
@BeforeAll
public static void initialize() throws Exception {
CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
creator.compileEntries();
creator.buildMultiReleaseJar();
creator.buildSignedMultiReleaseJar();
}
@AfterClass
public void close() throws IOException {
Files.delete(multirelease.toPath());
Files.delete(signedmultirelease.toPath());
@AfterAll
public static void close() throws IOException {
Files.delete(MULTI_RELEASE.toPath());
Files.delete(SIGNED_MULTI_RELEASE.toPath());
}
@Test
public void testCertsAndSigners() throws IOException {
try (JarFile jf = new JarFile(signedmultirelease, true, ZipFile.OPEN_READ, Runtime.version())) {
try (JarFile jf = new JarFile(SIGNED_MULTI_RELEASE, true, ZipFile.OPEN_READ, Runtime.version())) {
CertsAndSigners vcas = new CertsAndSigners(jf, jf.getJarEntry("version/Version.class"));
CertsAndSigners rcas = new CertsAndSigners(jf, jf.getJarEntry("META-INF/versions/" + MAJOR_VERSION + "/version/Version.class"));
Assert.assertTrue(Arrays.equals(rcas.getCertificates(), vcas.getCertificates()));
Assert.assertTrue(Arrays.equals(rcas.getCodeSigners(), vcas.getCodeSigners()));
assertArrayEquals(rcas.getCertificates(), vcas.getCertificates());
assertArrayEquals(rcas.getCodeSigners(), vcas.getCodeSigners());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2026, 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
@ -28,7 +28,7 @@
* @library /test/lib
* @build jdk.test.lib.Platform
* jdk.test.lib.util.FileUtils
* @run testng TestVersionedStream
* @run junit TestVersionedStream
*/
import java.io.File;
@ -41,7 +41,6 @@ import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@ -54,18 +53,20 @@ import java.util.stream.Stream;
import java.util.zip.ZipFile;
import jdk.test.lib.util.FileUtils;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;
public class TestVersionedStream {
private final Path userdir;
private final Set<String> unversionedEntryNames;
private static final Path userdir;
private static final Set<String> unversionedEntryNames;
private static final int LATEST_VERSION = Runtime.version().feature();
public TestVersionedStream() throws IOException {
static {
userdir = Paths.get(System.getProperty("user.dir", "."));
// These are not real class files even though they end with .class.
@ -91,8 +92,7 @@ public class TestVersionedStream {
"--release " + LATEST_VERSION + " -C v" + LATEST_VERSION + " .");
System.out.println("Contents of mmr.jar\n=======");
try(JarFile jf = new JarFile("mmr.jar")) {
try (JarFile jf = new JarFile("mmr.jar")) {
unversionedEntryNames = jf.stream()
.map(je -> je.getName())
.peek(System.out::println)
@ -100,13 +100,14 @@ public class TestVersionedStream {
? nm.replaceFirst("META-INF/versions/\\d+/", "")
: nm)
.collect(Collectors.toCollection(LinkedHashSet::new));
} catch (IOException e) {
throw new RuntimeException("Failed to init \"unversionedEntryNames\"", e);
}
System.out.println("=======");
}
@AfterClass
public void close() throws IOException {
@AfterAll
public static void close() throws IOException {
Files.walk(userdir, 1)
.filter(p -> !p.equals(userdir))
.forEach(p -> {
@ -122,53 +123,41 @@ public class TestVersionedStream {
});
}
@DataProvider
public Object[][] data() {
return new Object[][] {
{Runtime.Version.parse("8")},
{Runtime.Version.parse("9")},
{Runtime.Version.parse("10")},
{Runtime.Version.parse(Integer.toString(LATEST_VERSION))},
{JarFile.baseVersion()},
{JarFile.runtimeVersion()}
};
public static Stream<Runtime.Version> arguments() {
return Stream.of(
Runtime.Version.parse("8"),
Runtime.Version.parse("9"),
Runtime.Version.parse("10"),
Runtime.Version.parse(Integer.toString(LATEST_VERSION)),
JarFile.baseVersion(),
JarFile.runtimeVersion()
);
}
@Test(dataProvider="data")
public void test(Runtime.Version version) throws Exception {
@ParameterizedTest
@MethodSource("arguments")
public void versionTest(Runtime.Version version) throws Exception {
try (JarFile jf = new JarFile(new File("mmr.jar"), false, ZipFile.OPEN_READ, version);
Stream<JarEntry> jes = jf.versionedStream())
{
Assert.assertNotNull(jes);
assertNotNull(jes);
// put versioned entries in list so we can reuse them
List<JarEntry> versionedEntries = jes.collect(Collectors.toList());
Assert.assertTrue(versionedEntries.size() > 0);
assertTrue(versionedEntries.size() > 0);
// also keep the names
List<String> versionedNames = new ArrayList<>(versionedEntries.size());
List<String> versionedNames = versionedEntries.stream()
.map(JarEntry::getName)
.collect(Collectors.toList());
// verify the correct order while building enames
Iterator<String> allIt = unversionedEntryNames.iterator();
Iterator<JarEntry> verIt = versionedEntries.iterator();
boolean match = false;
List<String> unversionedOrder = new ArrayList<>(unversionedEntryNames);
unversionedOrder.retainAll(versionedNames);
while (verIt.hasNext()) {
match = false;
if (!allIt.hasNext()) break;
String name = verIt.next().getName();
versionedNames.add(name);
while (allIt.hasNext()) {
if (name.equals(allIt.next())) {
match = true;
break;
}
}
}
if (!match) {
Assert.fail("versioned entries not in same order as unversioned entries");
}
assertIterableEquals(unversionedOrder, versionedNames,
"versioned entries not in same order as unversioned entries");
// verify the contents:
// value.[0] end of the path
@ -205,21 +194,21 @@ public class TestVersionedStream {
expected.put("q/Bar.class",
new String[] { "q/Bar.class", "META-INF/versions/10/q/Bar.class"});
} else {
Assert.fail("Test out of date, please add more cases");
fail("Test out of date, please add more cases");
}
}
expected.entrySet().stream().forEach(e -> {
String name = e.getKey();
int i = versionedNames.indexOf(name);
Assert.assertTrue(i != -1, name + " not in enames");
assertTrue(i != -1, name + " not in enames");
JarEntry je = versionedEntries.get(i);
try (InputStream is = jf.getInputStream(je)) {
String s = new String(is.readAllBytes()).replaceAll(System.lineSeparator(), "");
// end of the path
Assert.assertTrue(s.endsWith(e.getValue()[0]), s);
assertTrue(s.endsWith(e.getValue()[0]), s);
// getRealName()
Assert.assertTrue(je.getRealName().equals(e.getValue()[1]));
assertTrue(je.getRealName().equals(e.getValue()[1]));
} catch (IOException x) {
throw new UncheckedIOException(x);
}
@ -227,12 +216,12 @@ public class TestVersionedStream {
if (!unversionedEntryNames.contains("META-INF/Foo.class") ||
versionedNames.indexOf("META-INF/Foo.class") != -1) {
Assert.fail("versioned META-INF/Foo.class test failed");
fail("versioned META-INF/Foo.class test failed");
}
}
}
private void createFiles(String... files) {
private static void createFiles(String... files) {
ArrayList<String> list = new ArrayList();
Arrays.stream(files)
.map(f -> Paths.get(userdir.toAbsolutePath().toString(), f))
@ -248,7 +237,7 @@ public class TestVersionedStream {
}});
}
private void jar(String args) {
private static void jar(String args) {
ToolProvider jar = ToolProvider.findFirst("jar").orElseThrow();
jar.run(System.out, System.err, args.split(" +"));
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2026, 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
@ -26,18 +26,22 @@
@summary Make sure JarInputStream constructor will not
throw NullPointerException when the JAR file is
empty.
*/
@run junit EmptyJar
*/
import org.junit.jupiter.api.Test;
import java.util.jar.*;
import java.io.*;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class EmptyJar {
public static void main(String args[]) throws Exception {
try {
JarInputStream is = new JarInputStream
(new ByteArrayInputStream(new byte[0]));
} catch (NullPointerException e) {
throw new Exception("unexpected NullPointerException");
}
@Test
void npeTest() {
// Ensure no NPE is thrown
assertDoesNotThrow(() -> new JarInputStream(new ByteArrayInputStream(new byte[0])),
"unexpected NullPointerException");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2026, 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
@ -27,16 +27,24 @@
* @summary JarInputStream doesn't provide certificates for some file under META-INF
* @modules java.base/sun.security.tools.keytool
* jdk.jartool/sun.security.tools.jarsigner
* @run junit ExtraFileInMetaInf
*/
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.jar.*;
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ExtraFileInMetaInf {
public static void main(String args[]) throws Exception {
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;
public class ExtraFileInMetaInf {
@BeforeAll
static void setup() throws Exception {
// Create a zip file with 2 entries
try (ZipOutputStream zos =
new ZipOutputStream(new FileOutputStream("x.jar"))) {
@ -44,7 +52,6 @@ public class ExtraFileInMetaInf {
zos.write(new byte[10]);
zos.putNextEntry(new ZipEntry("x"));
zos.write(new byte[10]);
zos.close();
}
// Sign it
@ -54,7 +61,10 @@ public class ExtraFileInMetaInf {
"-keyalg rsa -alias a -dname CN=A -genkeypair").split(" "));
sun.security.tools.jarsigner.Main.main(
"-keystore ks -storepass changeit x.jar a".split(" "));
}
@Test
void checkSignedTest() throws IOException {
// Check if the entries are signed
try (JarInputStream jis =
new JarInputStream(new FileInputStream("x.jar"))) {
@ -63,9 +73,7 @@ public class ExtraFileInMetaInf {
String name = je.toString();
if (name.equals("META-INF/SUB/file") || name.equals("x")) {
while (jis.read(new byte[1000]) >= 0);
if (je.getCertificates() == null) {
throw new Exception(name + " not signed");
}
assertNotNull(je.getCertificates(), name + " not signed");
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2026, 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
@ -25,13 +25,19 @@
* @test
* @bug 6284489
* @summary Confirm that JarEntry.getCertificates identifies signed entries.
* @run junit ScanSignedJar
*/
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.URL;
import java.security.CodeSigner;
import java.security.cert.Certificate;
import java.util.jar.*;
import static org.junit.jupiter.api.Assertions.fail;
/*
* Confirm that the signed entries in a JAR file are identified correctly
* when JarEntry.getCertificates is used to extract the signer's certificates.
@ -43,13 +49,13 @@ import java.util.jar.*;
public class ScanSignedJar {
private static final String JAR_LOCATION =
"file:" +
System.getProperty("test.src", ".") +
System.getProperty("file.separator") +
"signed.jar";
public static void main(String[] args) throws Exception {
"file:" +
System.getProperty("test.src", ".") +
System.getProperty("file.separator") +
"signed.jar";
@Test
void signedJarTest() throws IOException {
System.out.println("Opening " + JAR_LOCATION + "...");
JarInputStream inStream =
new JarInputStream(new URL(JAR_LOCATION).openStream(), true);
@ -71,7 +77,7 @@ public class ScanSignedJar {
System.out.println("[unsigned]\t" + name + "\t(" + size +
" bytes)");
if (name.equals("Count.class")) {
throw new Exception("Count.class should be signed");
fail("Count.class should be signed");
}
} else if (signers != null && certificates != null) {
System.out.println("[" + signers.length +
@ -80,7 +86,7 @@ public class ScanSignedJar {
} else {
System.out.println("[*ERROR*]\t" + name + "\t(" + size +
" bytes)");
throw new Exception("Cannot determine whether the entry is " +
fail("Cannot determine whether the entry is " +
"signed or unsigned (signers[] doesn't match certs[]).");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2026, 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
@ -26,32 +26,32 @@
* @bug 6544278
* @summary Confirm the JarInputStream throws the SecurityException when
* verifying an indexed jar file with corrupted signature
* @run junit TestIndexedJarWithBadSignature
*/
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.FileInputStream;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class TestIndexedJarWithBadSignature {
public static void main(String...args) throws Throwable {
@Test
void securityExceptionTest() throws IOException {
try (JarInputStream jis = new JarInputStream(
new FileInputStream(System.getProperty("test.src", ".") +
System.getProperty("file.separator") +
"BadSignedJar.jar")))
{
JarEntry je1 = jis.getNextJarEntry();
while(je1!=null){
System.out.println("Jar Entry1==>"+je1.getName());
je1 = jis.getNextJarEntry(); // This should throw Security Exception
}
throw new RuntimeException(
"Test Failed:Security Exception not being thrown");
} catch (IOException ie){
ie.printStackTrace();
} catch (SecurityException e) {
System.out.println("Test passed: Security Exception thrown as expected");
new FileInputStream(System.getProperty("test.src", ".") +
System.getProperty("file.separator") +
"BadSignedJar.jar"))) {
assertThrows(SecurityException.class, () -> {
JarEntry je1;
while ((je1 = jis.getNextJarEntry()) != null) {
System.out.println("Jar Entry1==>" + je1.getName());
}
});
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2026, 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
@ -27,40 +27,43 @@
* @summary Jar tools fails to generate manifest correctly when boundary condition hit
* @modules jdk.jartool/sun.tools.jar
* @compile -XDignore.symbol.file=true CreateManifest.java
* @run main CreateManifest
* @run junit CreateManifest
*/
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.jar.*;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class CreateManifest {
public static void main(String arg[]) throws Exception {
@Test
void boundaryTest() throws IOException {
String jarFileName = "test.jar";
String ManifestName = "MANIFEST.MF";
String jarFileName = "test.jar";
String ManifestName = "MANIFEST.MF";
// create the MANIFEST.MF file
Files.write(Paths.get(ManifestName), FILE_CONTENTS.getBytes());
// create the MANIFEST.MF file
Files.write(Paths.get(ManifestName), FILE_CONTENTS.getBytes());
String [] args = new String [] { "cvfm", jarFileName, ManifestName};
sun.tools.jar.Main jartool =
new sun.tools.jar.Main(System.out, System.err, "jar");
jartool.run(args);
String [] args = new String [] { "cvfm", jarFileName, ManifestName};
sun.tools.jar.Main jartool =
new sun.tools.jar.Main(System.out, System.err, "jar");
jartool.run(args);
try (JarFile jf = new JarFile(jarFileName)) {
Manifest m = jf.getManifest();
String result = m.getMainAttributes().getValue("Class-path");
if (result == null)
throw new RuntimeException("Failed to add Class-path attribute to manifest");
} finally {
Files.deleteIfExists(Paths.get(jarFileName));
Files.deleteIfExists(Paths.get(ManifestName));
try (JarFile jf = new JarFile(jarFileName)) {
Manifest m = jf.getManifest();
String result = m.getMainAttributes().getValue("Class-path");
assertNotNull(result, "Failed to add Class-path attribute to manifest");
} finally {
Files.deleteIfExists(Paths.get(jarFileName));
Files.deleteIfExists(Paths.get(ManifestName));
}
}
}
private static final String FILE_CONTENTS =
"Class-path: \n" +
" /ade/dtsao_re/oracle/emcore//lib/em-core-testconsole-uimodel.jar \n" +

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2026, 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
@ -21,7 +21,9 @@
* questions.
*/
import java.io.File;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
@ -30,23 +32,29 @@ import java.util.concurrent.Callable;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.stream.Stream;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
/**
/*
* @test
* @bug 8216362
* @run main/othervm -Djdk.includeInExceptions=jar IncludeInExceptionsTest yes
* @run main/othervm IncludeInExceptionsTest
* @summary Verify that the property jdk.net.includeInExceptions works as expected
* @run junit/othervm -Djdk.includeInExceptions=jar IncludeInExceptionsTest
* @run junit/othervm IncludeInExceptionsTest
* @summary Verify that the property jdk.includeInExceptions works as expected
* when an error occurs while reading an invalid Manifest file.
*/
/*
* @see Manifest#Manifest(JarVerifier,InputStream,String)
* @see Manifest#getErrorPosition
*/
public class IncludeInExceptionsTest {
private static final boolean includeInExceptions = System.getProperty("jdk.includeInExceptions") != null;
static final String FILENAME = "Unique-Filename-Expected-In_Msg.jar";
static final byte[] INVALID_MANIFEST = (
@ -66,36 +74,26 @@ public class IncludeInExceptionsTest {
return jar;
}
static void test(Callable<?> attempt, boolean includeInExceptions) throws Exception {
try {
attempt.call();
throw new AssertionError("Excpected Exception not thrown");
} catch (IOException e) {
boolean foundFileName = e.getMessage().contains(FILENAME);
if(includeInExceptions && !foundFileName) {
throw new AssertionError("JAR file name expected but not found in error message");
} else if (foundFileName && !includeInExceptions) {
throw new AssertionError("JAR file name found but should not be in error message");
}
@ParameterizedTest
@MethodSource("manifests")
void testInvalidManifest(Callable<?> attempt) {
var ioException = assertThrows(IOException.class, attempt::call);
boolean foundFileName = ioException.getMessage().contains(FILENAME);
if (includeInExceptions && !foundFileName) {
fail("JAR file name expected but not found in error message");
} else if (foundFileName && !includeInExceptions) {
fail("JAR file name found but should not be in error message");
}
}
public static void main(String[] args) throws Exception {
boolean includeInExceptions;
if(args.length > 0) {
includeInExceptions = true;
System.out.println("**** Running test WITH -Djdk.includeInExceptions=jar");
} else {
includeInExceptions = false;
System.out.println("**** Running test WITHOUT -Djdk.includeInExceptions=jar");
}
test(() -> new JarFile(createJarInvalidManifest(FILENAME)).getManifest(),
includeInExceptions);
test(() -> new JarFile(createJarInvalidManifest("Verifying-" + FILENAME),
true).getManifest(), includeInExceptions);
static Stream<Callable<?>> manifests() {
Callable<?> jarName = () -> new JarFile(createJarInvalidManifest(FILENAME)).getManifest();
Callable<?> jarNameVerify = () -> new JarFile(createJarInvalidManifest("Verifying-" + FILENAME),
true).getManifest();
return Stream.of(
jarName,
jarNameVerify
);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2026, 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
@ -21,23 +21,22 @@
* questions.
*/
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.jar.Manifest;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
import org.junit.jupiter.api.Test;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.*;
/**
* @test
* @bug 6372077
* @run testng LineBreakLineWidth
* @run junit LineBreakLineWidth
* @summary write valid manifests with respect to line breaks
* and read any line width
*/
@ -99,16 +98,10 @@ public class LineBreakLineWidth {
assertMainAndSectionValues(mf, name, value);
}
static void writeInvalidManifestThrowsException(String name, String value)
throws IOException {
try {
writeManifest(name, value);
} catch (IllegalArgumentException e) {
// no invalid manifest was produced which is considered acceptable
return;
}
fail("no error writing manifest considered invalid");
static void writeInvalidManifestThrowsException(String name, String value) {
// no invalid manifest was produced which is considered acceptable
assertThrows(IllegalArgumentException.class,
() -> writeManifest(name, value), "no error writing manifest considered invalid");
}
/**
@ -278,8 +271,8 @@ public class LineBreakLineWidth {
String mainValue = mf.getMainAttributes().getValue(name);
String sectionValue = mf.getAttributes(name).getValue(name);
assertEquals(value, mainValue, "value different in main section");
assertEquals(value, sectionValue, "value different in named section");
assertEquals(mainValue, value, "value different in main section");
assertEquals(sectionValue, value, "value different in named section");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2026, 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
@ -21,8 +21,6 @@
* questions.
*/
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@ -32,13 +30,15 @@ import java.util.jar.Manifest;
import java.util.List;
import java.util.ArrayList;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
import org.junit.jupiter.api.Test;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.*;
/**
* @test
* @bug 8066619 8351567
* @run testng ValueUtf8Coding
* @run junit ValueUtf8Coding
* @summary Tests encoding and decoding manifest header values to and from
* UTF-8 with the complete Unicode character set.
*/ /*
@ -187,11 +187,11 @@ public class ValueUtf8Coding {
String value = values.get(i);
Name name = azName(i);
assertEquals(mf.getMainAttributes().getValue(name), value,
assertEquals(value, mf.getMainAttributes().getValue(name),
"main attributes header value");
Attributes attributes = mf.getAttributes(value);
assertNotNull(attributes, "named section");
assertEquals(attributes.getValue(name), value,
assertEquals(value, attributes.getValue(name),
"named section attributes value");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2026, 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
@ -21,22 +21,22 @@
* questions.
*/
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.util.jar.Manifest;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
import org.junit.jupiter.api.Test;
/**
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.*;
/*
* @test
* @bug 8066619
* @run testng WriteBinaryStructure
* @summary Tests that jar manifests are written in a particular structure
* @run junit WriteBinaryStructure
*/
public class WriteBinaryStructure {
@ -47,10 +47,11 @@ public class WriteBinaryStructure {
mf.getMainAttributes().put(new Name("Key"), "Value");
ByteArrayOutputStream buf = new ByteArrayOutputStream();
mf.write(buf);
assertEquals(buf.toByteArray(), (
assertArrayEquals((
"Manifest-Version: 1.0\r\n" +
"Key: Value\r\n" +
"\r\n").getBytes(UTF_8));
"\r\n").getBytes(UTF_8),
buf.toByteArray());
}
@Test
@ -62,12 +63,12 @@ public class WriteBinaryStructure {
attributes.put(new Name("Key"), "Value");
ByteArrayOutputStream buf = new ByteArrayOutputStream();
mf.write(buf);
assertEquals(buf.toByteArray(), (
assertArrayEquals((
"Manifest-Version: 1.0\r\n" +
"\r\n" +
"Name: Individual-Section-Name\r\n" +
"Key: Value\r\n" +
"\r\n").getBytes(UTF_8));
"\r\n").getBytes(UTF_8),
buf.toByteArray());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2026, 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
@ -21,180 +21,84 @@
* questions.
*/
/**
/*
* @test
* @bug 6480504 6303183
* @summary Test that client-provided data in the extra field is written and
* read correctly, taking into account the JAR_MAGIC written into the extra
* field of the first entry of JAR files.
* @author Dave Bristor
* field of the first entry of JAR files. ZIP file specific.
* @run junit TestExtra
*/
import org.junit.jupiter.api.Test;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.nio.charset.StandardCharsets;
import java.util.jar.*;
import java.util.zip.*;
/**
* Tests that the get/set operations on extra data in zip and jar files work
* as advertised. The base class tests ZIP files, the member class
* TestJarExtra checks JAR files.
*/
public class TestExtra {
static final int JAR_MAGIC = 0xcafe; // private IN JarOutputStream.java
static final int TEST_HEADER = 0xbabe;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
static final Charset ascii = Charset.forName("ASCII");
// Tests that the get/set operations on extra data in ZIP files work as advertised.
public class TestExtra {
static final int TEST_HEADER = 0xbabe;
static final Charset ascii = StandardCharsets.US_ASCII;
// ZipEntry extra data
static final byte[][] extra = new byte[][] {
ascii.encode("hello, world").array(),
ascii.encode("foo bar").array()
ascii.encode("hello, world").array(),
ascii.encode("foo bar").array()
};
// For naming entries in JAR/ZIP streams
int count = 1;
// For naming entries in ZIP streams
static int count = 1;
// Use byte arrays instead of files
ByteArrayOutputStream baos;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// JAR/ZIP content written here.
ZipOutputStream zos;
// ZIP content written here.
ZipOutputStream zos = assertDoesNotThrow(() -> getOutputStream(baos));
public static void realMain(String[] args) throws Throwable{
new TestExtra().testHeaderPlusData();
new TestJarExtra().testHeaderPlusData();
new TestJarExtra().testHeaderOnly();
new TestJarExtra().testClientJarMagic();
}
TestExtra() {
try {
baos = new ByteArrayOutputStream();
zos = getOutputStream(baos);
} catch (Throwable t) {
unexpected(t);
}
}
/** Test that a header + data set by client works. */
// Test that a header + data set by client works.
@Test
void testHeaderPlusData() throws IOException {
for (byte[] b : extra) {
ZipEntry ze = getEntry();
byte[] data = new byte[b.length + 4];
set16(data, 0, TEST_HEADER);
set16(data, 2, b.length);
for (int i = 0; i < b.length; i++) {
data[i + 4] = b[i];
}
System.arraycopy(b, 0, data, 4, b.length);
ze.setExtra(data);
zos.putNextEntry(ze);
}
zos.close();
ZipInputStream zis = getInputStream();
ZipEntry ze = zis.getNextEntry();
checkEntry(ze, 0, extra[0].length);
ze = zis.getNextEntry();
checkEntry(ze, 1, extra[1].length);
}
/** Test that a header only (i.e., no extra "data") set by client works. */
void testHeaderOnly() throws IOException {
ZipEntry ze = getEntry();
byte[] data = new byte[4];
set16(data, 0, TEST_HEADER);
set16(data, 2, 0); // Length of data is 0.
ze.setExtra(data);
zos.putNextEntry(ze);
zos.close();
ZipInputStream zis = getInputStream();
ze = zis.getNextEntry();
checkExtra(data, ze.getExtra());
checkEntry(ze, 0, 0);
}
/** Tests the client providing extra data which uses JAR_MAGIC header. */
void testClientJarMagic() throws IOException {
ZipEntry ze = getEntry();
byte[] data = new byte[8];
set16(data, 0, TEST_HEADER);
set16(data, 2, 0); // Length of data is 0.
set16(data, 4, JAR_MAGIC);
set16(data, 6, 0); // Length of data is 0.
ze.setExtra(data);
zos.putNextEntry(ze);
zos.close();
ZipInputStream zis = getInputStream();
ze = zis.getNextEntry();
byte[] e = ze.getExtra();
checkExtra(data, ze.getExtra());
checkEntry(ze, 0, 0);
}
// check if all "expected" extra fields equal to their
// corresponding fields in "extra". The "extra" might have
// timestamp fields added by ZOS.
static void checkExtra(byte[] expected, byte[] extra) {
if (expected == null)
return;
int off = 0;
int len = expected.length;
while (off + 4 < len) {
int tag = get16(expected, off);
int sz = get16(expected, off + 2);
int off0 = 0;
int len0 = extra.length;
boolean matched = false;
while (off0 + 4 < len0) {
int tag0 = get16(extra, off0);
int sz0 = get16(extra, off0 + 2);
if (tag == tag0 && sz == sz0) {
matched = true;
for (int i = 0; i < sz; i++) {
if (expected[off + i] != extra[off0 +i])
matched = false;
}
break;
}
off0 += (4 + sz0);
}
if (!matched) {
fail("Expected extra data [tag=" + tag + "sz=" + sz + "] check failed");
}
off += (4 + sz);
}
}
/** Check that the entry's extra data is correct. */
// Check that the entry's extra data is correct.
void checkEntry(ZipEntry ze, int count, int dataLength) {
byte[] extraData = ze.getExtra();
byte[] data = getField(TEST_HEADER, extraData);
if (!check(data != null, "unexpected null data for TEST_HEADER")) {
return;
}
assertNotNull(data, "unexpected null data for TEST_HEADER");
if (dataLength == 0) {
check(data.length == 0, "unexpected non-zero data length for TEST_HEADER");
assertEquals(0, data.length, "unexpected non-zero data length for TEST_HEADER");
} else {
check(Arrays.equals(extra[count], data),
"failed to get entry " + ze.getName()
+ ", expected " + new String(extra[count]) + ", got '" + new String(data) + "'");
assertArrayEquals(data, extra[count],
"failed to get entry " + ze.getName()
+ ", expected " + new String(extra[count]) + ", got '" + new String(data) + "'");
}
}
/** Look up descriptor in data, returning corresponding byte[]. */
// Look up descriptor in data, returning corresponding byte[].
static byte[] getField(int descriptor, byte[] data) {
byte[] rc = null;
try {
@ -218,69 +122,23 @@ public class TestExtra {
ZipInputStream getInputStream() {
return new ZipInputStream(
new ByteArrayInputStream(baos.toByteArray()));
new ByteArrayInputStream(baos.toByteArray()));
}
ZipOutputStream getOutputStream(ByteArrayOutputStream baos) throws IOException {
ZipOutputStream getOutputStream(ByteArrayOutputStream baos) {
return new ZipOutputStream(baos);
}
ZipInputStream getInputStream(ByteArrayInputStream bais) throws IOException {
return new ZipInputStream(bais);
ZipEntry getEntry() {
return new ZipEntry("zip" + count++ + ".txt");
}
ZipEntry getEntry() { return new ZipEntry("zip" + count++ + ".txt"); }
private static int get16(byte[] b, int off) {
return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8);
static int get16(byte[] b, int off) {
return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8);
}
private static void set16(byte[] b, int off, int value) {
b[off+0] = (byte)value;
b[off+1] = (byte)(value >> 8);
static void set16(byte[] b, int off, int value) {
b[off + 0] = (byte) value;
b[off + 1] = (byte) (value >> 8);
}
/** Test extra field of a JAR file. */
static class TestJarExtra extends TestExtra {
ZipOutputStream getOutputStream(ByteArrayOutputStream baos) throws IOException {
return new JarOutputStream(baos);
}
ZipInputStream getInputStream(ByteArrayInputStream bais) throws IOException {
return new JarInputStream(bais);
}
ZipEntry getEntry() { return new ZipEntry("jar" + count++ + ".txt"); }
void checkEntry(ZipEntry ze, int count, int dataLength) {
// zeroth entry should have JAR_MAGIC
if (count == 0) {
byte[] extraData = ze.getExtra();
byte[] data = getField(JAR_MAGIC, extraData);
if (!check(data != null, "unexpected null data for JAR_MAGIC")) {
check(data.length != 0, "unexpected non-zero data length for JAR_MAGIC");
}
}
// In a jar file, the first ZipEntry should have both JAR_MAGIC
// and the TEST_HEADER, so check that also.
super.checkEntry(ze, count, dataLength);
}
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void check(boolean cond) {if (cond) pass(); else fail();}
static boolean check(boolean cond, String msg) {if (cond) pass(); else fail(msg); return cond; }
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new Error("Some tests failed");}
}

View File

@ -0,0 +1,224 @@
/*
* Copyright (c) 2026, 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.
*
* 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.
*/
/*
* @test
* @bug 6480504 6303183
* @summary Test that client-provided data in the extra field is written and
* read correctly, taking into account the JAR_MAGIC written into the extra
* field of the first entry of JAR files. Jar file specific.
* @run junit TestJarExtra
*/
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;
// Tests that the get/set operations on extra data in JAR files work as advertised.
public class TestJarExtra {
static final int JAR_MAGIC = 0xcafe; // private IN JarOutputStream.java
static final int TEST_HEADER = 0xbabe;
// For naming entries in JAR streams
static int count = 1;
static final Charset ascii = StandardCharsets.US_ASCII;
// ZipEntry extra data
static final byte[][] extra = new byte[][] {
ascii.encode("hello, world").array(),
ascii.encode("foo bar").array()
};
// Use byte arrays instead of files
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// JAR content written here.
JarOutputStream jos = assertDoesNotThrow(() -> getOutputStream(baos));
// Test that a header + data set by client works.
@Test
void testHeaderPlusData() throws IOException {
for (byte[] b : extra) {
ZipEntry ze = getEntry();
byte[] data = new byte[b.length + 4];
set16(data, 0, TEST_HEADER);
set16(data, 2, b.length);
System.arraycopy(b, 0, data, 4, b.length);
ze.setExtra(data);
jos.putNextEntry(ze);
}
jos.close();
ZipInputStream zis = getInputStream();
ZipEntry ze = zis.getNextEntry();
checkEntry(ze, 0, extra[0].length);
ze = zis.getNextEntry();
checkEntry(ze, 1, extra[1].length);
}
// Test that a header only (i.e., no extra "data") set by client works.
@Test
void testHeaderOnly() throws IOException {
ZipEntry ze = getEntry();
byte[] data = new byte[4];
set16(data, 0, TEST_HEADER);
set16(data, 2, 0); // Length of data is 0.
ze.setExtra(data);
jos.putNextEntry(ze);
jos.close();
ZipInputStream zis = getInputStream();
ze = zis.getNextEntry();
checkExtra(data, ze.getExtra());
checkEntry(ze, 0, 0);
}
// Tests the client providing extra data which uses JAR_MAGIC header.
@Test
void testClientJarMagic() throws IOException {
ZipEntry ze = getEntry();
byte[] data = new byte[8];
set16(data, 0, TEST_HEADER);
set16(data, 2, 0); // Length of data is 0.
set16(data, 4, JAR_MAGIC);
set16(data, 6, 0); // Length of data is 0.
ze.setExtra(data);
jos.putNextEntry(ze);
jos.close();
ZipInputStream zis = getInputStream();
ze = zis.getNextEntry();
byte[] e = ze.getExtra();
checkExtra(data, ze.getExtra());
checkEntry(ze, 0, 0);
}
JarOutputStream getOutputStream(ByteArrayOutputStream baos) throws IOException {
return new JarOutputStream(baos);
}
ZipInputStream getInputStream() {
return new ZipInputStream(
new ByteArrayInputStream(baos.toByteArray()));
}
ZipEntry getEntry() {
return new ZipEntry("jar" + count++ + ".txt");
}
// check if all "expected" extra fields equal to their
// corresponding fields in "extra". The "extra" might have
// timestamp fields added by ZOS.
static void checkExtra(byte[] expected, byte[] extra) {
if (expected == null)
return;
int off = 0;
int len = expected.length;
while (off + 4 < len) {
int tag = get16(expected, off);
int sz = get16(expected, off + 2);
int off0 = 0;
int len0 = extra.length;
boolean matched = false;
while (off0 + 4 < len0) {
int tag0 = get16(extra, off0);
int sz0 = get16(extra, off0 + 2);
if (tag == tag0 && sz == sz0) {
matched = true;
for (int i = 0; i < sz; i++) {
if (expected[off + i] != extra[off0 + i])
matched = false;
}
break;
}
off0 += (4 + sz0);
}
if (!matched) {
fail("Expected extra data [tag=" + tag + "sz=" + sz + "] check failed");
}
off += (4 + sz);
}
}
void checkEntry(ZipEntry ze, int count, int dataLength) {
// zeroth entry should have JAR_MAGIC
if (count == 0) {
byte[] extraData = ze.getExtra();
byte[] data = getField(JAR_MAGIC, extraData);
assertNotNull(data, "unexpected null data for JAR_MAGIC");
assertEquals(0, data.length, "unexpected non-zero data length for JAR_MAGIC");
}
// In a JAR file, the first ZipEntry should have both JAR_MAGIC
// and the TEST_HEADER, so check that also.
byte[] extraData = ze.getExtra();
byte[] data = getField(TEST_HEADER, extraData);
assertNotNull(data, "unexpected null data for TEST_HEADER");
if (dataLength == 0) {
assertEquals(0, data.length, "unexpected non-zero data length for TEST_HEADER");
} else {
assertArrayEquals(data, extra[count],
"failed to get entry " + ze.getName()
+ ", expected " + new String(extra[count]) + ", got '" + new String(data) + "'");
}
}
// Look up descriptor in data, returning corresponding byte[].
static byte[] getField(int descriptor, byte[] data) {
byte[] rc = null;
try {
int i = 0;
while (i < data.length) {
if (get16(data, i) == descriptor) {
int length = get16(data, i + 2);
rc = new byte[length];
for (int j = 0; j < length; j++) {
rc[j] = data[i + 4 + j];
}
return rc;
}
i += get16(data, i + 2) + 4;
}
} catch (ArrayIndexOutOfBoundsException e) {
// descriptor not found
}
return rc;
}
static int get16(byte[] b, int off) {
return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8);
}
static void set16(byte[] b, int off, int value) {
b[off + 0] = (byte) value;
b[off + 1] = (byte) (value >> 8);
}
}