undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
88
test/jdk/java/util/jar/Attributes/IterationOrder.java
Normal file
88
test/jdk/java/util/jar/Attributes/IterationOrder.java
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 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 {
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void checkOrderTest(Attributes.Name k0, String v0,
|
||||
Attributes.Name k1, String v1,
|
||||
Attributes.Name k2, String v2) {
|
||||
Attributes x = new Attributes();
|
||||
x.put(k0, v0);
|
||||
x.put(k1, v1);
|
||||
x.put(k2, v2);
|
||||
Map.Entry<?,?>[] entries
|
||||
= x.entrySet().toArray(new Map.Entry<?,?>[3]);
|
||||
if (!(entries.length == 3
|
||||
&& entries[0].getKey() == k0
|
||||
&& entries[0].getValue() == v0
|
||||
&& entries[1].getKey() == k1
|
||||
&& entries[1].getValue() == v1
|
||||
&& entries[2].getKey() == k2
|
||||
&& entries[2].getValue() == v2)) {
|
||||
fail(Arrays.toString(entries));
|
||||
}
|
||||
|
||||
Object[] keys = x.keySet().toArray();
|
||||
if (!(keys.length == 3
|
||||
&& keys[0] == k0
|
||||
&& keys[1] == k1
|
||||
&& keys[2] == k2)) {
|
||||
fail(Arrays.toString(keys));
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
44
test/jdk/java/util/jar/Attributes/Name.java
Normal file
44
test/jdk/java/util/jar/Attributes/Name.java
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 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 {
|
||||
|
||||
@Test
|
||||
void emptyStringTest() {
|
||||
assertThrows(IllegalArgumentException.class, () -> new Attributes.Name(""),
|
||||
"empty string should be rejected");
|
||||
}
|
||||
}
|
||||
199
test/jdk/java/util/jar/Attributes/NullAndEmptyKeysAndValues.java
Normal file
199
test/jdk/java/util/jar/Attributes/NullAndEmptyKeysAndValues.java
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.jar.Attributes.Name;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
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 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.
|
||||
*/
|
||||
/*
|
||||
* Note to future maintainer:
|
||||
* In order to actually being able to test all the cases where key and values
|
||||
* are null normal manifest and attributes manipulation through their public
|
||||
* api is not sufficient but then there were these null checks there before
|
||||
* which may or may not have had their reason and this way it's ensured that
|
||||
* the behavior does not change with that respect.
|
||||
* Once module isolation is enforced some test cases will not any longer be
|
||||
* possible and those now tested situations will be guaranteed not to occur
|
||||
* any longer at all at which point the corresponding tests can be removed
|
||||
* safely without replacement unless of course another way is found inject the
|
||||
* tested null values.
|
||||
* Another trick to access package private class members could be to use
|
||||
* deserialization or adding a new class to the same package on the classpath.
|
||||
* Here is not important how the values are set to null because it shows that
|
||||
* the behavior remains unchanged.
|
||||
*/
|
||||
public class NullAndEmptyKeysAndValues {
|
||||
|
||||
static final String SOME_KEY = "some-key";
|
||||
static final String SOME_VALUE = "some value";
|
||||
static final String NULL_TEXT = "null";
|
||||
static final String EMPTY_STR = "";
|
||||
static final Name EMPTY_NAME = new Name("tmp") {{
|
||||
try {
|
||||
Field name = Name.class.getDeclaredField("name");
|
||||
name.setAccessible(true);
|
||||
name.set(this, EMPTY_STR);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}};
|
||||
|
||||
@Test
|
||||
public void testMainAttributesHeaderNameNull() throws Exception {
|
||||
Manifest mf = new Manifest();
|
||||
Field attr = mf.getClass().getDeclaredField("attr");
|
||||
attr.setAccessible(true);
|
||||
Attributes mainAtts = new Attributes() {{
|
||||
super.put(null, SOME_VALUE);
|
||||
}};
|
||||
attr.set(mf, mainAtts);
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
assertThrows(NullPointerException.class, () -> writeAndRead(mf));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMainAttributesHeaderNameEmpty() throws Exception {
|
||||
Manifest mf = new Manifest();
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
mf.getMainAttributes().put(EMPTY_NAME, SOME_VALUE);
|
||||
assertThrows(IOException.class, () -> writeAndRead(mf));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMainAttributesHeaderValueNull() throws Exception {
|
||||
Manifest mf = new Manifest();
|
||||
Field attr = mf.getClass().getDeclaredField("attr");
|
||||
attr.setAccessible(true);
|
||||
Attributes mainAtts = new Attributes() {{
|
||||
map.put(new Name(SOME_KEY), null);
|
||||
}};
|
||||
attr.set(mf, mainAtts);
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
mf = writeAndRead(mf);
|
||||
assertEquals(NULL_TEXT, mf.getMainAttributes().getValue(SOME_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMainAttributesHeaderValueEmpty() throws Exception {
|
||||
Manifest mf = new Manifest();
|
||||
Field attr = mf.getClass().getDeclaredField("attr");
|
||||
attr.setAccessible(true);
|
||||
Attributes mainAtts = new Attributes() {{
|
||||
map.put(new Name(SOME_KEY), EMPTY_STR);
|
||||
}};
|
||||
attr.set(mf, mainAtts);
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
mf = writeAndRead(mf);
|
||||
assertEquals(EMPTY_STR, mf.getMainAttributes().getValue(SOME_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSectionNameNull() throws IOException {
|
||||
Manifest mf = new Manifest();
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
mf.getEntries().put(null, new Attributes());
|
||||
mf = writeAndRead(mf);
|
||||
assertNotNull(mf.getEntries().get(NULL_TEXT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSectionNameEmpty() throws IOException {
|
||||
Manifest mf = new Manifest();
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
mf.getEntries().put(EMPTY_STR, new Attributes());
|
||||
mf = writeAndRead(mf);
|
||||
assertNotNull(mf.getEntries().get(EMPTY_STR));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNamedSectionHeaderNameNull() throws IOException {
|
||||
Manifest mf = new Manifest();
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
mf.getEntries().put(SOME_KEY, new Attributes() {{
|
||||
map.put(null, SOME_VALUE);
|
||||
}});
|
||||
assertThrows(NullPointerException.class, () -> writeAndRead(mf));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNamedSectionHeaderNameEmpty() throws IOException {
|
||||
Manifest mf = new Manifest();
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
mf.getEntries().put(SOME_KEY, new Attributes() {{
|
||||
map.put(EMPTY_NAME, SOME_VALUE);
|
||||
}});
|
||||
assertThrows(IOException.class, () -> writeAndRead(mf));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNamedSectionHeaderValueNull() throws IOException {
|
||||
Manifest mf = new Manifest();
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
mf.getEntries().put(SOME_KEY, new Attributes() {{
|
||||
map.put(new Name(SOME_KEY), null);
|
||||
}});
|
||||
mf = writeAndRead(mf);
|
||||
assertEquals(NULL_TEXT, mf.getEntries().get(SOME_KEY).getValue(SOME_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNamedSectionHeaderValueEmpty() throws IOException {
|
||||
Manifest mf = new Manifest();
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
mf.getEntries().put(SOME_KEY, new Attributes() {{
|
||||
map.put(new Name(SOME_KEY), EMPTY_STR);
|
||||
}});
|
||||
mf = writeAndRead(mf);
|
||||
assertEquals(EMPTY_STR, mf.getEntries().get(SOME_KEY).getValue(SOME_KEY));
|
||||
}
|
||||
|
||||
static Manifest writeAndRead(Manifest mf) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
mf.write(out);
|
||||
byte[] mfBytes = out.toByteArray();
|
||||
System.out.println("-".repeat(72));
|
||||
System.out.print(new String(mfBytes, UTF_8));
|
||||
System.out.println("-".repeat(72));
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(mfBytes);
|
||||
return new Manifest(in);
|
||||
}
|
||||
|
||||
}
|
||||
51
test/jdk/java/util/jar/Attributes/PutAndPutAll.java
Normal file
51
test/jdk/java/util/jar/Attributes/PutAndPutAll.java
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 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 {
|
||||
|
||||
@Test
|
||||
void classCastTest() {
|
||||
Attributes at = new Attributes();
|
||||
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");
|
||||
}
|
||||
}
|
||||
134
test/jdk/java/util/jar/Attributes/TestAttrsNL.java
Normal file
134
test/jdk/java/util/jar/Attributes/TestAttrsNL.java
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 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 {
|
||||
|
||||
static Stream<Arguments> newLineAttributesTest() throws IOException {
|
||||
|
||||
String manifestStr =
|
||||
"Manifest-Version: 1.0\r\n" +
|
||||
"Created-By: 11 (Oracle Corporation)\r\n" +
|
||||
"key1: value1\r\n" +
|
||||
"key2: value2\r\n END\r\n" +
|
||||
"key3: value3\r\n \r\n" +
|
||||
"key4: value4\r\n" +
|
||||
"\r\n\r\n" +
|
||||
"Name: Hello\r\n" +
|
||||
"key11: value11\r\n" +
|
||||
"key22: value22\r\n END\r\n" +
|
||||
"key33: value33\r\n \r\n" +
|
||||
"key44: value44\r\n";
|
||||
|
||||
Map<Name, String> mainAttrsExped = Map.of(
|
||||
new Name("Manifest-Version"), "1.0",
|
||||
new Name("Created-By"), "11 (Oracle Corporation)",
|
||||
new Name("key1"), "value1",
|
||||
new Name("key2"), "value2END",
|
||||
new Name("key3"), "value3",
|
||||
new Name("key4"), "value4"
|
||||
);
|
||||
|
||||
Map<Name, String> attrsExped = Map.of(
|
||||
new Name("key11"), "value11",
|
||||
new Name("key22"), "value22END",
|
||||
new Name("key33"), "value33",
|
||||
new Name("key44"), "value44"
|
||||
);
|
||||
|
||||
var normal = Arguments.of(new Manifest(new ByteArrayInputStream(manifestStr.getBytes(UTF_8))),
|
||||
mainAttrsExped, attrsExped);
|
||||
|
||||
var carriage = Arguments.of(new Manifest(new ByteArrayInputStream(
|
||||
manifestStr.replaceAll("\r\n", "\r").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 =
|
||||
"Manifest-Version: 1.0\r\n" +
|
||||
"Created-By: 11 (Oracle Corporation)\n" +
|
||||
"key1: value1\r" +
|
||||
"key2: value2\r\n END\r" +
|
||||
"key3: value3\n \r\n" +
|
||||
"key4: value4\r" +
|
||||
"\r\n\n" +
|
||||
"Name: Hello\r\n" +
|
||||
"key11: value11\r" +
|
||||
"key22: value22\n END\r\n" +
|
||||
"key33: value33\r \n" +
|
||||
"key44: value44\n";
|
||||
var mixed = Arguments.of(new Manifest(new ByteArrayInputStream(manifestStr.getBytes(UTF_8))),
|
||||
mainAttrsExped, attrsExped);
|
||||
|
||||
return Stream.of(normal, carriage, newLine, mixed);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void newLineAttributesTest(Manifest m,
|
||||
Map<Name, String> mainAttrsExped,
|
||||
Map<Name, String> attrsExped) {
|
||||
Attributes mainAttrs = m.getMainAttributes();
|
||||
mainAttrsExped.forEach( (k, 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) -> {
|
||||
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));
|
||||
});
|
||||
}
|
||||
}
|
||||
92
test/jdk/java/util/jar/JarEntry/GetMethodsReturnClones.java
Normal file
92
test/jdk/java/util/jar/JarEntry/GetMethodsReturnClones.java
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 6337925
|
||||
* @summary Ensure that callers cannot modify the internal JarEntry cert and
|
||||
* codesigner arrays.
|
||||
* @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;
|
||||
|
||||
@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];
|
||||
Enumeration<JarEntry> e = jf.entries();
|
||||
while (e.hasMoreElements()) {
|
||||
JarEntry je = e.nextElement();
|
||||
entries.add(je);
|
||||
try (InputStream is = jf.getInputStream(je)) {
|
||||
while (is.read(buffer, 0, buffer.length) != -1) {
|
||||
// we just read. this will throw a SecurityException
|
||||
// if a signature/digest check fails.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
jarEntries = entries;
|
||||
}
|
||||
|
||||
@Test
|
||||
void certsTest() {
|
||||
for (JarEntry je : jarEntries) {
|
||||
Certificate[] certs = je.getCertificates();
|
||||
if (certs != null) {
|
||||
certs[0] = null;
|
||||
certs = je.getCertificates();
|
||||
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();
|
||||
assertNotNull(signers[0], "Modified internal codesigners array");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/jdk/java/util/jar/JarEntry/test.jar
Normal file
BIN
test/jdk/java/util/jar/JarEntry/test.jar
Normal file
Binary file not shown.
65
test/jdk/java/util/jar/JarFile/Constructor.java
Normal file
65
test/jdk/java/util/jar/JarFile/Constructor.java
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 4842702 8211765
|
||||
* @summary Check that constructors throw specified exceptions
|
||||
* @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 {
|
||||
|
||||
@Test
|
||||
void constructorTest() {
|
||||
|
||||
assertThrows(NullPointerException.class, () -> new JarFile ((File) null, true, JarFile.OPEN_READ));
|
||||
|
||||
assertThrows(NullPointerException.class, () -> new JarFile ((File) null, true));
|
||||
|
||||
assertThrows(NullPointerException.class, () -> new JarFile ((File) null));
|
||||
|
||||
assertThrows(NullPointerException.class, () -> new JarFile ((String) null, true));
|
||||
|
||||
assertThrows(NullPointerException.class, () -> new JarFile ((String) null));
|
||||
|
||||
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";
|
||||
|
||||
assertThrows(IOException.class, () -> new JarFile (invalidOSPath));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,394 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 8300140
|
||||
* @summary Make sure signature related files in subdirectories of META-INF are not considered for verification
|
||||
* @modules java.base/jdk.internal.access
|
||||
* @modules java.base/sun.security.util
|
||||
* @modules java.base/sun.security.tools.keytool
|
||||
* @modules jdk.jartool/sun.security.tools.jarsigner
|
||||
* @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;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.CodeSigner;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.CertPath;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
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();
|
||||
|
||||
// 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;
|
||||
|
||||
@BeforeAll
|
||||
static void setupJars() throws Exception {
|
||||
// Regular signed JAR
|
||||
j = createJarFile();
|
||||
s = signJarFile(j, "SIGNER1", "signed");
|
||||
|
||||
// Singed JAR with unrelated signature files
|
||||
m = moveSignatureRelated(s);
|
||||
sm = signJarFile(m, "SIGNER2", "modified-signed");
|
||||
|
||||
// Signed JAR with custom SIG-* files
|
||||
ca = createCustomAlgJar();
|
||||
cas = signJarFile(ca, "SIGNER1", "custom-signed");
|
||||
}
|
||||
|
||||
// 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();
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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("/"));
|
||||
assertEquals("META-INF", dir,
|
||||
"Signature related file does not reside directly in META-INF/ : " + signatureRelatedFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Check JarInputStream with doVerify = true
|
||||
@Test
|
||||
void jarIStreamDoVerifyTest() throws IOException {
|
||||
try (JarInputStream in = new JarInputStream(Files.newInputStream(m), true)) {
|
||||
while (in.getNextEntry() != null) {
|
||||
in.transferTo(OutputStream.nullOutputStream());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
String manifest = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
// 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
|
||||
assertFalse(manifest.startsWith("manifest-version"),
|
||||
"JarSigner unexpectedly treated unsigned jar as 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");
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
List<String> expectedOrder = List.of(
|
||||
"META-INF/MANIFEST.MF",
|
||||
"META-INF/SIGNER2.SF",
|
||||
"META-INF/SIGNER2.RSA",
|
||||
"META-INF/subdirectory/META-INF/SIGNER1.SF",
|
||||
"META-INF/subdirectory/META-INF/SIGNER1.RSA",
|
||||
"a.txt",
|
||||
"META-INF/subdirectory2/META-INF/SIGNER1.SF",
|
||||
"META-INF/subdirectory2/META-INF/SIGNER1.RSA"
|
||||
);
|
||||
|
||||
assertEquals(expectedOrder, actualOrder, ("""
|
||||
Unexpected file order in JAR with unrelated SF,RSA files
|
||||
Expected order: %s
|
||||
Actual order: %s""")
|
||||
.formatted(expectedOrder, actualOrder));
|
||||
}
|
||||
}
|
||||
|
||||
// Check that jarsigner ignores unrelated signature files
|
||||
@Test
|
||||
void jarSignerIgnoresUnrelatedTest() throws Exception {
|
||||
String message = jarSignerVerify(m);
|
||||
assertFalse(message.contains("WARNING"),
|
||||
"jarsigner output contains unexpected warning: " + message);
|
||||
}
|
||||
|
||||
// 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
|
||||
Set<String> expectedSigned = Set.of("a.txt",
|
||||
"META-INF/unrelated.txt",
|
||||
"META-INF/SIG-CUSTOM2.C-1",
|
||||
"META-INF/SIG-CUSTOM2.",
|
||||
"META-INF/SIG-CUSTOM2.ABCD",
|
||||
"META-INF/subdirectory/SIG-CUSTOM2.SF",
|
||||
"META-INF/subdirectory/SIG-CUSTOM2.CS1"
|
||||
);
|
||||
|
||||
Set<String> actualSigned = jf.getManifest().getEntries().keySet();
|
||||
|
||||
assertEquals(expectedSigned, actualSigned,
|
||||
"Unexpected MANIFEST entries. Expected %s, got %s"
|
||||
.formatted(expectedSigned, actualSigned));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* run "jarsigner -verify" on the JAR and return the captured output
|
||||
*/
|
||||
private static String jarSignerVerify(Path m) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PrintStream currentOut = System.out;
|
||||
try {
|
||||
System.setOut(new PrintStream(out));
|
||||
Main.main(new String[] {"-verify", m.toAbsolutePath().toString()});
|
||||
return out.toString(StandardCharsets.UTF_8);
|
||||
} finally {
|
||||
System.setOut(currentOut);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a path of a given JAR is signed once by the expected signer CN
|
||||
*/
|
||||
private static void checkSignedBy(JarFile jf, String name, String expectedSigner) throws Exception {
|
||||
JarEntry je = jf.getJarEntry(name);
|
||||
|
||||
// Read the contents to trigger verification
|
||||
try (InputStream in = jf.getInputStream(je)) {
|
||||
in.transferTo(OutputStream.nullOutputStream());
|
||||
}
|
||||
|
||||
// Verify that the entry is signed
|
||||
CodeSigner[] signers = je.getCodeSigners();
|
||||
assertNotNull(signers, "Expected %s to be signed".formatted(name));
|
||||
|
||||
// There should be a single signer
|
||||
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();
|
||||
assertEquals(expectedSigner, actualSigner,
|
||||
"Expected %s to be signed by %s, was signed by %s".formatted(name, expectedSigner, actualSigner));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a jar file with a '*.SF' file residing in META-INF/subdirectory/
|
||||
*/
|
||||
private static Path createJarFile() throws Exception {
|
||||
|
||||
Path jar = Path.of("unrelated-signature-file.jar");
|
||||
|
||||
Manifest manifest = new Manifest();
|
||||
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||
try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(jar), manifest)) {
|
||||
write(out, "a.txt", "a");
|
||||
}
|
||||
|
||||
return jar;
|
||||
}
|
||||
|
||||
private static Path createCustomAlgJar() throws Exception {
|
||||
Path jar = Path.of("unrelated-signature-file-custom-sig.jar");
|
||||
|
||||
Manifest manifest = new Manifest();
|
||||
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||
try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(jar), manifest)) {
|
||||
// Regular file
|
||||
write(out, "a.txt", "a");
|
||||
// Regular file in META-INF
|
||||
write(out, "META-INF/unrelated.txt", "a");
|
||||
|
||||
// Custom SIG files with valid extension (no extension is also OK)
|
||||
write(out, "META-INF/SIG-CUSTOM.SF", "");
|
||||
write(out, "META-INF/SIG-CUSTOM.CS1", "");
|
||||
write(out, "META-INF/SIG-CUSTOM", "");
|
||||
|
||||
// Custom SIG files with invalid extensions
|
||||
write(out, "META-INF/SIG-CUSTOM2.SF", "");
|
||||
write(out, "META-INF/SIG-CUSTOM2.C-1", "");
|
||||
write(out, "META-INF/SIG-CUSTOM2.", "");
|
||||
write(out, "META-INF/SIG-CUSTOM2.ABCD", "");
|
||||
|
||||
// Custom SIG files with valid extensions in subdirectories
|
||||
write(out, "META-INF/subdirectory/SIG-CUSTOM2.SF", "");
|
||||
write(out, "META-INF/subdirectory/SIG-CUSTOM2.CS1", "");
|
||||
|
||||
}
|
||||
|
||||
return jar;
|
||||
}
|
||||
|
||||
private static void write(JarOutputStream out, String name, String content) throws IOException {
|
||||
out.putNextEntry(new JarEntry(name));
|
||||
out.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a signed version of the given jar file
|
||||
*/
|
||||
private static Path signJarFile(Path jar, String signerName, String classifier) throws Exception {
|
||||
Path s = Path.of("unrelated-signature-files-" + classifier +".jar");
|
||||
|
||||
Files.deleteIfExists(Path.of("ks"));
|
||||
|
||||
sun.security.tools.keytool.Main.main(
|
||||
("-keystore ks -storepass changeit -keypass changeit -dname" +
|
||||
" CN=" + signerName +" -alias r -genkeypair -keyalg rsa").split(" "));
|
||||
|
||||
char[] pass = "changeit".toCharArray();
|
||||
|
||||
KeyStore ks = KeyStore.getInstance(
|
||||
new File("ks"), pass);
|
||||
PrivateKey pkr = (PrivateKey)ks.getKey("r", pass);
|
||||
|
||||
CertPath cp = CertificateFactory.getInstance("X.509")
|
||||
.generateCertPath(Arrays.asList(ks.getCertificateChain("r")));
|
||||
|
||||
JarSigner signer = new JarSigner.Builder(pkr, cp)
|
||||
.digestAlgorithm("SHA-256")
|
||||
.signatureAlgorithm("SHA256withRSA")
|
||||
.signerName(signerName)
|
||||
.build();
|
||||
|
||||
try (ZipFile in = new ZipFile(jar.toFile());
|
||||
OutputStream out = Files.newOutputStream(s)) {
|
||||
signer.sign(in, out);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a modified version of a signed jar file where signature-related files
|
||||
* are moved into a subdirectory of META-INF/ and the manifest is changed to trigger
|
||||
* a digest mismatch.
|
||||
*
|
||||
* Since the signature related files are moved out of META-INF/, the returned jar file should
|
||||
* not be considered signed
|
||||
*/
|
||||
private static Path moveSignatureRelated(Path s) throws Exception {
|
||||
Path m = Path.of("unrelated-signature-files-modified.jar");
|
||||
|
||||
try (ZipFile in = new ZipFile(s.toFile());
|
||||
ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(m))) {
|
||||
|
||||
// Change the digest of the manifest by lower-casing the Manifest-Version attribute:
|
||||
out.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
|
||||
out.write("manifest-version: 1.0\n\n".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
copy("META-INF/SIGNER1.SF", "META-INF/subdirectory/META-INF/SIGNER1.SF", in, out);
|
||||
copy("META-INF/SIGNER1.RSA", "META-INF/subdirectory/META-INF/SIGNER1.RSA", in, out);
|
||||
|
||||
// Copy over the regular a.txt file
|
||||
copy("a.txt", "a.txt", in, out);
|
||||
|
||||
// These are also just regular files in their new location, but putting them at end
|
||||
// allows us to verify that JarSigner does not move them to the beginning of the signed JAR
|
||||
copy("META-INF/SIGNER1.SF", "META-INF/subdirectory2/META-INF/SIGNER1.SF", in, out);
|
||||
copy("META-INF/SIGNER1.RSA", "META-INF/subdirectory2/META-INF/SIGNER1.RSA", in, out);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
// Copy a file from a ZipFile into a ZipOutputStream
|
||||
private static void copy(String from, String to, ZipFile in, ZipOutputStream out) throws Exception {
|
||||
out.putNextEntry(new ZipEntry(to));
|
||||
try (InputStream zi = in.getInputStream(new ZipEntry(from))) {
|
||||
zi.transferTo(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
69
test/jdk/java/util/jar/JarFile/JarBacktickManifest.java
Normal file
69
test/jdk/java/util/jar/JarFile/JarBacktickManifest.java
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 8186334
|
||||
* @summary Make sure scanning manifest doesn't throw AIOOBE on certain strings containing backticks.
|
||||
* @library /test/lib/
|
||||
* @build jdk.test.lib.util.JarBuilder
|
||||
* @run junit JarBacktickManifest
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
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";
|
||||
|
||||
@BeforeAll
|
||||
public static void initialize() throws Exception {
|
||||
JarBuilder jb = new JarBuilder(VERIFY_MANIFEST_JAR);
|
||||
jb.addAttribute("Test", " Class-`Path` ");
|
||||
jb.addAttribute("Test2", " Multi-`Release ");
|
||||
jb.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void backtickTest() throws Exception {
|
||||
try (JarFile jf = new JarFile(VERIFY_MANIFEST_JAR)) { // do not set runtime versioning
|
||||
assertFalse(jf.isMultiRelease(), "Shouldn't be multi-release");
|
||||
}
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void close() throws IOException {
|
||||
Files.delete(new File(VERIFY_MANIFEST_JAR).toPath());
|
||||
}
|
||||
}
|
||||
48
test/jdk/java/util/jar/JarFile/JarNoManifest.java
Normal file
48
test/jdk/java/util/jar/JarFile/JarNoManifest.java
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 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 {
|
||||
|
||||
@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));
|
||||
}
|
||||
}
|
||||
50
test/jdk/java/util/jar/JarFile/MevNPE.java
Normal file
50
test/jdk/java/util/jar/JarFile/MevNPE.java
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 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 {
|
||||
|
||||
@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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
67
test/jdk/java/util/jar/JarFile/ScanSignedJar.java
Normal file
67
test/jdk/java/util/jar/JarFile/ScanSignedJar.java
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 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.util.Enumeration;
|
||||
import java.util.jar.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class ScanSignedJar {
|
||||
|
||||
@Test
|
||||
void unsupportedSignerTest() throws IOException {
|
||||
boolean isSigned = false;
|
||||
try (JarFile file = new JarFile(new File(System.getProperty("test.src","."),
|
||||
"bogus-signerinfo-attr.jar"))) {
|
||||
byte[] buffer = new byte[8192];
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
if (entry.getCertificates() != null) {
|
||||
isSigned = true;
|
||||
}
|
||||
System.out.println((isSigned ? "[signed] " : "\t ") +
|
||||
entry.getName());
|
||||
}
|
||||
}
|
||||
assertTrue(isSigned, "Failed to detect that the JAR file is signed");
|
||||
}
|
||||
}
|
||||
BIN
test/jdk/java/util/jar/JarFile/Signed.jar
Normal file
BIN
test/jdk/java/util/jar/JarFile/Signed.jar
Normal file
Binary file not shown.
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 4845692 8206863
|
||||
* @summary JarFile.getInputStream should not throw when jar file is signed
|
||||
* @run junit SignedJarFileGetInputStream
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.jar.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
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 = 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];
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
194
test/jdk/java/util/jar/JarFile/SignedJarPendingBlock.java
Normal file
194
test/jdk/java/util/jar/JarFile/SignedJarPendingBlock.java
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* 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
|
||||
* 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
|
||||
* @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;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Collections;
|
||||
import java.util.jar.*;
|
||||
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 {
|
||||
|
||||
static Path signed;
|
||||
static Path pendingBlocks;
|
||||
static Path invalid;
|
||||
|
||||
// Construct the test data
|
||||
@BeforeAll
|
||||
static void setup() throws Exception {
|
||||
Path jar = createJarFile();
|
||||
signed = signJarFile(jar);
|
||||
pendingBlocks = moveBlockFirst(signed);
|
||||
invalid = invalidate(pendingBlocks);
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate signed file by modifying the contents of "a.txt"
|
||||
*/
|
||||
private static Path invalidate(Path s) throws Exception{
|
||||
Path invalid = Path.of("pending-block-file-invalidated.jar");
|
||||
|
||||
try (ZipFile zip = new ZipFile(s.toFile());
|
||||
ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(invalid))) {
|
||||
|
||||
for (ZipEntry ze : Collections.list(zip.entries())) {
|
||||
String name = ze.getName();
|
||||
out.putNextEntry(new ZipEntry(name));
|
||||
|
||||
if (name.equals("a.txt")) {
|
||||
// Change the contents of a.txt to trigger SignatureException
|
||||
out.write("b".getBytes(StandardCharsets.UTF_8));
|
||||
} else {
|
||||
try (InputStream in = zip.getInputStream(ze)) {
|
||||
in.transferTo(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return invalid;
|
||||
}
|
||||
|
||||
private static Path moveBlockFirst(Path s) throws Exception {
|
||||
Path b = Path.of("pending-block-file-blockfirst.jar");
|
||||
try (ZipFile in = new ZipFile(s.toFile());
|
||||
ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(b))) {
|
||||
|
||||
copy("META-INF/MANIFEST.MF", in, out);
|
||||
|
||||
// Switch the order of the RSA and SF files
|
||||
copy("META-INF/SIGNER.RSA", in, out);
|
||||
copy("META-INF/SIGNER.SF", in, out);
|
||||
|
||||
copy("a.txt", in, out);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an entry from a ZipFile to a ZipOutputStream
|
||||
*/
|
||||
private static void copy(String name, ZipFile in, ZipOutputStream out) throws Exception {
|
||||
out.putNextEntry(new ZipEntry(name));
|
||||
try (InputStream is = in.getInputStream(in.getEntry(name))) {
|
||||
is.transferTo(out);
|
||||
}
|
||||
}
|
||||
|
||||
private static Path signJarFile(Path j) throws Exception {
|
||||
Path s = Path.of("pending-block-file-signed.jar");
|
||||
|
||||
Files.deleteIfExists(Path.of("ks"));
|
||||
|
||||
sun.security.tools.keytool.Main.main(
|
||||
("-keystore ks -storepass changeit -keypass changeit -dname" +
|
||||
" CN=SIGNER" +" -alias r -genkeypair -keyalg rsa").split(" "));
|
||||
|
||||
char[] pass = "changeit".toCharArray();
|
||||
|
||||
KeyStore ks = KeyStore.getInstance(new File("ks"), pass);
|
||||
|
||||
KeyStore.PrivateKeyEntry pke = (KeyStore.PrivateKeyEntry)
|
||||
ks.getEntry("r", new KeyStore.PasswordProtection(pass));
|
||||
|
||||
JarSigner signer = new JarSigner.Builder(pke)
|
||||
.digestAlgorithm("SHA-256")
|
||||
.signatureAlgorithm("SHA256withRSA")
|
||||
.signerName("SIGNER")
|
||||
.build();
|
||||
|
||||
try (ZipFile in = new ZipFile(j.toFile());
|
||||
OutputStream out = Files.newOutputStream(s)) {
|
||||
signer.sign(in, out);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a jar file with single entry "a.txt" containing "a"
|
||||
*/
|
||||
private static Path createJarFile() throws Exception {
|
||||
Path jar = Path.of("pending-block-file.jar");
|
||||
Manifest manifest = new Manifest();
|
||||
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||
try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(jar),manifest)) {
|
||||
out.putNextEntry(new JarEntry("a.txt"));
|
||||
out.write("a".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
return jar;
|
||||
}
|
||||
}
|
||||
72
test/jdk/java/util/jar/JarFile/SorryClosed.java
Normal file
72
test/jdk/java/util/jar/JarFile/SorryClosed.java
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 4910572
|
||||
* @summary Accessing a closed jar file should generate IllegalStateException.
|
||||
* @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 {
|
||||
|
||||
private static final File file = new File(System.getProperty("test.src", "."), "test.jar");
|
||||
private static final String testEntryName = "test.class";
|
||||
|
||||
@Test
|
||||
void getInputStreamTest() throws IOException {
|
||||
JarFile f = new JarFile(file);
|
||||
ZipEntry e = f.getEntry(testEntryName);
|
||||
f.close();
|
||||
assertThrows(IllegalStateException.class, () -> f.getInputStream(e));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getEntryTest() throws IOException {
|
||||
JarFile f = new JarFile(file);
|
||||
f.close();
|
||||
assertThrows(IllegalStateException.class, () -> f.getEntry(testEntryName));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getJarEntryTest() throws IOException {
|
||||
JarFile f = new JarFile(file);
|
||||
f.close();
|
||||
assertThrows(IllegalStateException.class, () -> f.getJarEntry(testEntryName));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getManifestTest() throws IOException {
|
||||
JarFile f = new JarFile(file);
|
||||
f.close();
|
||||
assertThrows(IllegalStateException.class, f::getManifest);
|
||||
}
|
||||
}
|
||||
55
test/jdk/java/util/jar/JarFile/TurkCert.java
Normal file
55
test/jdk/java/util/jar/JarFile/TurkCert.java
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 4624534
|
||||
* @summary Make sure jar certificates work for Turkish locale
|
||||
* @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 {
|
||||
|
||||
@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) {
|
||||
}
|
||||
}
|
||||
assertNotNull(je.getCertificates(), "Null certificate for test.class.");
|
||||
}
|
||||
}
|
||||
}
|
||||
139
test/jdk/java/util/jar/JarFile/VerifySignedJar.java
Normal file
139
test/jdk/java/util/jar/JarFile/VerifySignedJar.java
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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
|
||||
* 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
|
||||
* @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;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
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 {
|
||||
|
||||
@Test
|
||||
void signedJarSecurityExceptionTest() throws Exception {
|
||||
Path j = createJar();
|
||||
Path s = signJar(j, keyEntry("cn=duke"));
|
||||
|
||||
try (JarFile jf = new JarFile(s.toFile())) {
|
||||
|
||||
for (JarEntry e: Collections.list(jf.entries())) {
|
||||
// Reading entry to trigger verification
|
||||
jf.getInputStream(e).transferTo(OutputStream.nullOutputStream());
|
||||
// Check that all regular files are signed by duke
|
||||
if (!e.getName().startsWith("META-INF/")) {
|
||||
checkSignedBy(e, "cn=duke");
|
||||
}
|
||||
}
|
||||
|
||||
// Read ZIP and JAR entries by name
|
||||
assertNotNull(jf.getEntry("getprop.class"));
|
||||
assertNotNull(jf.getJarEntry("getprop.class"));
|
||||
|
||||
// Make sure we throw NPE on null parameters
|
||||
assertThrows(NullPointerException.class, () -> jf.getEntry(null));
|
||||
assertThrows(NullPointerException.class, () -> jf.getJarEntry(null));
|
||||
assertThrows(NullPointerException.class, () -> jf.getInputStream(null));
|
||||
|
||||
} catch (SecurityException 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) {
|
||||
Certificate[] certs = e.getCertificates();
|
||||
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 IOException {
|
||||
Path j = Path.of("unsigned.jar");
|
||||
try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(j))){
|
||||
out.putNextEntry(new JarEntry("getprop.class"));
|
||||
out.write(new byte[] {(byte) 0XCA, (byte) 0XFE, (byte) 0XBA, (byte) 0XBE});
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
private static Path signJar(Path j, KeyStore.PrivateKeyEntry entry) throws Exception {
|
||||
Path s = Path.of("signed.jar");
|
||||
|
||||
JarSigner signer = new JarSigner.Builder(entry)
|
||||
.signerName("zigbert")
|
||||
.digestAlgorithm("SHA-256")
|
||||
.signatureAlgorithm("SHA256withRSA")
|
||||
.build();
|
||||
|
||||
try (ZipFile zip = new ZipFile(j.toFile());
|
||||
OutputStream out = Files.newOutputStream(s)) {
|
||||
signer.sign(zip, out);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private static KeyStore.PrivateKeyEntry keyEntry(String dname) throws Exception {
|
||||
|
||||
CertAndKeyGen gen = new CertAndKeyGen("RSA", "SHA256withRSA");
|
||||
|
||||
gen.generate(1048); // Small key size makes test run faster
|
||||
|
||||
var oneDay = TimeUnit.DAYS.toSeconds(1);
|
||||
Certificate cert = gen.getSelfCertificate(new X500Name(dname), oneDay);
|
||||
|
||||
return new KeyStore.PrivateKeyEntry(gen.getPrivateKey(),
|
||||
new Certificate[] {cert});
|
||||
}
|
||||
}
|
||||
BIN
test/jdk/java/util/jar/JarFile/bogus-signerinfo-attr.jar
Normal file
BIN
test/jdk/java/util/jar/JarFile/bogus-signerinfo-attr.jar
Normal file
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
public abstract class FooService { }
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 8234466
|
||||
* @summary attempt to trigger class loading from the classloader
|
||||
* during JAR file verification
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.compiler.CompilerUtils
|
||||
* jdk.test.lib.process.*
|
||||
* jdk.test.lib.util.JarUtils
|
||||
* jdk.test.lib.JDKToolLauncher
|
||||
* MultiThreadLoad FooService
|
||||
* @modules java.base/jdk.internal.access:+open
|
||||
* @run junit MultiProviderTest
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.test.lib.JDKToolFinder;
|
||||
import jdk.test.lib.JDKToolLauncher;
|
||||
import jdk.test.lib.Utils;
|
||||
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 TEST_CLASS_PATH = System.getProperty("test.classes", ".");
|
||||
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";
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void classLoadingTest(boolean sign) throws Throwable {
|
||||
signJars = sign;
|
||||
initialize();
|
||||
List<String> cmds = new ArrayList<>();
|
||||
cmds.add(JDKToolFinder.getJDKTool("java"));
|
||||
cmds.addAll(asList(Utils.getTestJavaOpts()));
|
||||
cmds.addAll(List.of(
|
||||
"-cp",
|
||||
COMBO_CP,
|
||||
"--add-opens",
|
||||
"java.base/jdk.internal.access=ALL-UNNAMED",
|
||||
"-Djava.util.logging.config.file=" +
|
||||
Path.of(System.getProperty("test.src", "."), "logging.properties").toString(),
|
||||
"MultiThreadLoad",
|
||||
TEST_CLASS_PATH));
|
||||
|
||||
assertDoesNotThrow(() -> {
|
||||
OutputAnalyzer outputAnalyzer = ProcessTools.executeCommand(cmds.stream()
|
||||
.filter(t -> !t.isEmpty())
|
||||
.toArray(String[]::new))
|
||||
.shouldHaveExitValue(0);
|
||||
System.out.println("Output:" + outputAnalyzer.getOutput());
|
||||
});
|
||||
}
|
||||
|
||||
public void initialize() throws Throwable {
|
||||
if (signJars) {
|
||||
genKey();
|
||||
}
|
||||
for (int i = 0; i < NUM_JARS; i++) {
|
||||
String p = "FooProvider" + i;
|
||||
String jarName = "FooProvider" + i + ".jar";
|
||||
Path javaPath = Path.of(p + ".java");
|
||||
Path jarPath = Path.of(p + ".jar");
|
||||
String contents = "public class FooProvider" + i + " extends FooService { }";
|
||||
Files.write(javaPath, contents.getBytes());
|
||||
CompilerUtils.compile(javaPath, Path.of(System.getProperty("test.classes")), "-cp", Utils.TEST_CLASS_PATH);
|
||||
createJar(jarPath, p, List.of(p));
|
||||
if (signJars) {
|
||||
signJar(TEST_CLASS_PATH + File.separator + jarName);
|
||||
}
|
||||
COMBO_CP += TEST_CLASS_PATH + File.separator + jarName + File.pathSeparator;
|
||||
}
|
||||
}
|
||||
|
||||
private static void createProviderConfig(Path config, String providerName) throws Exception {
|
||||
Files.createDirectories(config.getParent());
|
||||
Files.write(config, providerName.getBytes(), CREATE);
|
||||
}
|
||||
|
||||
private static void createJar(Path jar, String provider, List<String> files) throws Exception {
|
||||
Path xdir = Path.of(provider);
|
||||
createProviderConfig(xdir.resolve(METAINFO), provider);
|
||||
|
||||
for (String f : files) {
|
||||
Path source = Path.of(Utils.TEST_CLASSES, f + ".class");
|
||||
Path target = xdir.resolve(source.getFileName());
|
||||
Files.copy(source, target, REPLACE_EXISTING);
|
||||
}
|
||||
JarUtils.createJarFile(Path.of(TEST_CLASS_PATH, jar.getFileName().toString()), xdir);
|
||||
}
|
||||
|
||||
private static void genKey() throws Throwable {
|
||||
String keytool = JDKToolFinder.getJDKTool("keytool");
|
||||
Files.deleteIfExists(Paths.get(KEYSTORE));
|
||||
ProcessTools.executeCommand(keytool,
|
||||
"-J-Duser.language=en",
|
||||
"-J-Duser.country=US",
|
||||
"-genkey",
|
||||
"-keyalg", "rsa",
|
||||
"-alias", ALIAS,
|
||||
"-keystore", KEYSTORE,
|
||||
"-keypass", KEYPASS,
|
||||
"-dname", "cn=sample",
|
||||
"-storepass", STOREPASS
|
||||
).shouldHaveExitValue(0);
|
||||
}
|
||||
|
||||
|
||||
private static OutputAnalyzer signJar(String jarName) throws Throwable {
|
||||
List<String> args = new ArrayList<>();
|
||||
args.add("-verbose");
|
||||
args.add(jarName);
|
||||
args.add(ALIAS);
|
||||
|
||||
return jarsigner(args);
|
||||
}
|
||||
|
||||
private static OutputAnalyzer jarsigner(List<String> extra)
|
||||
throws Throwable {
|
||||
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jarsigner")
|
||||
.addVMArg("-Duser.language=en")
|
||||
.addVMArg("-Duser.country=US")
|
||||
.addToolArg("-keystore")
|
||||
.addToolArg(KEYSTORE)
|
||||
.addToolArg("-storepass")
|
||||
.addToolArg(STOREPASS)
|
||||
.addToolArg("-keypass")
|
||||
.addToolArg(KEYPASS);
|
||||
for (String s : extra) {
|
||||
if (s.startsWith("-J")) {
|
||||
launcher.addVMArg(s.substring(2));
|
||||
} else {
|
||||
launcher.addToolArg(s);
|
||||
}
|
||||
}
|
||||
return ProcessTools.executeCommand(launcher.getCommand());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import jdk.internal.access.JavaUtilJarAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public class MultiThreadLoad {
|
||||
|
||||
private static PrintStream out = System.err;
|
||||
static String TEST_CLASS_PATH;
|
||||
private static final JavaUtilJarAccess JUJA = SharedSecrets.javaUtilJarAccess();
|
||||
private static CountDownLatch cdl = new CountDownLatch(1);
|
||||
|
||||
private static <T> Set<T> setOf(Iterable<T> it) {
|
||||
Set<T> s = new HashSet<T>();
|
||||
for (T t : it)
|
||||
s.add(t);
|
||||
return s;
|
||||
}
|
||||
|
||||
private static <T> void checkEquals(Set<T> s1, Set<T> s2, boolean eq) {
|
||||
if (s1.equals(s2) != eq)
|
||||
throw new RuntimeException(String.format("%b %s : %s",
|
||||
eq, s1, s2));
|
||||
}
|
||||
|
||||
abstract static class TestLoader {
|
||||
String name;
|
||||
|
||||
TestLoader(String name) { this.name = name; }
|
||||
|
||||
abstract ServiceLoader<FooService> load();
|
||||
}
|
||||
|
||||
static TestLoader tcclLoader = new TestLoader("Thread context class loader") {
|
||||
ServiceLoader<FooService> load() {
|
||||
return ServiceLoader.load(FooService.class);
|
||||
}
|
||||
};
|
||||
|
||||
static TestLoader systemClLoader = new TestLoader("System class loader") {
|
||||
ServiceLoader<FooService> load() {
|
||||
return ServiceLoader.load(FooService.class, ClassLoader.getSystemClassLoader());
|
||||
}
|
||||
};
|
||||
|
||||
static TestLoader nullClLoader = new TestLoader("null (defer to system class loader)") {
|
||||
ServiceLoader<FooService> load() {
|
||||
return ServiceLoader.load(FooService.class, null);
|
||||
}
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
// keep reference to variables for the newly launced process
|
||||
TEST_CLASS_PATH = args[0];
|
||||
for (TestLoader tl : Arrays.asList(tcclLoader, systemClLoader, nullClLoader)) {
|
||||
test(tl);
|
||||
}
|
||||
}
|
||||
|
||||
static void test(TestLoader tl) {
|
||||
Runnable r1 = () -> {
|
||||
ServiceLoader<FooService> sl = tl.load();
|
||||
out.format("%s: %s%n", tl.name, sl);
|
||||
|
||||
// Providers are cached
|
||||
Set<FooService> ps = setOf(sl);
|
||||
cdl.countDown();
|
||||
checkEquals(ps, setOf(sl), true);
|
||||
|
||||
// The cache can be flushed and reloaded
|
||||
sl.reload();
|
||||
checkEquals(ps, setOf(sl), false);
|
||||
};
|
||||
|
||||
Runnable r2 = () -> {
|
||||
jarCrawler(Path.of(TEST_CLASS_PATH));
|
||||
};
|
||||
new Thread(r2).start();
|
||||
new Thread(r1).start();
|
||||
|
||||
}
|
||||
|
||||
private static void jarCrawler(Path p) {
|
||||
try {
|
||||
// let the other thread spin up
|
||||
cdl.await();
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
for (int i = MultiProviderTest.NUM_JARS -1; i >= 0; i--) {
|
||||
JUJA.ensureInitialization(new JarFile(TEST_CLASS_PATH + File.separator
|
||||
+ "FooProvider" + i + ".jar"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Exception during jar crawl: ");
|
||||
e.printStackTrace(System.out);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
############################################################
|
||||
# Configuration file for log testing
|
||||
#
|
||||
############################################################
|
||||
|
||||
handlers= java.util.logging.ConsoleHandler
|
||||
|
||||
.level= FINE
|
||||
|
||||
java.util.logging.ConsoleHandler.level = FINE
|
||||
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
|
||||
|
||||
jdk.event.security.level = FINE
|
||||
258
test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarAPI.java
Normal file
258
test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarAPI.java
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 8132734 8144062 8165723 8199172
|
||||
* @summary Test the extended API and the aliasing additions in JarFile that
|
||||
* support multi-release jar files
|
||||
* @library /lib/testlibrary/java/util/jar /test/lib
|
||||
* @build jdk.test.lib.RandomFactory
|
||||
* CreateMultiReleaseTestJars
|
||||
* jdk.test.lib.compiler.Compiler
|
||||
* jdk.test.lib.util.JarBuilder
|
||||
* @run junit MultiReleaseJarAPI
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
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.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();
|
||||
|
||||
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");
|
||||
|
||||
@BeforeAll
|
||||
public static void initialize() throws Exception {
|
||||
creator.compileEntries();
|
||||
creator.buildUnversionedJar();
|
||||
creator.buildMultiReleaseJar();
|
||||
creator.buildSignedMultiReleaseJar();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void close() throws IOException {
|
||||
Files.delete(unversioned.toPath());
|
||||
Files.delete(multirelease.toPath());
|
||||
Files.delete(signedmultirelease.toPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isMultiReleaseJar() throws Exception {
|
||||
try (JarFile jf = new JarFile(unversioned)) {
|
||||
assertFalse(jf.isMultiRelease());
|
||||
}
|
||||
|
||||
try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, Runtime.version())) {
|
||||
assertFalse(jf.isMultiRelease());
|
||||
}
|
||||
|
||||
try (JarFile jf = new JarFile(multirelease)) {
|
||||
assertTrue(jf.isMultiRelease());
|
||||
}
|
||||
|
||||
try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Runtime.version())) {
|
||||
assertTrue(jf.isMultiRelease());
|
||||
}
|
||||
|
||||
testCustomMultiReleaseValue("true", true);
|
||||
testCustomMultiReleaseValue("true\r\nOther: value", true);
|
||||
testCustomMultiReleaseValue("true\nOther: value", true);
|
||||
testCustomMultiReleaseValue("true\rOther: value", true);
|
||||
|
||||
testCustomMultiReleaseValue("false", false);
|
||||
testCustomMultiReleaseValue(" true", false);
|
||||
testCustomMultiReleaseValue("true ", false);
|
||||
testCustomMultiReleaseValue("true\n true", false);
|
||||
testCustomMultiReleaseValue("true\r true", false);
|
||||
testCustomMultiReleaseValue("true\r\n true", false);
|
||||
|
||||
// "Multi-Release: true/false" not in main attributes
|
||||
testCustomMultiReleaseValue("\r\n\r\nName: test\r\nMulti-Release: true\r\n",
|
||||
false);
|
||||
testCustomMultiReleaseValue("\n\nName: entryname\nMulti-Release: true\n",
|
||||
false);
|
||||
testCustomMultiReleaseValue("EndOfMainAttr: whatever\r\n" +
|
||||
"\r\nName: entryname\r\nMulti-Release: true\r\n",
|
||||
false);
|
||||
testCustomMultiReleaseValue("EndOfMainAttr: whatever\r\n" +
|
||||
"\nName: entryname\nMulti-Release: true\n",
|
||||
false);
|
||||
|
||||
// generate "random" Strings to use as extra attributes, and
|
||||
// verify that Multi-Release: true is always properly matched
|
||||
for (int i = 0; i < 100; i++) {
|
||||
byte[] keyBytes = new byte[RANDOM.nextInt(70) + 1];
|
||||
Arrays.fill(keyBytes, (byte)('a' + RANDOM.nextInt(24)));
|
||||
byte[] valueBytes = new byte[RANDOM.nextInt(70) + 1];
|
||||
Arrays.fill(valueBytes, (byte)('a' + RANDOM.nextInt(24)));
|
||||
|
||||
String key = new String(keyBytes, StandardCharsets.UTF_8);
|
||||
String value = new String(valueBytes, StandardCharsets.UTF_8);
|
||||
// test that Multi-Release: true anywhere in the manifest always
|
||||
// return true
|
||||
testCustomMultiReleaseValue("true", Map.of(key, value), true);
|
||||
|
||||
// test that we don't get any false positives
|
||||
testCustomMultiReleaseValue("false", Map.of(key, value), false);
|
||||
}
|
||||
}
|
||||
|
||||
private void testCustomMultiReleaseValue(String value, boolean expected)
|
||||
throws Exception {
|
||||
testCustomMultiReleaseValue(value, Map.of(), expected);
|
||||
}
|
||||
|
||||
private static final AtomicInteger JAR_COUNT = new AtomicInteger(0);
|
||||
|
||||
private void testCustomMultiReleaseValue(String value,
|
||||
Map<String, String> extraAttributes, boolean expected)
|
||||
throws Exception {
|
||||
String fileName = "custom-mr" + JAR_COUNT.incrementAndGet() + ".jar";
|
||||
creator.buildCustomMultiReleaseJar(fileName, value, extraAttributes);
|
||||
File custom = new File(userdir, fileName);
|
||||
try (JarFile jf = new JarFile(custom, true, ZipFile.OPEN_READ, Runtime.version())) {
|
||||
assertEquals(expected, jf.isMultiRelease());
|
||||
}
|
||||
Files.delete(custom.toPath());
|
||||
}
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
@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)) {
|
||||
assertEquals(base, jar.getVersion());
|
||||
}
|
||||
|
||||
System.err.println("test versioning for Release " + value);
|
||||
try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, value)) {
|
||||
assertEquals(expected, jf.getVersion());
|
||||
}
|
||||
|
||||
// regular, unversioned, jar
|
||||
try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, value)) {
|
||||
assertEquals(base, jf.getVersion());
|
||||
}
|
||||
}
|
||||
|
||||
@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));
|
||||
System.err.println("test aliasing for Release " + version);
|
||||
String prefix;
|
||||
if (JarFile.baseVersion().equals(value)) {
|
||||
prefix = "";
|
||||
} else {
|
||||
prefix = "META-INF/versions/" + value.major() + "/";
|
||||
}
|
||||
// test both multi-release jars
|
||||
readAndCompare(multirelease, value, "README", prefix + "README");
|
||||
readAndCompare(multirelease, value, "version/Version.class", prefix + "version/Version.class");
|
||||
// and signed multi-release jars
|
||||
readAndCompare(signedmultirelease, value, "README", prefix + "README");
|
||||
readAndCompare(signedmultirelease, value, "version/Version.class", prefix + "version/Version.class");
|
||||
}
|
||||
|
||||
private void readAndCompare(File jar, Runtime.Version version, String name, String realName) throws Exception {
|
||||
byte[] baseBytes;
|
||||
byte[] versionedBytes;
|
||||
try (JarFile jf = new JarFile(jar, true, ZipFile.OPEN_READ, JarFile.baseVersion())) {
|
||||
ZipEntry ze = jf.getEntry(realName);
|
||||
try (InputStream is = jf.getInputStream(ze)) {
|
||||
baseBytes = is.readAllBytes();
|
||||
}
|
||||
}
|
||||
assert baseBytes.length > 0;
|
||||
|
||||
try (JarFile jf = new JarFile(jar, true, ZipFile.OPEN_READ, version)) {
|
||||
ZipEntry ze = jf.getEntry(name);
|
||||
try (InputStream is = jf.getInputStream(ze)) {
|
||||
versionedBytes = is.readAllBytes();
|
||||
}
|
||||
}
|
||||
assert versionedBytes.length > 0;
|
||||
|
||||
assertTrue(Arrays.equals(baseBytes, versionedBytes));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNames() throws Exception {
|
||||
String rname = "version/Version.class";
|
||||
String vname = "META-INF/versions/9/version/Version.class";
|
||||
ZipEntry ze1;
|
||||
ZipEntry ze2;
|
||||
try (JarFile jf = new JarFile(multirelease)) {
|
||||
ze1 = jf.getEntry(vname);
|
||||
}
|
||||
assertEquals(vname, ze1.getName());
|
||||
try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Runtime.Version.parse("9"))) {
|
||||
ze2 = jf.getEntry(rname);
|
||||
}
|
||||
assertEquals(rname, ze2.getName());
|
||||
assertNotEquals(ze2.getName(), ze1.getName());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 8132734 8194070
|
||||
* @summary Test the System properties for JarFile that support multi-release jar files
|
||||
* @library /lib/testlibrary/java/util/jar /test/lib
|
||||
* @modules jdk.jartool
|
||||
* jdk.compiler
|
||||
* @build CreateMultiReleaseTestJars
|
||||
* jdk.test.lib.compiler.Compiler
|
||||
* jdk.test.lib.util.JarBuilder
|
||||
* @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;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
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.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
|
||||
|
||||
@BeforeAll
|
||||
public void initialize() throws Exception {
|
||||
server = SimpleFileServer.createFileServer(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0),
|
||||
Path.of(System.getProperty("user.dir", ".")), SimpleFileServer.OutputLevel.INFO);
|
||||
executor = Executors.newCachedThreadPool();
|
||||
server.setExecutor(executor);
|
||||
server.start();
|
||||
super.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeClassLoader() throws Exception {
|
||||
URL[] urls = new URL[]{
|
||||
URIBuilder.newBuilder().scheme("http").port(server.getAddress().getPort()).loopback()
|
||||
.path(TESTCONTEXT).toURL(),
|
||||
};
|
||||
cldr = new URLClassLoader(urls);
|
||||
// load any class, Main is convenient and in the root entries
|
||||
rootClass = cldr.loadClass("version.Main");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public void close() throws IOException {
|
||||
// Windows requires server to stop before file is deleted
|
||||
if (server != null) {
|
||||
server.stop(0);
|
||||
executor.shutdown();
|
||||
}
|
||||
super.close();
|
||||
}
|
||||
|
||||
/*
|
||||
* jdk.util.jar.enableMultiRelease=force is a no-op for URLClassLoader
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testURLClassLoader() throws Throwable {
|
||||
Class<?> vcls = cldr.loadClass("version.Version");
|
||||
invokeMethod(vcls, rtVersion);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceAsStream() throws Exception {
|
||||
String resource = rtVersion == 9 ? "/version/PackagePrivate.java" : "/version/Version.java";
|
||||
// use rootClass as a base for getting resources
|
||||
getResourceAsStream(rootClass, resource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResource() throws Exception {
|
||||
String resource = rtVersion == 9 ? "/version/PackagePrivate.java" : "/version/Version.java";
|
||||
// use rootClass as a base for getting resources
|
||||
getResource(rootClass, resource);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 8132734 8144062 8194070
|
||||
* @summary Test the System properties for JarFile that support multi-release jar files
|
||||
* @library /lib/testlibrary/java/util/jar /test/lib/
|
||||
* @build CreateMultiReleaseTestJars
|
||||
* jdk.test.lib.compiler.Compiler
|
||||
* jdk.test.lib.util.JarBuilder
|
||||
* @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;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
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();
|
||||
|
||||
final static String userdir = System.getProperty("user.dir", ".");
|
||||
final static File multirelease = new File(userdir, "multi-release.jar");
|
||||
protected int rtVersion;
|
||||
boolean force;
|
||||
protected ClassLoader cldr;
|
||||
protected Class<?> rootClass;
|
||||
|
||||
@BeforeAll
|
||||
public void initialize() throws Exception {
|
||||
CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
|
||||
creator.compileEntries();
|
||||
creator.buildMultiReleaseJar();
|
||||
int RUNTIME_VERSION = Runtime.version().major();
|
||||
rtVersion = Integer.getInteger("jdk.util.jar.version", RUNTIME_VERSION);
|
||||
String mrprop = System.getProperty("jdk.util.jar.enableMultiRelease", "");
|
||||
if (mrprop.equals("false")) {
|
||||
rtVersion = BASE_VERSION;
|
||||
} else if (rtVersion < BASE_VERSION) {
|
||||
rtVersion = BASE_VERSION;
|
||||
} else if (rtVersion > RUNTIME_VERSION) {
|
||||
rtVersion = RUNTIME_VERSION;
|
||||
}
|
||||
force = mrprop.equals("force");
|
||||
|
||||
initializeClassLoader();
|
||||
}
|
||||
|
||||
protected void initializeClassLoader() throws Exception {
|
||||
URL[] urls = new URL[]{multirelease.toURI().toURL()};
|
||||
cldr = new URLClassLoader(urls);
|
||||
// load any class, Main is convenient and in the root entries
|
||||
rootClass = cldr.loadClass("version.Main");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public void close() throws IOException {
|
||||
((URLClassLoader) cldr).close();
|
||||
Files.delete(multirelease.toPath());
|
||||
}
|
||||
|
||||
/*
|
||||
* jdk.util.jar.enableMultiRelease=force is a no-op for URLClassLoader
|
||||
*/
|
||||
@Test
|
||||
public void testURLClassLoader() throws Throwable {
|
||||
Class<?> vcls = cldr.loadClass("version.Version");
|
||||
invokeMethod(vcls, rtVersion);
|
||||
}
|
||||
|
||||
protected void invokeMethod(Class<?> vcls, int expected) throws Throwable {
|
||||
MethodType mt = MethodType.methodType(int.class);
|
||||
MethodHandle mh = MethodHandles.lookup().findVirtual(vcls, "getVersion", mt);
|
||||
assertEquals(expected, (int) mh.invoke(vcls.newInstance()));
|
||||
}
|
||||
|
||||
/*
|
||||
* jdk.util.jar.enableMultiRelease=force should affect a custom class loader
|
||||
*/
|
||||
@Test
|
||||
public void testClassLoader() throws Throwable {
|
||||
try (JarFile jf = new JarFile(multirelease)) { // do not set runtime versioning
|
||||
ClassLoader cldr = new CustomClassLoader(jf);
|
||||
Class<?> vcls = cldr.loadClass("version.Version");
|
||||
if (rtVersion == 9) {
|
||||
try {
|
||||
cldr.loadClass("version.PackagePrivate");
|
||||
} catch (ClassNotFoundException x) {
|
||||
if (force) throw x;
|
||||
}
|
||||
}
|
||||
invokeMethod(vcls, force ? rtVersion : BASE_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CustomClassLoader extends ClassLoader {
|
||||
private final JarFile jf;
|
||||
|
||||
CustomClassLoader(JarFile jf) throws Exception {
|
||||
super(null);
|
||||
this.jf = jf;
|
||||
}
|
||||
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
try {
|
||||
byte[] b;
|
||||
String entryName = name.replace(".", "/") + ".class";
|
||||
JarEntry je = jf.getJarEntry(entryName);
|
||||
if (je != null) {
|
||||
try (InputStream is = jf.getInputStream(je)) {
|
||||
b = new byte[(int) je.getSize()];
|
||||
is.read(b);
|
||||
}
|
||||
return defineClass(name, b, 0, b.length);
|
||||
}
|
||||
throw new ClassNotFoundException(name);
|
||||
} catch (IOException x) {
|
||||
throw new ClassNotFoundException(x.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceAsStream() throws Exception {
|
||||
String resource = rtVersion == 9 ? "/version/PackagePrivate.java" : "/version/Version.java";
|
||||
// use fileRootClass as a base for getting resources
|
||||
getResourceAsStream(rootClass, resource);
|
||||
}
|
||||
|
||||
protected void getResourceAsStream(Class<?> rootClass, String resource) throws Exception {
|
||||
try (InputStream is = rootClass.getResourceAsStream(resource)) {
|
||||
byte[] bytes = is.readAllBytes();
|
||||
resource = new String(bytes);
|
||||
}
|
||||
String match = "return " + rtVersion + ";";
|
||||
assertTrue(resource.contains(match));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResource() throws Exception {
|
||||
String resource = rtVersion == 9 ? "/version/PackagePrivate.java" : "/version/Version.java";
|
||||
// use rootClass as a base for getting resources
|
||||
getResource(rootClass, resource);
|
||||
}
|
||||
|
||||
protected void getResource(Class<?> rootClass, String resource) throws Exception {
|
||||
URL url = rootClass.getResource(resource);
|
||||
try (InputStream is = url.openStream()) {
|
||||
byte[] bytes = is.readAllBytes();
|
||||
resource = new String(bytes);
|
||||
}
|
||||
String match = "return " + rtVersion + ";";
|
||||
assertTrue(resource.contains(match));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 8132734 8144062
|
||||
* @summary Test potential security related issues
|
||||
* @library /lib/testlibrary/java/util/jar /test/lib/
|
||||
* @build CreateMultiReleaseTestJars
|
||||
* jdk.test.lib.compiler.Compiler
|
||||
* jdk.test.lib.util.JarBuilder
|
||||
* @run junit MultiReleaseJarSecurity
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.security.CodeSigner;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
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();
|
||||
|
||||
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");
|
||||
|
||||
@BeforeAll
|
||||
public static void initialize() throws Exception {
|
||||
CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
|
||||
creator.compileEntries();
|
||||
creator.buildMultiReleaseJar();
|
||||
creator.buildSignedMultiReleaseJar();
|
||||
}
|
||||
|
||||
@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(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"));
|
||||
assertArrayEquals(rcas.getCertificates(), vcas.getCertificates());
|
||||
assertArrayEquals(rcas.getCodeSigners(), vcas.getCodeSigners());
|
||||
}
|
||||
}
|
||||
|
||||
private static class CertsAndSigners {
|
||||
final private JarFile jf;
|
||||
final private JarEntry je;
|
||||
private boolean readComplete;
|
||||
|
||||
CertsAndSigners(JarFile jf, JarEntry je) {
|
||||
this.jf = jf;
|
||||
this.je = je;
|
||||
}
|
||||
|
||||
Certificate[] getCertificates() throws IOException {
|
||||
readEntry();
|
||||
return je.getCertificates();
|
||||
}
|
||||
|
||||
CodeSigner[] getCodeSigners() throws IOException {
|
||||
readEntry();
|
||||
return je.getCodeSigners();
|
||||
}
|
||||
|
||||
private void readEntry() throws IOException {
|
||||
if (!readComplete) {
|
||||
try (InputStream is = jf.getInputStream(je)) {
|
||||
is.readAllBytes();
|
||||
}
|
||||
readComplete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
test/jdk/java/util/jar/JarFile/mrjar/TEST.properties
Normal file
3
test/jdk/java/util/jar/JarFile/mrjar/TEST.properties
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
modules = \
|
||||
jdk.compiler \
|
||||
jdk.jartool
|
||||
244
test/jdk/java/util/jar/JarFile/mrjar/TestVersionedStream.java
Normal file
244
test/jdk/java/util/jar/JarFile/mrjar/TestVersionedStream.java
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 8163798 8189611 8211728
|
||||
* @summary basic tests for multi-release jar versioned streams
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.Platform
|
||||
* jdk.test.lib.util.FileUtils
|
||||
* @run junit TestVersionedStream
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.spi.ToolProvider;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import jdk.test.lib.util.FileUtils;
|
||||
|
||||
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 static final Path userdir;
|
||||
private static final Set<String> unversionedEntryNames;
|
||||
private static final int LATEST_VERSION = Runtime.version().feature();
|
||||
|
||||
static {
|
||||
userdir = Paths.get(System.getProperty("user.dir", "."));
|
||||
|
||||
// These are not real class files even though they end with .class.
|
||||
// They are resource files so jar tool validation won't reject them.
|
||||
// But they are what we want to test, especially q/Bar.class that
|
||||
// could be in a concealed package if this was a modular multi-release
|
||||
// jar.
|
||||
createFiles(
|
||||
"base/p/Bar.class",
|
||||
"base/p/Foo.class",
|
||||
"base/p/Main.class",
|
||||
"v9/p/Foo.class",
|
||||
"v10/p/Foo.class",
|
||||
"v10/q/Bar.class",
|
||||
"v" + LATEST_VERSION + "/p/Bar.class",
|
||||
"v" + LATEST_VERSION + "/p/Foo.class",
|
||||
"v" + LATEST_VERSION + "/META-INF/Foo.class"
|
||||
);
|
||||
|
||||
jar("cf mmr.jar -C base . " +
|
||||
"--release 9 -C v9 . " +
|
||||
"--release 10 -C v10 . " +
|
||||
"--release " + LATEST_VERSION + " -C v" + LATEST_VERSION + " .");
|
||||
|
||||
System.out.println("Contents of mmr.jar\n=======");
|
||||
try (JarFile jf = new JarFile("mmr.jar")) {
|
||||
unversionedEntryNames = jf.stream()
|
||||
.map(je -> je.getName())
|
||||
.peek(System.out::println)
|
||||
.map(nm -> nm.startsWith("META-INF/versions/")
|
||||
? 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("=======");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void close() throws IOException {
|
||||
Files.walk(userdir, 1)
|
||||
.filter(p -> !p.equals(userdir))
|
||||
.forEach(p -> {
|
||||
try {
|
||||
if (Files.isDirectory(p)) {
|
||||
FileUtils.deleteFileTreeWithRetry(p);
|
||||
} else {
|
||||
FileUtils.deleteFileIfExistsWithRetry(p);
|
||||
}
|
||||
} catch (IOException x) {
|
||||
throw new UncheckedIOException(x);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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()
|
||||
);
|
||||
}
|
||||
|
||||
@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())
|
||||
{
|
||||
assertNotNull(jes);
|
||||
|
||||
// put versioned entries in list so we can reuse them
|
||||
List<JarEntry> versionedEntries = jes.collect(Collectors.toList());
|
||||
|
||||
assertTrue(versionedEntries.size() > 0);
|
||||
|
||||
// also keep the names
|
||||
List<String> versionedNames = versionedEntries.stream()
|
||||
.map(JarEntry::getName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// verify the correct order while building enames
|
||||
List<String> unversionedOrder = new ArrayList<>(unversionedEntryNames);
|
||||
unversionedOrder.retainAll(versionedNames);
|
||||
|
||||
assertIterableEquals(unversionedOrder, versionedNames,
|
||||
"versioned entries not in same order as unversioned entries");
|
||||
|
||||
// verify the contents:
|
||||
// value.[0] end of the path
|
||||
// value.[1] versioned path/real name
|
||||
Map<String,String[]> expected = new HashMap<>();
|
||||
|
||||
expected.put("p/Bar.class", new String[] { "base/p/Bar.class", "p/Bar.class" });
|
||||
expected.put("p/Main.class", new String[] { "base/p/Main.class", "p/Main.class" });
|
||||
int majorVersion = version.major();
|
||||
switch (majorVersion) {
|
||||
case 8:
|
||||
expected.put("p/Foo.class", new String[]
|
||||
{ "base/p/Foo.class", "p/Foo.class" });
|
||||
break;
|
||||
case 9:
|
||||
expected.put("p/Foo.class", new String[]
|
||||
{ "v9/p/Foo.class", "META-INF/versions/9/p/Foo.class" });
|
||||
break;
|
||||
case 10:
|
||||
expected.put("p/Foo.class", new String[]
|
||||
{ "v10/p/Foo.class", "META-INF/versions/10/p/Foo.class" });
|
||||
|
||||
expected.put("q/Bar.class", new String[]
|
||||
{ "v10/q/Bar.class", "META-INF/versions/10/q/Bar.class" });
|
||||
break;
|
||||
default:
|
||||
if (majorVersion == LATEST_VERSION) {
|
||||
expected.put("p/Bar.class",
|
||||
new String[] { "v" + LATEST_VERSION + "/p/Bar.class",
|
||||
"META-INF/versions/" + LATEST_VERSION + "/p/Bar.class"});
|
||||
expected.put("p/Foo.class",
|
||||
new String[]{ "v" + LATEST_VERSION + "/p/Foo.class",
|
||||
"META-INF/versions/" + LATEST_VERSION + "/p/Foo.class"});
|
||||
expected.put("q/Bar.class",
|
||||
new String[] { "q/Bar.class", "META-INF/versions/10/q/Bar.class"});
|
||||
} else {
|
||||
fail("Test out of date, please add more cases");
|
||||
}
|
||||
}
|
||||
|
||||
expected.entrySet().stream().forEach(e -> {
|
||||
String name = e.getKey();
|
||||
int i = versionedNames.indexOf(name);
|
||||
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
|
||||
assertTrue(s.endsWith(e.getValue()[0]), s);
|
||||
// getRealName()
|
||||
assertTrue(je.getRealName().equals(e.getValue()[1]));
|
||||
} catch (IOException x) {
|
||||
throw new UncheckedIOException(x);
|
||||
}
|
||||
});
|
||||
|
||||
if (!unversionedEntryNames.contains("META-INF/Foo.class") ||
|
||||
versionedNames.indexOf("META-INF/Foo.class") != -1) {
|
||||
fail("versioned META-INF/Foo.class test failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void createFiles(String... files) {
|
||||
ArrayList<String> list = new ArrayList();
|
||||
Arrays.stream(files)
|
||||
.map(f -> Paths.get(userdir.toAbsolutePath().toString(), f))
|
||||
.forEach(p -> {
|
||||
try {
|
||||
Files.createDirectories(p.getParent());
|
||||
Files.createFile(p);
|
||||
list.clear();
|
||||
list.add(p.toString().replace(File.separatorChar, '/'));
|
||||
Files.write(p, list);
|
||||
} catch (IOException x) {
|
||||
throw new UncheckedIOException(x);
|
||||
}});
|
||||
}
|
||||
|
||||
private static void jar(String args) {
|
||||
ToolProvider jar = ToolProvider.findFirst("jar").orElseThrow();
|
||||
jar.run(System.out, System.err, args.split(" +"));
|
||||
}
|
||||
}
|
||||
BIN
test/jdk/java/util/jar/JarFile/no-manifest.jar
Normal file
BIN
test/jdk/java/util/jar/JarFile/no-manifest.jar
Normal file
Binary file not shown.
BIN
test/jdk/java/util/jar/JarFile/test.jar
Normal file
BIN
test/jdk/java/util/jar/JarFile/test.jar
Normal file
Binary file not shown.
BIN
test/jdk/java/util/jar/JarInputStream/BadSignedJar.jar
Normal file
BIN
test/jdk/java/util/jar/JarInputStream/BadSignedJar.jar
Normal file
Binary file not shown.
47
test/jdk/java/util/jar/JarInputStream/EmptyJar.java
Normal file
47
test/jdk/java/util/jar/JarInputStream/EmptyJar.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 4166469
|
||||
@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 {
|
||||
|
||||
@Test
|
||||
void npeTest() {
|
||||
// Ensure no NPE is thrown
|
||||
assertDoesNotThrow(() -> new JarInputStream(new ByteArrayInputStream(new byte[0])),
|
||||
"unexpected NullPointerException");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 8021788
|
||||
* @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;
|
||||
|
||||
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"))) {
|
||||
zos.putNextEntry(new ZipEntry("META-INF/SUB/file"));
|
||||
zos.write(new byte[10]);
|
||||
zos.putNextEntry(new ZipEntry("x"));
|
||||
zos.write(new byte[10]);
|
||||
}
|
||||
|
||||
// Sign it
|
||||
new File("ks").delete();
|
||||
sun.security.tools.keytool.Main.main(
|
||||
("-keystore ks -storepass changeit -keypass changeit " +
|
||||
"-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"))) {
|
||||
JarEntry je;
|
||||
while ((je = jis.getNextJarEntry()) != null) {
|
||||
String name = je.toString();
|
||||
if (name.equals("META-INF/SUB/file") || name.equals("x")) {
|
||||
while (jis.read(new byte[1000]) >= 0);
|
||||
assertNotNull(je.getCertificates(), name + " not signed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
94
test/jdk/java/util/jar/JarInputStream/ScanSignedJar.java
Normal file
94
test/jdk/java/util/jar/JarInputStream/ScanSignedJar.java
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 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.
|
||||
*
|
||||
* NOTE: the original problem occurred only when entries were extracted using
|
||||
* the JarInputStream.getNextJarEntry method; it never occurred when
|
||||
* entries were extracted using the JarFile.entries method.
|
||||
*/
|
||||
public class ScanSignedJar {
|
||||
|
||||
private static final String JAR_LOCATION =
|
||||
"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);
|
||||
JarEntry entry;
|
||||
byte[] buffer = new byte[1024];
|
||||
|
||||
while ((entry = inStream.getNextJarEntry()) != null) {
|
||||
|
||||
// need to read the entry's data to see the certs.
|
||||
while(inStream.read(buffer) != -1)
|
||||
;
|
||||
|
||||
String name = entry.getName();
|
||||
long size = entry.getSize();
|
||||
Certificate[] certificates = entry.getCertificates();
|
||||
CodeSigner[] signers = entry.getCodeSigners();
|
||||
|
||||
if (signers == null && certificates == null) {
|
||||
System.out.println("[unsigned]\t" + name + "\t(" + size +
|
||||
" bytes)");
|
||||
if (name.equals("Count.class")) {
|
||||
fail("Count.class should be signed");
|
||||
}
|
||||
} else if (signers != null && certificates != null) {
|
||||
System.out.println("[" + signers.length +
|
||||
(signers.length == 1 ? " signer" : " signers") + "]\t" +
|
||||
name + "\t(" + size + " bytes)");
|
||||
} else {
|
||||
System.out.println("[*ERROR*]\t" + name + "\t(" + size +
|
||||
" bytes)");
|
||||
fail("Cannot determine whether the entry is " +
|
||||
"signed or unsigned (signers[] doesn't match certs[]).");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 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 {
|
||||
|
||||
@Test
|
||||
void securityExceptionTest() throws IOException {
|
||||
try (JarInputStream jis = new JarInputStream(
|
||||
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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/jdk/java/util/jar/JarInputStream/signed.jar
Normal file
BIN
test/jdk/java/util/jar/JarInputStream/signed.jar
Normal file
Binary file not shown.
305
test/jdk/java/util/jar/Manifest/CreateManifest.java
Normal file
305
test/jdk/java/util/jar/Manifest/CreateManifest.java
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 7148584
|
||||
* @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 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 {
|
||||
|
||||
@Test
|
||||
void boundaryTest() throws IOException {
|
||||
String jarFileName = "test.jar";
|
||||
String ManifestName = "MANIFEST.MF";
|
||||
|
||||
// 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);
|
||||
|
||||
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" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-testconsole-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-bmp-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-bmp-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-host-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-host-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-agent-patching-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-agent-patching-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-connector-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-connector-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-discovery-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-gccompliance-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-mos-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-mos-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-security-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-security-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-topology-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-topology-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-event-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-event-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-event-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-mext-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-mext-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-discovery-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-discovery-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ecm-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ecm-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ecm-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-event-console-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-event-console-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-event-rules-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-event-rules-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-gccompliance-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ip-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ip-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-probanalysis-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-probanalysis-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-swlib-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-installmediacomponent-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-uifwk-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-uifwk-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-discovery-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-gccompliance-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-bmp-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-host-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-agent-patching-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-connector-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-mos-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-event-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-discovery-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-gccompliance-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ip-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-probanalysis-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-testconsole-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-uifwk-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-mext-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-security-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-agentpush-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-agentpush-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-agentpush-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-selfupdate-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-selfupdate-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-selfupdate-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-agentpush-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-groups-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-groups-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-groups-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-topology-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-jobs-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-jobs-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-jobs-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-templ-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-templ-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-templ-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-metricalertserrors-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-metricalertserrors-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-metricalertserrors-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-metrics-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-metrics-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-metrics-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-tc-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-tc-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-tc-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-agentmgmt-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-agentmgmt-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-agentmgmt-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-gcharvester-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-gcharvester-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-gcharvester-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-patching-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-patching-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-patching-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ohinv-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ohinv-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ohinv-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ohagent-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ohcoherence-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ohjrockit-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-extensibility-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-mpcustom-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-selfmonitor-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ocheck-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-udmmig-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-multioms-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-postupgrade-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-postupgrade-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-postupgrade-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ppc-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ppc-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ppc-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ppc-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ppc-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-mextjmx-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-mextjmx-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-mextjmx-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ocheck-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-services-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-services-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-services-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-eventmobile-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-uifwkmobile-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-logmgmt-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-omsproperties-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-ohel-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-agentupgrade-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-lm-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-lm-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-core-lm-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-regiontest-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-uipatterns-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-uipatterns-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-uipatterns-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-uielements-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-uielements-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-sandbox-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-sandbox-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-sdkcore-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-sdkcore-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-sdkcore-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-core-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-core-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-samples-core-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-adfext-bc-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-aslm-services-public-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-avail-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-charge-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-config-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-connect-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-db-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-discovery-public-entity.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-discovery-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-event-console-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-event-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-event-rules-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-extens-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-filebrowser-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-filebrowser-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-gccompliance-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-gccompliance-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-gccompliance-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-ip-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-job-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-me-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-metric-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-ecm-public-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-ecm-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-ecm-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-ecm-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-paf-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-security-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-swlib-public-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-swlib-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-templ-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-uifwk-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-uifwk-public-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-uifwk-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-bmp-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-bmp-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-bmp-public-entity.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-agent-patching-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-agent-patching-public-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-mext-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-mext-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-mext-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-testconsole-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-testconsole-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-testconsole-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-mos-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-mos-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-mos-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-topology-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-topology-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-regions-uimodel.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-regions-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-event-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-uifwk-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-adfext-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-agentpatching-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-avail-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-bmp-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-charge-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-config-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-connect-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-db-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-discovery-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-ecm-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-extens-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-gccompliance-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-ip-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-job-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-me-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-metric-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-paf-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-regions-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-security-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-swlib-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-templ-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-groups-public-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-groups-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-topology-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-resources-public-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-clonecomponents-public-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-clonecomponents-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-clonecomponents-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-clonecomponents-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-patching-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-patching-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-ohinv-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-ohinv-test.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-ppc-public-pojo.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-ppc-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-agentpush-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-uifwkmobile-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-lm-public-model.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-lm-public-ui.jar \n" +
|
||||
" /ade/dtsao_re/oracle/emcore//lib/em-sdkcore-lm-test.jar \n";
|
||||
}
|
||||
99
test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java
Normal file
99
test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
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 org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static sun.security.util.SecurityProperties.INCLUDE_JAR_NAME_IN_EXCEPTIONS;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8216362
|
||||
* @summary Verify that the property jdk.includeInExceptions works as expected
|
||||
* when an error occurs while reading an invalid Manifest file.
|
||||
* @modules java.base/sun.security.util
|
||||
* @run junit/othervm -Djdk.includeInExceptions=jar IncludeInExceptionsTest
|
||||
* @run junit/othervm -Djdk.includeInExceptions= IncludeInExceptionsTest
|
||||
* @run junit/othervm IncludeInExceptionsTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @see Manifest#Manifest(JarVerifier,InputStream,String)
|
||||
* @see Manifest#getErrorPosition
|
||||
*/
|
||||
public class IncludeInExceptionsTest {
|
||||
|
||||
static final String FILENAME = "Unique-Filename-Expected-In_Msg.jar";
|
||||
|
||||
static final byte[] INVALID_MANIFEST = (
|
||||
"Manifest-Version: 1.0\r\n" +
|
||||
"\r\n" +
|
||||
"Illegal\r\n" +
|
||||
"\r\n").getBytes(UTF_8);
|
||||
|
||||
static String createJarInvalidManifest(String jar) throws IOException {
|
||||
try (OutputStream out = Files.newOutputStream(Paths.get(jar));
|
||||
JarOutputStream jos = new JarOutputStream(out)) {
|
||||
JarEntry je = new JarEntry(JarFile.MANIFEST_NAME);
|
||||
jos.putNextEntry(je);
|
||||
jos.write(INVALID_MANIFEST);
|
||||
jos.closeEntry();
|
||||
}
|
||||
return jar;
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("manifests")
|
||||
void testInvalidManifest(Callable<?> attempt) {
|
||||
var ioException = assertThrows(IOException.class, attempt::call);
|
||||
boolean foundFileName = ioException.getMessage().contains(FILENAME);
|
||||
if (INCLUDE_JAR_NAME_IN_EXCEPTIONS && !foundFileName) {
|
||||
fail("JAR file name expected but not found in error message");
|
||||
} else if (foundFileName && !INCLUDE_JAR_NAME_IN_EXCEPTIONS) {
|
||||
fail("JAR file name found but should not be in error message");
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
278
test/jdk/java/util/jar/Manifest/LineBreakLineWidth.java
Normal file
278
test/jdk/java/util/jar/Manifest/LineBreakLineWidth.java
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Attributes.Name;
|
||||
|
||||
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 junit LineBreakLineWidth
|
||||
* @summary write valid manifests with respect to line breaks
|
||||
* and read any line width
|
||||
*/
|
||||
public class LineBreakLineWidth {
|
||||
|
||||
/**
|
||||
* maximum header name length from {@link Name#isValid(String)}
|
||||
* not including the name-value delimiter <code>": "</code>
|
||||
*/
|
||||
final static int MAX_HEADER_NAME_LENGTH = 70;
|
||||
|
||||
/**
|
||||
* range of one..{@link #TEST_WIDTH_RANGE} covered in this test that
|
||||
* exceeds the range of allowed header name lengths or line widths
|
||||
* in order to also cover invalid cases beyond the valid boundaries
|
||||
* and to keep it somewhat independent from the actual manifest width.
|
||||
* <p>
|
||||
* bigger than 72 (maximum manifest header line with in bytes (not utf-8
|
||||
* encoded characters) but otherwise arbitrarily chosen
|
||||
*/
|
||||
final static int TEST_WIDTH_RANGE = 123;
|
||||
|
||||
/**
|
||||
* tests if only valid manifest files can written depending on the header
|
||||
* name length or that an exception occurs already on the attempt to write
|
||||
* an invalid one otherwise and that no invalid manifest can be written.
|
||||
* <p>
|
||||
* due to bug JDK-6372077 it was possible to write a manifest that could
|
||||
* not be read again. independent of the actual manifest line width, such
|
||||
* a situation should never happen, which is the subject of this test.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteValidManifestOrException() throws IOException {
|
||||
/*
|
||||
* multi-byte utf-8 characters cannot occur in header names,
|
||||
* only in values which are not subject of this test here.
|
||||
* hence, each character in a header name uses exactly one byte and
|
||||
* variable length utf-8 character encoding doesn't affect this test.
|
||||
*/
|
||||
|
||||
String name = "";
|
||||
for (int l = 1; l <= TEST_WIDTH_RANGE; l++) {
|
||||
name += "x";
|
||||
System.out.println("name = " + name + ", "
|
||||
+ "name.length = " + name.length());
|
||||
|
||||
if (l <= MAX_HEADER_NAME_LENGTH) {
|
||||
writeValidManifest(name, "somevalue");
|
||||
} else {
|
||||
writeInvalidManifestThrowsException(name, "somevalue");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void writeValidManifest(String name, String value)
|
||||
throws IOException {
|
||||
byte[] mfBytes = writeManifest(name, value);
|
||||
Manifest mf = new Manifest(new ByteArrayInputStream(mfBytes));
|
||||
assertMainAndSectionValues(mf, name, value);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* tests that manifest files can be read even if the line breaks are
|
||||
* placed in different positions than where the current JDK's
|
||||
* {@link Manifest#write(java.io.OutputStream)} would have put it provided
|
||||
* the manifest is valid otherwise.
|
||||
* <p>
|
||||
* the <a href="{@docRoot}/../specs/jar/jar.html#Notes_on_Manifest_and_Signature_Files">
|
||||
* "Notes on Manifest and Signature Files" in the "JAR File
|
||||
* Specification"</a> state that "no line may be longer than 72 bytes
|
||||
* (not characters), in its utf8-encoded form." but allows for earlier or
|
||||
* additional line breaks.
|
||||
* <p>
|
||||
* the most important purpose of this test case is probably to make sure
|
||||
* that manifest files broken at 70 bytes line width written with the
|
||||
* previous version of {@link Manifest} before this fix still work well.
|
||||
*/
|
||||
@Test
|
||||
public void testReadDifferentLineWidths() throws IOException {
|
||||
/*
|
||||
* uses only one-byte utf-8 encoded characters as values.
|
||||
* correctly breaking multi-byte utf-8 encoded characters
|
||||
* would be subject of another test if there was one such.
|
||||
*/
|
||||
|
||||
// w: line width
|
||||
// 6 minimum required for section names starting with "Name: "
|
||||
for (int w = 6; w <= TEST_WIDTH_RANGE; w++) {
|
||||
|
||||
// ln: header name length
|
||||
String name = "";
|
||||
// - 2 due to the delimiter ": " that has to fit on the same
|
||||
// line as the name
|
||||
for (int ln = 1; ln <= w - 2; ln++) {
|
||||
name += "x";
|
||||
|
||||
// lv: value length
|
||||
String value = "";
|
||||
for (int lv = 1; lv <= TEST_WIDTH_RANGE; lv++) {
|
||||
value += "y";
|
||||
}
|
||||
|
||||
System.out.println("lineWidth = " + w);
|
||||
System.out.println("name = " + name + ""
|
||||
+ ", name.length = " + name.length());
|
||||
System.out.println("value = " + value + ""
|
||||
+ ", value.length = " + value.length());
|
||||
|
||||
readSpecificLineWidthManifest(name, value, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void readSpecificLineWidthManifest(String name, String value,
|
||||
int lineWidth) throws IOException {
|
||||
/*
|
||||
* breaking header names is not allowed and hence cannot be reasonably
|
||||
* tested. it cannot easily be helped, that invalid manifest files
|
||||
* written by the previous Manifest version implementation are illegal
|
||||
* if the header name is 69 or 70 bytes and in that case the name/value
|
||||
* delimiter ": " was broken on a new line.
|
||||
*
|
||||
* changing the line width in {@link Manifest#println72(OutputStream, String)},
|
||||
* however, also affects at which positions values are broken across
|
||||
* lines (should always have affected values only and never header
|
||||
* names or the delimiter) which is tested here.
|
||||
*
|
||||
* ideally, any previous Manifest implementation would have been used
|
||||
* here to provide manifest files to test reading but these are no
|
||||
* longer available in this version's sources and there might as well
|
||||
* be other libraries writing manifests. Therefore, in order to be able
|
||||
* to test any manifest file considered valid with respect to line
|
||||
* breaks that could not possibly be produced with the current Manifest
|
||||
* implementation, this test provides its own manifests in serialized
|
||||
* form.
|
||||
*/
|
||||
String lineBrokenSectionName = breakLines(lineWidth, "Name: " + name);
|
||||
String lineBrokenNameAndValue = breakLines(lineWidth, name + ": " + value);
|
||||
|
||||
ByteArrayOutputStream mfBuf = new ByteArrayOutputStream();
|
||||
mfBuf.write("Manifest-Version: 1.0".getBytes(UTF_8));
|
||||
mfBuf.write("\r\n".getBytes(UTF_8));
|
||||
mfBuf.write(lineBrokenNameAndValue.getBytes(UTF_8));
|
||||
mfBuf.write("\r\n".getBytes(UTF_8));
|
||||
mfBuf.write("\r\n".getBytes(UTF_8));
|
||||
mfBuf.write(lineBrokenSectionName.getBytes(UTF_8));
|
||||
mfBuf.write("\r\n".getBytes(UTF_8));
|
||||
mfBuf.write(lineBrokenNameAndValue.getBytes(UTF_8));
|
||||
mfBuf.write("\r\n".getBytes(UTF_8));
|
||||
mfBuf.write("\r\n".getBytes(UTF_8));
|
||||
byte[] mfBytes = mfBuf.toByteArray();
|
||||
printManifest(mfBytes);
|
||||
|
||||
boolean nameValid = name.length() <= MAX_HEADER_NAME_LENGTH;
|
||||
|
||||
Manifest mf;
|
||||
try {
|
||||
mf = new Manifest(new ByteArrayInputStream(mfBytes));
|
||||
} catch (IOException e) {
|
||||
if (!nameValid &&
|
||||
e.getMessage().startsWith("invalid header field")) {
|
||||
// expected because the name is not valid
|
||||
return;
|
||||
}
|
||||
|
||||
throw new AssertionError(e.getMessage(), e);
|
||||
}
|
||||
|
||||
assertTrue(nameValid, "failed to detect invalid manifest");
|
||||
|
||||
assertMainAndSectionValues(mf, name, value);
|
||||
}
|
||||
|
||||
static String breakLines(int lineWidth, String nameAndValue) {
|
||||
String lineBrokenNameAndValue = "";
|
||||
int charsOnLastLine = 0;
|
||||
for (int i = 0; i < nameAndValue.length(); i++) {
|
||||
lineBrokenNameAndValue += nameAndValue.substring(i, i + 1);
|
||||
charsOnLastLine++;
|
||||
if (0 < i && i < nameAndValue.length() - 1
|
||||
&& charsOnLastLine == lineWidth) {
|
||||
lineBrokenNameAndValue += "\r\n ";
|
||||
charsOnLastLine = 1;
|
||||
}
|
||||
}
|
||||
return lineBrokenNameAndValue;
|
||||
}
|
||||
|
||||
static byte[] writeManifest(String name, String value) throws IOException {
|
||||
/*
|
||||
* writing manifest main headers is implemented separately from
|
||||
* writing named sections manifest headers:
|
||||
* - java.util.jar.Attributes.writeMain(DataOutputStream)
|
||||
* - java.util.jar.Attributes.write(DataOutputStream)
|
||||
* which is why this is also covered separately in this test by
|
||||
* always adding the same value twice, in the main attributes as
|
||||
* well as in a named section (using the header name also as the
|
||||
* section name).
|
||||
*/
|
||||
|
||||
Manifest mf = new Manifest();
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
mf.getMainAttributes().putValue(name, value);
|
||||
|
||||
Attributes section = new Attributes();
|
||||
section.putValue(name, value);
|
||||
mf.getEntries().put(name, section);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
mf.write(out);
|
||||
byte[] mfBytes = out.toByteArray();
|
||||
printManifest(mfBytes);
|
||||
return mfBytes;
|
||||
}
|
||||
|
||||
private static void printManifest(byte[] mfBytes) {
|
||||
final String sepLine = "----------------------------------------------"
|
||||
+ "---------------------|-|-|"; // |-positions: ---68-70-72
|
||||
System.out.println(sepLine);
|
||||
System.out.print(new String(mfBytes, UTF_8));
|
||||
System.out.println(sepLine);
|
||||
}
|
||||
|
||||
private static void assertMainAndSectionValues(Manifest mf, String name,
|
||||
String value) {
|
||||
String mainValue = mf.getMainAttributes().getValue(name);
|
||||
String sectionValue = mf.getAttributes(name).getValue(name);
|
||||
|
||||
assertEquals(mainValue, value, "value different in main section");
|
||||
assertEquals(sectionValue, value, "value different in named section");
|
||||
}
|
||||
|
||||
}
|
||||
208
test/jdk/java/util/jar/Manifest/ValueUtf8Coding.java
Normal file
208
test/jdk/java/util/jar/Manifest/ValueUtf8Coding.java
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
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 java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
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 junit ValueUtf8Coding
|
||||
* @summary Tests encoding and decoding manifest header values to and from
|
||||
* UTF-8 with the complete Unicode character set.
|
||||
*/ /*
|
||||
* see also "../tools/launcher/UnicodeTest.java" for manifest attributes
|
||||
* parsed during launch
|
||||
*/
|
||||
public class ValueUtf8Coding {
|
||||
|
||||
/**
|
||||
* Maximum number of bytes of UTF-8 encoded characters in one header value.
|
||||
* <p>
|
||||
* There are too many different Unicode code points (more than one million)
|
||||
* to fit all into one manifest value. The specifications state:
|
||||
* <q>Implementations should support 65535-byte (not character) header
|
||||
* values, and 65535 headers per file. They might run out of memory,
|
||||
* but there should not be hard-coded limits below these values.</q>
|
||||
*
|
||||
* @see <a
|
||||
* href="{@docRoot}/../specs/jar/jar.html#Notes_on_Manifest_and_Signature_Files">
|
||||
* Notes on Manifest and Signature Files</a>
|
||||
*/
|
||||
static final int SUPPORTED_VALUE_LENGTH = 65535;
|
||||
|
||||
/**
|
||||
* Returns {@code true} if {@code codePoint} is known not to be a supported
|
||||
* character in manifest header values. Explicitly forbidden in manifest
|
||||
* header values are according to a statement from the specifications:
|
||||
* <q>otherchar: any UTF-8 character except NUL, CR and LF</q>.
|
||||
* {@code NUL} ({@code 0x0}), however, works just fine and might have been
|
||||
* used and might still be.
|
||||
*
|
||||
* @see <a href="{@docRoot}/../specs/jar/jar.html#Section-Specification">
|
||||
* Jar File Specification</a>
|
||||
*/
|
||||
static boolean isUnsupportedManifestValueCharacter(int codePoint) {
|
||||
return codePoint == '\r' /* CR */ || codePoint == '\n' /* LF */;
|
||||
};
|
||||
|
||||
/**
|
||||
* Produces a list of strings with all Unicode characters except those
|
||||
* explicitly invalid in manifest header values.
|
||||
* Each string is filled with as many characters as fit into
|
||||
* {@link #SUPPORTED_VALUE_LENGTH} bytes with UTF-8 encoding except the
|
||||
* last string which contains the remaining characters. Each of those
|
||||
* strings becomes a header value the number of which 65535 should be
|
||||
* supported per file.
|
||||
*
|
||||
* @see <a
|
||||
* href="{@docRoot}/../specs/jar/jar.html#Notes_on_Manifest_and_Signature_Files">
|
||||
* Notes on Manifest and Signature Files</a>
|
||||
*/
|
||||
static List<String> produceValuesWithAllUnicodeCharacters() {
|
||||
ArrayList<String> values = new ArrayList<>();
|
||||
byte[] valueBuf = new byte[SUPPORTED_VALUE_LENGTH];
|
||||
int pos = 0;
|
||||
for (int codePoint = Character.MIN_CODE_POINT;
|
||||
codePoint <= Character.MAX_CODE_POINT; codePoint++) {
|
||||
if (isUnsupportedManifestValueCharacter(codePoint)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
byte[] charBuf = Character.toString(codePoint).getBytes(UTF_8);
|
||||
if (pos + charBuf.length > valueBuf.length) {
|
||||
values.add(new String(valueBuf, 0, pos, UTF_8));
|
||||
pos = 0;
|
||||
}
|
||||
System.arraycopy(charBuf, 0, valueBuf, pos, charBuf.length);
|
||||
pos += charBuf.length;
|
||||
}
|
||||
if (pos > 0) {
|
||||
values.add(new String(valueBuf, 0, pos, UTF_8));
|
||||
}
|
||||
// minimum number of headers supported is the same as the minimum size
|
||||
// of each header value in bytes
|
||||
assertTrue(values.size() <= SUPPORTED_VALUE_LENGTH);
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns simple, valid, short, and distinct manifest header names.
|
||||
* The returned name cannot collide with "{@code Manifest-Version}" because
|
||||
* the returned string does not contain "{@code -}".
|
||||
*/
|
||||
static Name azName(int seed) {
|
||||
StringBuffer name = new StringBuffer();
|
||||
do {
|
||||
name.insert(0, (char) (seed % 26 + (seed < 26 ? 'A' : 'a')));
|
||||
seed = seed / 26 - 1;
|
||||
} while (seed >= 0);
|
||||
return new Name(name.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes and reads a manifest with the complete Unicode character set.
|
||||
* The characters are grouped into manifest header values with about as
|
||||
* many bytes as allowed each, utilizing a single big manifest.
|
||||
* <p>
|
||||
* This test assumes that a manifest is encoded and decoded correctly if
|
||||
* writing and then reading it again results in a manifest with identical
|
||||
* values as the original. The test is not about other aspects of writing
|
||||
* and reading manifests than only that, given the fact and the way it
|
||||
* works for some characters such as the most widely and often used ones,
|
||||
* it also works for the complete Unicode character set just the same.
|
||||
* <p>
|
||||
* Only header values are tested. The set of allowed characters for header
|
||||
* names are much more limited and are a different topic entirely and most
|
||||
* simple ones are used here as necessary just to get valid and different
|
||||
* ones (see {@link #azName}).
|
||||
* <p>
|
||||
* Because the current implementation under test uses different portions
|
||||
* of code depending on where the value occurs to read or write, each
|
||||
* character is tested in each of the three positions:<ul>
|
||||
* <li>main attribute header,</li>
|
||||
* <li>named section name, and</li>
|
||||
* <li>named sections header values</li>
|
||||
* </ul>
|
||||
* Implementation of writing the main section headers in
|
||||
* {@link Attributes#writeMain(java.io.DataOutputStream)} differs from the
|
||||
* one writing named section headers in
|
||||
* {@link Attributes#write(java.io.DataOutputStream)} regarding the special
|
||||
* order of {@link Name#MANIFEST_VERSION} and
|
||||
* {@link Name#SIGNATURE_VERSION} and also
|
||||
* {@link Manifest#read(java.io.InputStream)} at least potentially reads
|
||||
* main sections differently than reading named sections names headers in
|
||||
* {@link Attributes#read(Manifest.FastInputStream, byte[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testCompleteUnicodeCharacterSet() throws IOException {
|
||||
Manifest mf = new Manifest();
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
|
||||
List<String> values = produceValuesWithAllUnicodeCharacters();
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
Name name = azName(i);
|
||||
String value = values.get(i);
|
||||
|
||||
mf.getMainAttributes().put(name, value);
|
||||
Attributes attributes = new Attributes();
|
||||
mf.getEntries().put(value, attributes);
|
||||
attributes.put(name, value);
|
||||
}
|
||||
|
||||
mf = writeAndRead(mf);
|
||||
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
String value = values.get(i);
|
||||
Name name = azName(i);
|
||||
|
||||
assertEquals(value, mf.getMainAttributes().getValue(name),
|
||||
"main attributes header value");
|
||||
Attributes attributes = mf.getAttributes(value);
|
||||
assertNotNull(attributes, "named section");
|
||||
assertEquals(value, attributes.getValue(name),
|
||||
"named section attributes value");
|
||||
}
|
||||
}
|
||||
|
||||
static Manifest writeAndRead(Manifest mf) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
mf.write(out);
|
||||
byte[] mfBytes = out.toByteArray();
|
||||
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(mfBytes);
|
||||
return new Manifest(in);
|
||||
}
|
||||
|
||||
}
|
||||
74
test/jdk/java/util/jar/Manifest/WriteBinaryStructure.java
Normal file
74
test/jdk/java/util/jar/Manifest/WriteBinaryStructure.java
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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.junit.jupiter.api.Test;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8066619
|
||||
* @summary Tests that jar manifests are written in a particular structure
|
||||
* @run junit WriteBinaryStructure
|
||||
*/
|
||||
public class WriteBinaryStructure {
|
||||
|
||||
@Test
|
||||
public void testMainAttributes() throws IOException {
|
||||
Manifest mf = new Manifest();
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
mf.getMainAttributes().put(new Name("Key"), "Value");
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
mf.write(buf);
|
||||
assertArrayEquals((
|
||||
"Manifest-Version: 1.0\r\n" +
|
||||
"Key: Value\r\n" +
|
||||
"\r\n").getBytes(UTF_8),
|
||||
buf.toByteArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndividualSection() throws IOException {
|
||||
Manifest mf = new Manifest();
|
||||
mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
|
||||
Attributes attributes = new Attributes();
|
||||
mf.getEntries().put("Individual-Section-Name", attributes);
|
||||
attributes.put(new Name("Key"), "Value");
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
mf.write(buf);
|
||||
assertArrayEquals((
|
||||
"Manifest-Version: 1.0\r\n" +
|
||||
"\r\n" +
|
||||
"Name: Individual-Section-Name\r\n" +
|
||||
"Key: Value\r\n" +
|
||||
"\r\n").getBytes(UTF_8),
|
||||
buf.toByteArray());
|
||||
}
|
||||
}
|
||||
144
test/jdk/java/util/jar/TestExtra.java
Normal file
144
test/jdk/java/util/jar/TestExtra.java
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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
|
||||
* 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. ZIP file specific.
|
||||
* @run junit TestExtra
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.jar.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
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;
|
||||
|
||||
// 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()
|
||||
};
|
||||
|
||||
// For naming entries in ZIP streams
|
||||
static int count = 1;
|
||||
|
||||
// Use byte arrays instead of files
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
// ZIP content written here.
|
||||
ZipOutputStream zos = 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);
|
||||
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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
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;
|
||||
}
|
||||
|
||||
ZipInputStream getInputStream() {
|
||||
return new ZipInputStream(
|
||||
new ByteArrayInputStream(baos.toByteArray()));
|
||||
}
|
||||
|
||||
ZipOutputStream getOutputStream(ByteArrayOutputStream baos) {
|
||||
return new ZipOutputStream(baos);
|
||||
}
|
||||
|
||||
ZipEntry getEntry() {
|
||||
return new ZipEntry("zip" + count++ + ".txt");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
224
test/jdk/java/util/jar/TestJarExtra.java
Normal file
224
test/jdk/java/util/jar/TestJarExtra.java
Normal 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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue