| /* |
| * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * 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.time.ZoneId; |
| import java.util.Base64; |
| import java.util.Locale; |
| import java.util.TimeZone; |
| import java.util.logging.Level; |
| import java.util.logging.LogRecord; |
| import java.util.logging.SimpleFormatter; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import java.util.stream.Stream; |
| |
| /** |
| * @test |
| * @bug 8072645 |
| * @summary tests the compatibility of LogRecord serial form between |
| * JDK 8 and JDK 9. Ideally this test should be run on both platforms. |
| * (It is designed to run on both). |
| * @run main/othervm SerializeLogRecord |
| * @author danielfuchs |
| */ |
| public class SerializeLogRecord { |
| |
| /** |
| * 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 { |
| |
| // Format the given logRecord using the SimpleFormatter |
| SimpleFormatter formatter = new SimpleFormatter(); |
| 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(); |
| |
| // Format the deserialized LogRecord using the SimpleFormatter, and |
| // check that the string representation obtained matches the string |
| // representation of the original LogRecord |
| 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); |
| //System.out.println(base64); |
| //System.out.println(); |
| |
| // 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(" **/").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 + "); |
| } |
| } |
| sb.append('\n'); |
| sb.append(" /**").append('\n'); |
| sb.append(" * SimpleFormatter output for LogRecord object.").append('\n'); |
| sb.append(" * Java version: ").append(System.getProperty("java.version")).append('\n'); |
| sb.append(" **/").append('\n'); |
| sb.append(" final String str = ").append("\n "); |
| sb.append("\"").append(str.replace("\n", "\\n")).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 Jdk8SerializedLog and Jdk9SerializedLog below) |
| */ |
| public abstract static class SerializedLog { |
| public abstract String getBase64(); |
| public abstract String getString(); |
| |
| /** |
| * Deserializes the Base64 encoded string returned by {@link |
| * #getBase64()}, format the obtained LogRecord using a |
| * SimpleFormatter, and checks that the string representation obtained |
| * matches the original string representation returned by {@link |
| * #getString()}. |
| */ |
| 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(); |
| final SimpleFormatter formatter = new SimpleFormatter(); |
| String expected = getString(); |
| String str2 = formatter.format(record); |
| check(expected, str2); |
| System.out.println(str2); |
| System.out.println("PASSED: "+this.getClass().getName()+"\n"); |
| } catch (IOException | ClassNotFoundException x) { |
| throw new RuntimeException(x); |
| } |
| } |
| /** |
| * Check that the actual String representation obtained matches the |
| * expected String representation. |
| * @param expected Expected String representation, as returned by |
| * {@link #getString()}. |
| * @param actual Actual String representation obtained by formatting |
| * the LogRecord obtained by the deserialization of the |
| * bytes encoded in {@link #getBase64()}. |
| */ |
| protected void check(String expected, String actual) { |
| if (!expected.equals(actual)) { |
| throw new RuntimeException(this.getClass().getName() |
| + " - Unexpected values in deserialized object:" |
| + "\n\tExpected: " + expected |
| + "\n\tRetrieved: "+ actual); |
| } |
| } |
| } |
| |
| public static class Jdk8SerializedLog extends SerializedLog { |
| |
| // Generated by generate() on JDK 8. |
| // -------------------------------- |
| // BEGIN |
| |
| /** |
| * Base64 encoded string for LogRecord object. |
| * Java version: 1.8.0_11 |
| **/ |
| final String base64 = |
| "rO0ABXNyABtqYXZhLnV0aWwubG9nZ2luZy5Mb2dSZWNvcmRKjVk982lRlgMACkoA" |
| + "Bm1pbGxpc0oADnNlcXVlbmNlTnVtYmVySQAIdGhyZWFkSURMAAVsZXZlbHQAGUxq" |
| + "YXZhL3V0aWwvbG9nZ2luZy9MZXZlbDtMAApsb2dnZXJOYW1ldAASTGphdmEvbGFu" |
| + "Zy9TdHJpbmc7TAAHbWVzc2FnZXEAfgACTAAScmVzb3VyY2VCdW5kbGVOYW1lcQB+" |
| + "AAJMAA9zb3VyY2VDbGFzc05hbWVxAH4AAkwAEHNvdXJjZU1ldGhvZE5hbWVxAH4A" |
| + "AkwABnRocm93bnQAFUxqYXZhL2xhbmcvVGhyb3dhYmxlO3hwAAABSjUCgo0AAAAA" |
| + "AAAAAAAAAAFzcgAXamF2YS51dGlsLmxvZ2dpbmcuTGV2ZWyOiHETUXM2kgIAA0kA" |
| + "BXZhbHVlTAAEbmFtZXEAfgACTAAScmVzb3VyY2VCdW5kbGVOYW1lcQB+AAJ4cAAA" |
| + "AyB0AARJTkZPdAAic3VuLnV0aWwubG9nZ2luZy5yZXNvdXJjZXMubG9nZ2luZ3QA" |
| + "BHRlc3R0ABFKYXZhIFZlcnNpb246IHswfXBwcHB3BgEAAAAAAXQACDEuOC4wXzEx" |
| + "eA=="; |
| |
| /** |
| * SimpleFormatter output for LogRecord object. |
| * Java version: 1.8.0_11 |
| **/ |
| final String str = |
| "Dec 10, 2014 4:22:44.621000000 PM test - INFO: Java Version: 1.8.0_11"; |
| // ^^^ |
| // Notice the milli second resolution above... |
| |
| // END |
| // -------------------------------- |
| |
| @Override |
| public String getBase64() { |
| return base64; |
| } |
| |
| @Override |
| public String getString() { |
| return str; |
| } |
| |
| public static void test() { |
| new Jdk8SerializedLog().dotest(); |
| } |
| } |
| |
| public static class Jdk9SerializedLog extends SerializedLog { |
| |
| // Generated by generate() on JDK 9. |
| // -------------------------------- |
| // BEGIN |
| |
| /** |
| * Base64 encoded string for LogRecord object. |
| * Java version: 1.9.0-internal |
| **/ |
| final String base64 = |
| "rO0ABXNyABtqYXZhLnV0aWwubG9nZ2luZy5Mb2dSZWNvcmRKjVk982lRlgMAC0oA" |
| + "Bm1pbGxpc0kADm5hbm9BZGp1c3RtZW50SgAOc2VxdWVuY2VOdW1iZXJJAAh0aHJl" |
| + "YWRJREwABWxldmVsdAAZTGphdmEvdXRpbC9sb2dnaW5nL0xldmVsO0wACmxvZ2dl" |
| + "ck5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztMAAdtZXNzYWdlcQB+AAJMABJyZXNv" |
| + "dXJjZUJ1bmRsZU5hbWVxAH4AAkwAD3NvdXJjZUNsYXNzTmFtZXEAfgACTAAQc291" |
| + "cmNlTWV0aG9kTmFtZXEAfgACTAAGdGhyb3dudAAVTGphdmEvbGFuZy9UaHJvd2Fi" |
| + "bGU7eHAAAAFLl3u6OAAOU/gAAAAAAAAAAAAAAAFzcgAXamF2YS51dGlsLmxvZ2dp" |
| + "bmcuTGV2ZWyOiHETUXM2kgIAA0kABXZhbHVlTAAEbmFtZXEAfgACTAAScmVzb3Vy" |
| + "Y2VCdW5kbGVOYW1lcQB+AAJ4cAAAAyB0AARJTkZPdAAic3VuLnV0aWwubG9nZ2lu" |
| + "Zy5yZXNvdXJjZXMubG9nZ2luZ3QABHRlc3R0ABFKYXZhIFZlcnNpb246IHswfXBw" |
| + "cHB3BgEAAAAAAXQADjEuOS4wLWludGVybmFseA=="; |
| |
| /** |
| * SimpleFormatter output for LogRecord object. |
| * Java version: 1.9.0-internal |
| **/ |
| final String str = |
| "Feb 17, 2015 12:20:43.192939000 PM test - INFO: Java Version: 1.9.0-internal"; |
| // ^^^ |
| // Notice the micro second resolution above... |
| |
| // END |
| // -------------------------------- |
| |
| @Override |
| public String getBase64() { |
| return base64; |
| } |
| |
| @Override |
| public String getString() { |
| return str; |
| } |
| |
| @Override |
| protected void check(String expected, String actual) { |
| if (System.getProperty("java.version").startsWith("1.8")) { |
| // If we are in JDK 8 and print a log record serialized in JDK 9, |
| // then we won't be able to print anything below the millisecond |
| // precision, since that hasn't been implemented in JDK 8. |
| // Therefore - we need to replace anything below millseconds by |
| // zeroes in the expected string (which was generated on JDK 9). |
| Pattern pattern = Pattern.compile("^" |
| + "(.*\\.[0-9][0-9][0-9])" // group1: everything up to milliseconds |
| + "([0-9][0-9][0-9][0-9][0-9][0-9])" // group 2: micros and nanos |
| + "(.* - .*)$"); // group three: all the rest... |
| Matcher matcher = pattern.matcher(expected); |
| if (matcher.matches()) { |
| expected = matcher.group(1) + "000000" + matcher.group(3); |
| } |
| } |
| super.check(expected, actual); |
| } |
| |
| public static void test() { |
| new Jdk9SerializedLog().dotest(); |
| } |
| } |
| |
| public static void generate() { |
| try { |
| LogRecord record = new LogRecord(Level.INFO, "Java Version: {0}"); |
| record.setLoggerName("test"); |
| record.setParameters(new Object[] {System.getProperty("java.version")}); |
| System.out.println(generate(record)); |
| } catch (IOException | ClassNotFoundException x) { |
| throw new RuntimeException(x); |
| } |
| } |
| |
| static enum TestCase { GENERATE, TESTJDK8, TESTJDK9 }; |
| |
| public static void main(String[] args) { |
| // Set the locale and time zone to make sure we won't depend on the |
| // test env - in particular we don't want to depend on the |
| // time zone in which the test machine might be located. |
| // So we're gong to use Locale English and Time Zone UTC for this test. |
| // (Maybe that should be Locale.ROOT?) |
| Locale.setDefault(Locale.ENGLISH); |
| TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("UTC"))); |
| |
| // Set the format property to make sure we always have the nanos, and |
| // to make sure it's the same format than what we used when |
| // computing the formatted string for Jdk8SerializedLog and |
| // Jdk9SerializedLog above. |
| // |
| // If you change the formatting, then you will need to regenerate |
| // the data for Jdk8SerializedLog and Jdk9SerializedLog. |
| // |
| // To do that - just run this test on JDK 8, and cut & paste the |
| // pseudo code printed by generate() into Jdk8SerializedLog. |
| // Then run this test again on JDK 9, and cut & paste the |
| // pseudo code printed by generate() into Jdk9SerializedLog. |
| // [Note: you can pass GENERATE as single arg to main() to avoid |
| // running the actual test] |
| // Finally run the test again to check that it still passes after |
| // your modifications. |
| // |
| System.setProperty("java.util.logging.SimpleFormatter.format", |
| "%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS.%1$tN %1$Tp %2$s - %4$s: %5$s%6$s"); |
| |
| // If no args, then run everything.... |
| if (args == null || args.length == 0) { |
| args = new String[] { "GENERATE", "TESTJDK8", "TESTJDK9" }; |
| } |
| |
| // Run the specified test case(s) |
| Stream.of(args).map(x -> TestCase.valueOf(x)).forEach((x) -> { |
| switch(x) { |
| case GENERATE: generate(); break; |
| case TESTJDK8: Jdk8SerializedLog.test(); break; |
| case TESTJDK9: Jdk9SerializedLog.test(); break; |
| } |
| }); |
| } |
| } |