1 /* 2 * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 import java.io.ByteArrayInputStream; 24 import java.io.ByteArrayOutputStream; 25 import java.io.IOException; 26 import java.io.ObjectInputStream; 27 import java.io.ObjectOutputStream; 28 import java.util.Base64; 29 import java.util.logging.Level; 30 import java.util.logging.LogRecord; 31 import java.util.logging.XMLFormatter; 32 import java.util.stream.Stream; 33 34 /** 35 * @test 36 * @bug 8245302 37 * @summary tests the deprecation of threadID and a new field longThreadID, 38 * test should be run on jdk16 and subsequent versions 39 * @run main/othervm SerializeLogRecordTest 40 */ 41 public class SerializeLogRecordTest { 42 43 /** 44 * Serializes a log record, encode the serialized bytes in base 64, and 45 * prints pseudo java code that can be cut and pasted into this test. 46 * @param record the log record to serialize, encode in base 64, and for 47 * which test data will be generated. 48 * @return A string containing the generated pseudo java code. 49 * @throws IOException Unexpected. 50 * @throws ClassNotFoundException Unexpected. 51 */ generate(LogRecord record)52 public static String generate(LogRecord record) throws IOException, ClassNotFoundException { 53 54 XMLFormatter formatter = new XMLFormatter(); 55 String str = formatter.format(record); 56 57 // Serialize the given LogRecord 58 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 59 final ObjectOutputStream oos = new ObjectOutputStream(baos); 60 oos.writeObject(record); 61 oos.flush(); 62 oos.close(); 63 64 // Now we're going to perform a number of smoke tests before 65 // generating the Java pseudo code. 66 // 67 // First checks that the log record can be deserialized 68 final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 69 final ObjectInputStream ois = new ObjectInputStream(bais); 70 final LogRecord record2 = (LogRecord)ois.readObject(); 71 72 String str2 = formatter.format(record2); 73 74 if (!str.equals(str2)) throw new RuntimeException("Unexpected values in deserialized object:" 75 + "\n\tExpected: " + str 76 + "\n\tRetrieved: "+str); 77 78 // Now get a Base64 string representation of the serialized bytes. 79 final String base64 = Base64.getEncoder().encodeToString(baos.toByteArray()); 80 81 // Check that we can deserialize a log record from the Base64 string 82 // representation we just computed. 83 final ByteArrayInputStream bais2 = new ByteArrayInputStream(Base64.getDecoder().decode(base64)); 84 final ObjectInputStream ois2 = new ObjectInputStream(bais2); 85 final LogRecord record3 = (LogRecord)ois2.readObject(); 86 87 // Format the new deserialized LogRecord using the SimpleFormatter, and 88 // check that the string representation obtained matches the string 89 // representation of the original LogRecord 90 String str3 = formatter.format(record3); 91 if (!str.equals(str3)) throw new RuntimeException("Unexpected values in deserialized object:" 92 + "\n\tExpected: " + str 93 + "\n\tRetrieved: "+str); 94 95 // Generates the Java Pseudo code that can be cut & pasted into 96 // this test (see Jdk8SerializedLog and Jdk9SerializedLog below) 97 final StringBuilder sb = new StringBuilder(); 98 sb.append(" /**").append('\n'); 99 sb.append(" * Base64 encoded string for LogRecord object.").append('\n'); 100 sb.append(" * Java version: ").append(System.getProperty("java.version")).append('\n'); 101 sb.append(" * threadID: ").append(record.getThreadID()).append('\n'); 102 sb.append(" * longThreadID: ").append(record.getLongThreadID()).append('\n'); 103 sb.append(" **/").append('\n'); 104 sb.append(" final String base64 = ").append("\n "); 105 final int last = base64.length() - 1; 106 for (int i=0; i<base64.length();i++) { 107 if (i%64 == 0) sb.append("\""); 108 sb.append(base64.charAt(i)); 109 if (i%64 == 63 || i == last) { 110 sb.append("\""); 111 if (i == last) sb.append(";\n"); 112 else sb.append("\n + "); 113 } 114 } 115 return sb.toString(); 116 } 117 118 /** 119 * An abstract class to test that a log record previously serialized on a 120 * different java version can be deserialized in the current java version. 121 * (see Jdk11SerializedLog and Jdk16SerializedLog below) 122 */ 123 public abstract static class SerializedLog { getBase64()124 public abstract String getBase64(); 125 126 /** 127 * Deserializes the Base64 encoded string returned by {@link 128 * #getBase64()}, and checks that the string representation obtained 129 * matches the original string representation returned by 130 */ dotest()131 protected void dotest() { 132 try { 133 final String base64 = getBase64(); 134 final ByteArrayInputStream bais = 135 new ByteArrayInputStream(Base64.getDecoder().decode(base64)); 136 final ObjectInputStream ois = new ObjectInputStream(bais); 137 final LogRecord record = (LogRecord)ois.readObject(); 138 check(record); 139 System.out.println("PASSED: "+this.getClass().getName()+"\n"); 140 } catch (IOException | ClassNotFoundException x) { 141 throw new RuntimeException(x); 142 } 143 } 144 /** 145 * Check that the values of threadID and longThreadID 146 * are correct and consistent when de-serialized from 147 * earlier jdk versions(jdk11) 148 */ check(LogRecord r1)149 protected void check(LogRecord r1) { 150 XMLFormatter formatter = new XMLFormatter(); 151 int check = Integer.MAX_VALUE - 10; 152 String str = formatter.format(r1); 153 String loggerName = r1.getLoggerName(); 154 155 if (loggerName.equals("test2")) { 156 int id = -2147483639; 157 if (r1.getLongThreadID() != Integer.MAX_VALUE + 10L || r1.getThreadID() != id) { 158 throw new RuntimeException(this.getClass().getName()); 159 } 160 else { 161 System.out.println("Test Integer Check Passed"); 162 } 163 164 } 165 166 if (loggerName.equals("test1") || loggerName.equals("test")) { 167 if (loggerName.equals("test1")) { 168 check = Integer.MAX_VALUE / 2 - 20; 169 } 170 171 int tid = r1.getThreadID(); 172 long longThreadID = r1.getLongThreadID(); 173 if (tid != check || longThreadID != check || !str.contains("<thread>" + String.valueOf(longThreadID))) { 174 throw new RuntimeException(this.getClass().getName()); 175 } 176 else { 177 System.out.println("Test Integer Check Passed"); 178 } 179 } 180 } 181 } 182 generate()183 public static void generate() { 184 try { 185 LogRecord record = new LogRecord(Level.INFO, "Java Version: {0}"); 186 record.setLoggerName("test"); 187 record.setThreadID(Integer.MAX_VALUE - 10); 188 record.setParameters(new Object[] {System.getProperty("java.version")}); 189 System.out.println(generate(record)); 190 LogRecord record1 = new LogRecord(Level.INFO, "Java Version: {0}"); 191 record1.setLoggerName("test1"); 192 record1.setLongThreadID(Integer.MAX_VALUE/2 - 20); 193 record1.setParameters(new Object[] {System.getProperty("java.version")}); 194 System.out.println(generate(record1)); 195 LogRecord record2 = new LogRecord(Level.INFO, "Java Version: {0}"); 196 record2.setLoggerName("test2"); 197 record2.setLongThreadID(Integer.MAX_VALUE + 10L); 198 record2.setParameters(new Object[] {System.getProperty("java.version")}); 199 System.out.println(generate(record2)); 200 } catch (IOException | ClassNotFoundException x) { 201 throw new RuntimeException(x); 202 } 203 } 204 205 public static class Jdk11SerializedLog extends SerializedLog { 206 207 /** 208 * Base64 encoded string for LogRecord object. 209 * It is generated using generate method and 210 * can be copy pasted as base64 value, for generating 211 * this value, execute this class with an argument value 212 * generate and the test will print out the base64 value 213 * that can be copy pasted below 214 * Java version: 11.0.6 215 **/ 216 final String base64 = 217 "rO0ABXNyABtqYXZhLnV0aWwubG9nZ2luZy5Mb2dSZWNvcmRKjVk982lRlgMAC0oA" 218 + "Bm1pbGxpc0kADm5hbm9BZGp1c3RtZW50SgAOc2VxdWVuY2VOdW1iZXJJAAh0aHJl" 219 + "YWRJREwABWxldmVsdAAZTGphdmEvdXRpbC9sb2dnaW5nL0xldmVsO0wACmxvZ2dl" 220 + "ck5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztMAAdtZXNzYWdlcQB+AAJMABJyZXNv" 221 + "dXJjZUJ1bmRsZU5hbWVxAH4AAkwAD3NvdXJjZUNsYXNzTmFtZXEAfgACTAAQc291" 222 + "cmNlTWV0aG9kTmFtZXEAfgACTAAGdGhyb3dudAAVTGphdmEvbGFuZy9UaHJvd2Fi" 223 + "bGU7eHAAAAFycRovVgAFN/AAAAAAAAAAAH////VzcgAXamF2YS51dGlsLmxvZ2dp" 224 + "bmcuTGV2ZWyOiHETUXM2kgIAA0kABXZhbHVlTAAEbmFtZXEAfgACTAAScmVzb3Vy" 225 + "Y2VCdW5kbGVOYW1lcQB+AAJ4cAAAAyB0AARJTkZPdAAic3VuLnV0aWwubG9nZ2lu" 226 + "Zy5yZXNvdXJjZXMubG9nZ2luZ3QABHRlc3R0ABFKYXZhIFZlcnNpb246IHswfXBw" 227 + "cHB3BgEAAAAAAXQABjExLjAuNng="; 228 229 230 @Override getBase64()231 public String getBase64() { 232 return base64; 233 } 234 235 @Override check(LogRecord r1)236 protected void check(LogRecord r1) { 237 super.check(r1); 238 } 239 test()240 public static void test() { 241 new Jdk11SerializedLog().dotest(); 242 } 243 } 244 245 public static class Jdk16SerializedLog extends SerializedLog { 246 247 /** 248 * Base64 encoded string for LogRecord object. 249 * It is generated using generate method and 250 * can be copy pasted as base64 value, for generating 251 * this value, execute this class with an argument value 252 * generate and the test will print out the base64 value 253 * that can be copy pasted below 254 * Java version: 16-internal 255 **/ 256 final String base64 = 257 "rO0ABXNyABtqYXZhLnV0aWwubG9nZ2luZy5Mb2dSZWNvcmRKjVk982lRlgMADEoA" 258 + "DGxvbmdUaHJlYWRJREoABm1pbGxpc0kADm5hbm9BZGp1c3RtZW50SgAOc2VxdWVu" 259 + "Y2VOdW1iZXJJAAh0aHJlYWRJREwABWxldmVsdAAZTGphdmEvdXRpbC9sb2dnaW5n" 260 + "L0xldmVsO0wACmxvZ2dlck5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztMAAdtZXNz" 261 + "YWdlcQB+AAJMABJyZXNvdXJjZUJ1bmRsZU5hbWVxAH4AAkwAD3NvdXJjZUNsYXNz" 262 + "TmFtZXEAfgACTAAQc291cmNlTWV0aG9kTmFtZXEAfgACTAAGdGhyb3dudAAVTGph" 263 + "dmEvbGFuZy9UaHJvd2FibGU7eHAAAAAAgAAACQAAAXLMALDdAAS+2AAAAAAAAAAC" 264 + "gAAACXNyABdqYXZhLnV0aWwubG9nZ2luZy5MZXZlbI6IcRNRczaSAgADSQAFdmFs" 265 + "dWVMAARuYW1lcQB+AAJMABJyZXNvdXJjZUJ1bmRsZU5hbWVxAH4AAnhwAAADIHQA" 266 + "BElORk90ACJzdW4udXRpbC5sb2dnaW5nLnJlc291cmNlcy5sb2dnaW5ndAAFdGVz" 267 + "dDJ0ABFKYXZhIFZlcnNpb246IHswfXBwcHB3BgEAAAAAAXQACzE2LWludGVybmFs" 268 + "eA=="; 269 270 271 @Override getBase64()272 public String getBase64() { 273 return base64; 274 } 275 276 @Override check(LogRecord r1)277 protected void check(LogRecord r1) { 278 super.check(r1); 279 } 280 test()281 public static void test() { 282 new Jdk16SerializedLog().dotest(); 283 } 284 } 285 286 static enum TestCase { GENERATE, TESTJDK11, TESTJDK16 }; 287 main(String[] args)288 public static void main(String[] args) { 289 290 291 // If no args, then run everything.... 292 if (args == null || args.length == 0) { 293 args = new String[] { "GENERATE", "TESTJDK11", "TESTJDK16" }; 294 } 295 296 // Run the specified test case(s) 297 Stream.of(args).map(x -> TestCase.valueOf(x)).forEach((x) -> { 298 switch(x) { 299 case GENERATE: generate(); break; 300 case TESTJDK11: Jdk11SerializedLog.test(); break; 301 case TESTJDK16: Jdk16SerializedLog.test(); break; 302 } 303 }); 304 } 305 } 306