8245302: Upgrade LogRecord to support long thread ids and remove its usage of ThreadLocal
Java.util.logging.LogRecord has been updated to use long thread ids instead of int thread id Reviewed-by: alanb, dfuchs
This commit is contained in:
parent
f23c983a46
commit
e2072bba70
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 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
|
||||
@ -26,7 +26,6 @@
|
||||
package java.util.logging;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.io.*;
|
||||
import java.security.AccessController;
|
||||
@ -75,21 +74,6 @@ public class LogRecord implements java.io.Serializable {
|
||||
private static final AtomicLong globalSequenceNumber
|
||||
= new AtomicLong(0);
|
||||
|
||||
/**
|
||||
* The default value of threadID will be the current thread's
|
||||
* thread id, for ease of correlation, unless it is greater than
|
||||
* MIN_SEQUENTIAL_THREAD_ID, in which case we try harder to keep
|
||||
* our promise to keep threadIDs unique by avoiding collisions due
|
||||
* to 32-bit wraparound. Unfortunately, LogRecord.getThreadID()
|
||||
* returns int, while Thread.getId() returns long.
|
||||
*/
|
||||
private static final int MIN_SEQUENTIAL_THREAD_ID = Integer.MAX_VALUE / 2;
|
||||
|
||||
private static final AtomicInteger nextThreadId
|
||||
= new AtomicInteger(MIN_SEQUENTIAL_THREAD_ID);
|
||||
|
||||
private static final ThreadLocal<Integer> threadIds = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* Logging message level
|
||||
*/
|
||||
@ -120,6 +104,11 @@ public class LogRecord implements java.io.Serializable {
|
||||
*/
|
||||
private int threadID;
|
||||
|
||||
/**
|
||||
* long value of Thread ID for thread that issued logging call.
|
||||
*/
|
||||
private long longThreadID;
|
||||
|
||||
/**
|
||||
* The Throwable (if any) associated with log message
|
||||
*/
|
||||
@ -147,7 +136,10 @@ public class LogRecord implements java.io.Serializable {
|
||||
* @serialField sourceClassName String Class that issued logging call
|
||||
* @serialField sourceMethodName String Method that issued logging call
|
||||
* @serialField message String Non-localized raw message text
|
||||
* @serialField threadID int Thread ID for thread that issued logging call
|
||||
* @serialField threadID int this is deprecated and is available for backward compatibility.
|
||||
* Values may have been synthesized. If present, {@code longThreadID} represents
|
||||
* the actual thread id.
|
||||
* @serialField longThreadID long Thread ID for thread that issued logging call
|
||||
* @serialField millis long Truncated event time in milliseconds since 1970
|
||||
* - calculated as getInstant().toEpochMilli().
|
||||
* The event time instant can be reconstructed using
|
||||
@ -164,6 +156,7 @@ public class LogRecord implements java.io.Serializable {
|
||||
* @serialField resourceBundleName String Resource bundle name to localized
|
||||
* log message
|
||||
*/
|
||||
@Serial
|
||||
private static final ObjectStreamField[] serialPersistentFields =
|
||||
new ObjectStreamField[] {
|
||||
new ObjectStreamField("level", Level.class),
|
||||
@ -172,6 +165,7 @@ public class LogRecord implements java.io.Serializable {
|
||||
new ObjectStreamField("sourceMethodName", String.class),
|
||||
new ObjectStreamField("message", String.class),
|
||||
new ObjectStreamField("threadID", int.class),
|
||||
new ObjectStreamField("longThreadID", long.class),
|
||||
new ObjectStreamField("millis", long.class),
|
||||
new ObjectStreamField("nanoAdjustment", int.class),
|
||||
new ObjectStreamField("thrown", Throwable.class),
|
||||
@ -184,20 +178,22 @@ public class LogRecord implements java.io.Serializable {
|
||||
private transient ResourceBundle resourceBundle;
|
||||
|
||||
/**
|
||||
* Returns the default value for a new LogRecord's threadID.
|
||||
* Synthesizes a pseudo unique integer value from a long {@code id} value.
|
||||
* For backward compatibility with previous releases,the returned integer is
|
||||
* such that for any positive long less than or equals to {@code Integer.MAX_VALUE},
|
||||
* the returned integer is equal to the original value.
|
||||
* Otherwise - it is synthesized with a best effort hashing algorithm,
|
||||
* and the returned value is negative.
|
||||
* Calling this method multiple times with the same value always yields the same result.
|
||||
*
|
||||
* @return thread id
|
||||
*/
|
||||
private int defaultThreadID() {
|
||||
long tid = Thread.currentThread().getId();
|
||||
if (tid < MIN_SEQUENTIAL_THREAD_ID) {
|
||||
return (int) tid;
|
||||
} else {
|
||||
Integer id = threadIds.get();
|
||||
if (id == null) {
|
||||
id = nextThreadId.getAndIncrement();
|
||||
threadIds.set(id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
private int shortThreadID(long id) {
|
||||
if (id >= 0 && id <= Integer.MAX_VALUE)
|
||||
return (int) id;
|
||||
int hash = Long.hashCode(id);
|
||||
return hash < 0 ? hash : (-1 - hash);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -225,10 +221,13 @@ public class LogRecord implements java.io.Serializable {
|
||||
message = msg;
|
||||
// Assign a thread ID and a unique sequence number.
|
||||
sequenceNumber = globalSequenceNumber.getAndIncrement();
|
||||
threadID = defaultThreadID();
|
||||
long id = Thread.currentThread().getId();
|
||||
// threadID is deprecated and this value is synthesised for backward compatibility
|
||||
threadID = shortThreadID(id);
|
||||
longThreadID = id;
|
||||
instant = Instant.now();
|
||||
needToInferCaller = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source Logger's name.
|
||||
@ -447,8 +446,12 @@ public class LogRecord implements java.io.Serializable {
|
||||
* This is a thread identifier within the Java VM and may or
|
||||
* may not map to any operating system ID.
|
||||
*
|
||||
* @deprecated Values returned by this method may be synthesized,
|
||||
* and may not correspond to the actual {@linkplain Thread#getId() thread id},
|
||||
* use {@link #getLongThreadID()} instead.
|
||||
* @return thread ID
|
||||
*/
|
||||
@Deprecated(since = "16")
|
||||
public int getThreadID() {
|
||||
return threadID;
|
||||
}
|
||||
@ -456,9 +459,41 @@ public class LogRecord implements java.io.Serializable {
|
||||
/**
|
||||
* Set an identifier for the thread where the message originated.
|
||||
* @param threadID the thread ID
|
||||
*
|
||||
* @deprecated This method doesn't allow to pass a long {@linkplain Thread#getId() thread id},
|
||||
* use {@link #setLongThreadID(long)} instead.
|
||||
*/
|
||||
@Deprecated(since = "16")
|
||||
public void setThreadID(int threadID) {
|
||||
this.threadID = threadID;
|
||||
this.longThreadID = threadID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a thread identifier for the thread where message originated
|
||||
*
|
||||
* <p>
|
||||
* This is a thread identifier within the Java VM and may or
|
||||
* may not map to any operating system ID.
|
||||
*
|
||||
* @return thread ID
|
||||
* @since 16
|
||||
*/
|
||||
public long getLongThreadID() {
|
||||
return longThreadID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an identifier for the thread where the message originated.
|
||||
*
|
||||
* @param longThreadID the thread ID
|
||||
* @return this LogRecord
|
||||
* @since 16
|
||||
*/
|
||||
public LogRecord setLongThreadID(long longThreadID) {
|
||||
this.threadID = shortThreadID(longThreadID);
|
||||
this.longThreadID = longThreadID;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -552,6 +587,7 @@ public class LogRecord implements java.io.Serializable {
|
||||
this.thrown = thrown;
|
||||
}
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 5372048053134512534L;
|
||||
|
||||
/**
|
||||
@ -564,6 +600,7 @@ public class LogRecord implements java.io.Serializable {
|
||||
* a null String is written. Otherwise the output of Object.toString()
|
||||
* is written.
|
||||
*/
|
||||
@Serial
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
// We have to write serialized fields first.
|
||||
ObjectOutputStream.PutField pf = out.putFields();
|
||||
@ -573,6 +610,7 @@ public class LogRecord implements java.io.Serializable {
|
||||
pf.put("sourceMethodName", sourceMethodName);
|
||||
pf.put("message", message);
|
||||
pf.put("threadID", threadID);
|
||||
pf.put("longThreadID", longThreadID);
|
||||
pf.put("millis", instant.toEpochMilli());
|
||||
pf.put("nanoAdjustment", instant.getNano() % 1000_000);
|
||||
pf.put("thrown", thrown);
|
||||
@ -594,8 +632,25 @@ public class LogRecord implements java.io.Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the LogRecord from deserialized data.
|
||||
* <ul>
|
||||
* <li>If {@code longThreadID} is present in the serial form, its value
|
||||
* takes precedence over {@code threadID} and a value for {@code threadID}
|
||||
* is synthesized from it, such that for {@code longThreadID} values between
|
||||
* {@code 0} and {@code Integer.MAX_VALUE} inclusive, {@code longThreadID}
|
||||
* and {@code threadID} will have the same value. For values outside of this
|
||||
* range a negative synthesized value will be deterministically derived
|
||||
* from {@code longThreadID}.
|
||||
* <li>Otherwise, when only {@code threadID} is
|
||||
* present, {@code longThreadID} is initialized with the value of
|
||||
* {@code threadID} which may be anything between {@code Integer.MIN_VALUE}
|
||||
* and {Integer.MAX_VALUE}.
|
||||
* </ul>
|
||||
*/
|
||||
@Serial
|
||||
private void readObject(ObjectInputStream in)
|
||||
throws IOException, ClassNotFoundException {
|
||||
throws IOException, ClassNotFoundException {
|
||||
// We have to read serialized fields first.
|
||||
ObjectInputStream.GetField gf = in.readFields();
|
||||
level = (Level) gf.get("level", null);
|
||||
@ -603,7 +658,14 @@ public class LogRecord implements java.io.Serializable {
|
||||
sourceClassName = (String) gf.get("sourceClassName", null);
|
||||
sourceMethodName = (String) gf.get("sourceMethodName", null);
|
||||
message = (String) gf.get("message", null);
|
||||
threadID = gf.get("threadID", 0);
|
||||
// If longthreadID is not present, it will be initialised with threadID value
|
||||
// If longthreadID is present, threadID might have a synthesized value
|
||||
int threadID = gf.get("threadID", 0);
|
||||
long longThreadID = gf.get("longThreadID", (long)threadID);
|
||||
if (threadID != longThreadID)
|
||||
threadID = shortThreadID(longThreadID);
|
||||
this.threadID = threadID;
|
||||
this.longThreadID = longThreadID;
|
||||
long millis = gf.get("millis", 0L);
|
||||
int nanoOfMilli = gf.get("nanoAdjustment", 0);
|
||||
instant = Instant.ofEpochSecond(
|
||||
@ -641,9 +703,9 @@ public class LogRecord implements java.io.Serializable {
|
||||
// use system class loader to ensure the ResourceBundle
|
||||
// instance is a different instance than null loader uses
|
||||
final ResourceBundle bundle =
|
||||
ResourceBundle.getBundle(resourceBundleName,
|
||||
Locale.getDefault(),
|
||||
ClassLoader.getSystemClassLoader());
|
||||
ResourceBundle.getBundle(resourceBundleName,
|
||||
Locale.getDefault(),
|
||||
ClassLoader.getSystemClassLoader());
|
||||
resourceBundle = bundle;
|
||||
} catch (MissingResourceException ex) {
|
||||
// This is not a good place to throw an exception,
|
||||
@ -697,7 +759,7 @@ public class LogRecord implements java.io.Serializable {
|
||||
private static final StackWalker WALKER;
|
||||
static {
|
||||
final PrivilegedAction<StackWalker> action =
|
||||
() -> StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||
() -> StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||
WALKER = AccessController.doPrivileged(action);
|
||||
}
|
||||
|
||||
@ -736,7 +798,7 @@ public class LogRecord implements java.io.Serializable {
|
||||
|
||||
private boolean isLoggerImplFrame(String cname) {
|
||||
return (cname.equals("java.util.logging.Logger") ||
|
||||
cname.startsWith("sun.util.logging.PlatformLogger"));
|
||||
cname.startsWith("sun.util.logging.PlatformLogger"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 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
|
||||
@ -219,7 +219,7 @@ public class XMLFormatter extends Formatter {
|
||||
}
|
||||
|
||||
sb.append(" <thread>");
|
||||
sb.append(record.getThreadID());
|
||||
sb.append(record.getLongThreadID());
|
||||
sb.append("</thread>\n");
|
||||
|
||||
if (record.getMessage() != null) {
|
||||
|
86
test/jdk/java/util/logging/LogRecordThreadIdTest.java
Normal file
86
test/jdk/java/util/logging/LogRecordThreadIdTest.java
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8245302
|
||||
* @summary test the relationship between
|
||||
* thread id long and int methods
|
||||
* @build LogRecordThreadIdTest
|
||||
* @run testng/othervm LogRecordThreadIdTest
|
||||
*/
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotEquals;
|
||||
|
||||
|
||||
|
||||
public class LogRecordThreadIdTest {
|
||||
|
||||
LogRecord record, record1, record2;
|
||||
|
||||
@BeforeTest
|
||||
public void setUp() throws Exception {
|
||||
record = new LogRecord(Level.INFO, "record");
|
||||
record1 = new LogRecord(Level.INFO, "record1");
|
||||
record2 = new LogRecord(Level.INFO, "record2");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests threadID setter methods for consistency
|
||||
* with longThreadID
|
||||
*/
|
||||
@Test
|
||||
public void testSetThreadId() {
|
||||
record.setThreadID(Integer.MAX_VALUE - 20);
|
||||
record1.setThreadID(Integer.MAX_VALUE - 1);
|
||||
assertEquals(record.getLongThreadID(), Integer.MAX_VALUE - 20L);
|
||||
assertEquals(record.getThreadID(), Integer.MAX_VALUE - 20);
|
||||
assertEquals(record1.getThreadID(), Integer.MAX_VALUE - 1);
|
||||
assertEquals(record1.getLongThreadID(), Integer.MAX_VALUE - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests longThreadID methods for consistency
|
||||
* with threadID
|
||||
*/
|
||||
@Test
|
||||
public void testSetLongThreadId() {
|
||||
record.setLongThreadID(Integer.MAX_VALUE - 20L);
|
||||
record1.setLongThreadID(Integer.MAX_VALUE + 10L);
|
||||
record2.setLongThreadID(Integer.MAX_VALUE);
|
||||
assertEquals(record.getThreadID(), Integer.MAX_VALUE - 20);
|
||||
assertEquals(record.getLongThreadID(), Integer.MAX_VALUE - 20L);
|
||||
assertNotEquals(record1.getThreadID(), Integer.MAX_VALUE + 10L);
|
||||
assertEquals(record1.getLongThreadID(), Integer.MAX_VALUE + 10L);
|
||||
assertEquals(record2.getThreadID(), Integer.MAX_VALUE);
|
||||
assertEquals(record2.getLongThreadID(), Integer.MAX_VALUE);
|
||||
|
||||
}
|
||||
}
|
305
test/jdk/java/util/logging/SerializeLogRecordTest.java
Normal file
305
test/jdk/java/util/logging/SerializeLogRecordTest.java
Normal file
@ -0,0 +1,305 @@
|
||||
/*
|
||||
* 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 java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.Base64;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.XMLFormatter;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8245302
|
||||
* @summary tests the deprecation of threadID and a new field longThreadID,
|
||||
* test should be run on jdk16 and subsequent versions
|
||||
* @run main/othervm SerializeLogRecordTest
|
||||
*/
|
||||
public class SerializeLogRecordTest {
|
||||
|
||||
/**
|
||||
* Serializes a log record, encode the serialized bytes in base 64, and
|
||||
* prints pseudo java code that can be cut and pasted into this test.
|
||||
* @param record the log record to serialize, encode in base 64, and for
|
||||
* which test data will be generated.
|
||||
* @return A string containing the generated pseudo java code.
|
||||
* @throws IOException Unexpected.
|
||||
* @throws ClassNotFoundException Unexpected.
|
||||
*/
|
||||
public static String generate(LogRecord record) throws IOException, ClassNotFoundException {
|
||||
|
||||
XMLFormatter formatter = new XMLFormatter();
|
||||
String str = formatter.format(record);
|
||||
|
||||
// Serialize the given LogRecord
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
final ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||
oos.writeObject(record);
|
||||
oos.flush();
|
||||
oos.close();
|
||||
|
||||
// Now we're going to perform a number of smoke tests before
|
||||
// generating the Java pseudo code.
|
||||
//
|
||||
// First checks that the log record can be deserialized
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
final ObjectInputStream ois = new ObjectInputStream(bais);
|
||||
final LogRecord record2 = (LogRecord)ois.readObject();
|
||||
|
||||
String str2 = formatter.format(record2);
|
||||
|
||||
if (!str.equals(str2)) throw new RuntimeException("Unexpected values in deserialized object:"
|
||||
+ "\n\tExpected: " + str
|
||||
+ "\n\tRetrieved: "+str);
|
||||
|
||||
// Now get a Base64 string representation of the serialized bytes.
|
||||
final String base64 = Base64.getEncoder().encodeToString(baos.toByteArray());
|
||||
|
||||
// Check that we can deserialize a log record from the Base64 string
|
||||
// representation we just computed.
|
||||
final ByteArrayInputStream bais2 = new ByteArrayInputStream(Base64.getDecoder().decode(base64));
|
||||
final ObjectInputStream ois2 = new ObjectInputStream(bais2);
|
||||
final LogRecord record3 = (LogRecord)ois2.readObject();
|
||||
|
||||
// Format the new deserialized LogRecord using the SimpleFormatter, and
|
||||
// check that the string representation obtained matches the string
|
||||
// representation of the original LogRecord
|
||||
String str3 = formatter.format(record3);
|
||||
if (!str.equals(str3)) throw new RuntimeException("Unexpected values in deserialized object:"
|
||||
+ "\n\tExpected: " + str
|
||||
+ "\n\tRetrieved: "+str);
|
||||
|
||||
// Generates the Java Pseudo code that can be cut & pasted into
|
||||
// this test (see Jdk8SerializedLog and Jdk9SerializedLog below)
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(" /**").append('\n');
|
||||
sb.append(" * Base64 encoded string for LogRecord object.").append('\n');
|
||||
sb.append(" * Java version: ").append(System.getProperty("java.version")).append('\n');
|
||||
sb.append(" * threadID: ").append(record.getThreadID()).append('\n');
|
||||
sb.append(" * longThreadID: ").append(record.getLongThreadID()).append('\n');
|
||||
sb.append(" **/").append('\n');
|
||||
sb.append(" final String base64 = ").append("\n ");
|
||||
final int last = base64.length() - 1;
|
||||
for (int i=0; i<base64.length();i++) {
|
||||
if (i%64 == 0) sb.append("\"");
|
||||
sb.append(base64.charAt(i));
|
||||
if (i%64 == 63 || i == last) {
|
||||
sb.append("\"");
|
||||
if (i == last) sb.append(";\n");
|
||||
else sb.append("\n + ");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract class to test that a log record previously serialized on a
|
||||
* different java version can be deserialized in the current java version.
|
||||
* (see Jdk11SerializedLog and Jdk16SerializedLog below)
|
||||
*/
|
||||
public abstract static class SerializedLog {
|
||||
public abstract String getBase64();
|
||||
|
||||
/**
|
||||
* Deserializes the Base64 encoded string returned by {@link
|
||||
* #getBase64()}, and checks that the string representation obtained
|
||||
* matches the original string representation returned by
|
||||
*/
|
||||
protected void dotest() {
|
||||
try {
|
||||
final String base64 = getBase64();
|
||||
final ByteArrayInputStream bais =
|
||||
new ByteArrayInputStream(Base64.getDecoder().decode(base64));
|
||||
final ObjectInputStream ois = new ObjectInputStream(bais);
|
||||
final LogRecord record = (LogRecord)ois.readObject();
|
||||
check(record);
|
||||
System.out.println("PASSED: "+this.getClass().getName()+"\n");
|
||||
} catch (IOException | ClassNotFoundException x) {
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Check that the values of threadID and longThreadID
|
||||
* are correct and consistent when de-serialized from
|
||||
* earlier jdk versions(jdk11)
|
||||
*/
|
||||
protected void check(LogRecord r1) {
|
||||
XMLFormatter formatter = new XMLFormatter();
|
||||
int check = Integer.MAX_VALUE - 10;
|
||||
String str = formatter.format(r1);
|
||||
String loggerName = r1.getLoggerName();
|
||||
|
||||
if (loggerName.equals("test2")) {
|
||||
int id = -2147483639;
|
||||
if (r1.getLongThreadID() != Integer.MAX_VALUE + 10L || r1.getThreadID() != id) {
|
||||
throw new RuntimeException(this.getClass().getName());
|
||||
}
|
||||
else {
|
||||
System.out.println("Test Integer Check Passed");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (loggerName.equals("test1") || loggerName.equals("test")) {
|
||||
if (loggerName.equals("test1")) {
|
||||
check = Integer.MAX_VALUE / 2 - 20;
|
||||
}
|
||||
|
||||
int tid = r1.getThreadID();
|
||||
long longThreadID = r1.getLongThreadID();
|
||||
if (tid != check || longThreadID != check || !str.contains("<thread>" + String.valueOf(longThreadID))) {
|
||||
throw new RuntimeException(this.getClass().getName());
|
||||
}
|
||||
else {
|
||||
System.out.println("Test Integer Check Passed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void generate() {
|
||||
try {
|
||||
LogRecord record = new LogRecord(Level.INFO, "Java Version: {0}");
|
||||
record.setLoggerName("test");
|
||||
record.setThreadID(Integer.MAX_VALUE - 10);
|
||||
record.setParameters(new Object[] {System.getProperty("java.version")});
|
||||
System.out.println(generate(record));
|
||||
LogRecord record1 = new LogRecord(Level.INFO, "Java Version: {0}");
|
||||
record1.setLoggerName("test1");
|
||||
record1.setLongThreadID(Integer.MAX_VALUE/2 - 20);
|
||||
record1.setParameters(new Object[] {System.getProperty("java.version")});
|
||||
System.out.println(generate(record1));
|
||||
LogRecord record2 = new LogRecord(Level.INFO, "Java Version: {0}");
|
||||
record2.setLoggerName("test2");
|
||||
record2.setLongThreadID(Integer.MAX_VALUE + 10L);
|
||||
record2.setParameters(new Object[] {System.getProperty("java.version")});
|
||||
System.out.println(generate(record2));
|
||||
} catch (IOException | ClassNotFoundException x) {
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Jdk11SerializedLog extends SerializedLog {
|
||||
|
||||
/**
|
||||
* Base64 encoded string for LogRecord object.
|
||||
* It is generated using generate method and
|
||||
* can be copy pasted as base64 value, for generating
|
||||
* this value, execute this class with an argument value
|
||||
* generate and the test will print out the base64 value
|
||||
* that can be copy pasted below
|
||||
* Java version: 11.0.6
|
||||
**/
|
||||
final String base64 =
|
||||
"rO0ABXNyABtqYXZhLnV0aWwubG9nZ2luZy5Mb2dSZWNvcmRKjVk982lRlgMAC0oA"
|
||||
+ "Bm1pbGxpc0kADm5hbm9BZGp1c3RtZW50SgAOc2VxdWVuY2VOdW1iZXJJAAh0aHJl"
|
||||
+ "YWRJREwABWxldmVsdAAZTGphdmEvdXRpbC9sb2dnaW5nL0xldmVsO0wACmxvZ2dl"
|
||||
+ "ck5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztMAAdtZXNzYWdlcQB+AAJMABJyZXNv"
|
||||
+ "dXJjZUJ1bmRsZU5hbWVxAH4AAkwAD3NvdXJjZUNsYXNzTmFtZXEAfgACTAAQc291"
|
||||
+ "cmNlTWV0aG9kTmFtZXEAfgACTAAGdGhyb3dudAAVTGphdmEvbGFuZy9UaHJvd2Fi"
|
||||
+ "bGU7eHAAAAFycRovVgAFN/AAAAAAAAAAAH////VzcgAXamF2YS51dGlsLmxvZ2dp"
|
||||
+ "bmcuTGV2ZWyOiHETUXM2kgIAA0kABXZhbHVlTAAEbmFtZXEAfgACTAAScmVzb3Vy"
|
||||
+ "Y2VCdW5kbGVOYW1lcQB+AAJ4cAAAAyB0AARJTkZPdAAic3VuLnV0aWwubG9nZ2lu"
|
||||
+ "Zy5yZXNvdXJjZXMubG9nZ2luZ3QABHRlc3R0ABFKYXZhIFZlcnNpb246IHswfXBw"
|
||||
+ "cHB3BgEAAAAAAXQABjExLjAuNng=";
|
||||
|
||||
|
||||
@Override
|
||||
public String getBase64() {
|
||||
return base64;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void check(LogRecord r1) {
|
||||
super.check(r1);
|
||||
}
|
||||
|
||||
public static void test() {
|
||||
new Jdk11SerializedLog().dotest();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Jdk16SerializedLog extends SerializedLog {
|
||||
|
||||
/**
|
||||
* Base64 encoded string for LogRecord object.
|
||||
* It is generated using generate method and
|
||||
* can be copy pasted as base64 value, for generating
|
||||
* this value, execute this class with an argument value
|
||||
* generate and the test will print out the base64 value
|
||||
* that can be copy pasted below
|
||||
* Java version: 16-internal
|
||||
**/
|
||||
final String base64 =
|
||||
"rO0ABXNyABtqYXZhLnV0aWwubG9nZ2luZy5Mb2dSZWNvcmRKjVk982lRlgMADEoA"
|
||||
+ "DGxvbmdUaHJlYWRJREoABm1pbGxpc0kADm5hbm9BZGp1c3RtZW50SgAOc2VxdWVu"
|
||||
+ "Y2VOdW1iZXJJAAh0aHJlYWRJREwABWxldmVsdAAZTGphdmEvdXRpbC9sb2dnaW5n"
|
||||
+ "L0xldmVsO0wACmxvZ2dlck5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztMAAdtZXNz"
|
||||
+ "YWdlcQB+AAJMABJyZXNvdXJjZUJ1bmRsZU5hbWVxAH4AAkwAD3NvdXJjZUNsYXNz"
|
||||
+ "TmFtZXEAfgACTAAQc291cmNlTWV0aG9kTmFtZXEAfgACTAAGdGhyb3dudAAVTGph"
|
||||
+ "dmEvbGFuZy9UaHJvd2FibGU7eHAAAAAAgAAACQAAAXLMALDdAAS+2AAAAAAAAAAC"
|
||||
+ "gAAACXNyABdqYXZhLnV0aWwubG9nZ2luZy5MZXZlbI6IcRNRczaSAgADSQAFdmFs"
|
||||
+ "dWVMAARuYW1lcQB+AAJMABJyZXNvdXJjZUJ1bmRsZU5hbWVxAH4AAnhwAAADIHQA"
|
||||
+ "BElORk90ACJzdW4udXRpbC5sb2dnaW5nLnJlc291cmNlcy5sb2dnaW5ndAAFdGVz"
|
||||
+ "dDJ0ABFKYXZhIFZlcnNpb246IHswfXBwcHB3BgEAAAAAAXQACzE2LWludGVybmFs"
|
||||
+ "eA==";
|
||||
|
||||
|
||||
@Override
|
||||
public String getBase64() {
|
||||
return base64;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void check(LogRecord r1) {
|
||||
super.check(r1);
|
||||
}
|
||||
|
||||
public static void test() {
|
||||
new Jdk16SerializedLog().dotest();
|
||||
}
|
||||
}
|
||||
|
||||
static enum TestCase { GENERATE, TESTJDK11, TESTJDK16 };
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
|
||||
// If no args, then run everything....
|
||||
if (args == null || args.length == 0) {
|
||||
args = new String[] { "GENERATE", "TESTJDK11", "TESTJDK16" };
|
||||
}
|
||||
|
||||
// Run the specified test case(s)
|
||||
Stream.of(args).map(x -> TestCase.valueOf(x)).forEach((x) -> {
|
||||
switch(x) {
|
||||
case GENERATE: generate(); break;
|
||||
case TESTJDK11: Jdk11SerializedLog.test(); break;
|
||||
case TESTJDK16: Jdk16SerializedLog.test(); break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user