1 /* 2 * Copyright (c) 2016, 2018, 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 24 /* 25 * @test 26 * @bug 6479237 27 * @summary Test the format of StackTraceElement::toString and its serial form 28 * @modules java.logging 29 * 30 * @run main SerialTest 31 */ 32 33 import java.io.BufferedInputStream; 34 import java.io.BufferedOutputStream; 35 import java.io.IOException; 36 import java.io.InputStream; 37 import java.io.ObjectInputStream; 38 import java.io.ObjectOutputStream; 39 import java.io.OutputStream; 40 import java.io.UncheckedIOException; 41 import java.lang.reflect.Method; 42 import java.net.MalformedURLException; 43 import java.net.URL; 44 import java.net.URLClassLoader; 45 import java.nio.file.Files; 46 import java.nio.file.Path; 47 import java.nio.file.Paths; 48 import java.util.Arrays; 49 import java.util.logging.Logger; 50 51 public class SerialTest { 52 private static final Path SER_DIR = Paths.get("sers"); 53 private static final String JAVA_BASE = "java.base"; 54 private static final String JAVA_LOGGING = "java.logging"; 55 56 private static boolean isImage; 57 main(String... args)58 public static void main(String... args) throws Exception { 59 Files.createDirectories(SER_DIR); 60 61 // detect if exploded image build 62 Path home = Paths.get(System.getProperty("java.home")); 63 isImage = Files.exists(home.resolve("lib").resolve("modules")); 64 65 // test stack trace from built-in loaders 66 try { 67 Logger.getLogger(null); 68 } catch (NullPointerException e) { 69 Arrays.stream(e.getStackTrace()) 70 .filter(ste -> ste.getClassName().startsWith("java.util.logging.") || 71 ste.getClassName().equals("SerialTest")) 72 .forEach(SerialTest::test); 73 } 74 75 // test stack trace with class loader name from other class loader 76 Loader loader = new Loader("myloader"); 77 Class<?> cls = Class.forName("SerialTest", true, loader); 78 Method method = cls.getMethod("throwException"); 79 StackTraceElement ste = (StackTraceElement)method.invoke(null); 80 test(ste, loader); 81 82 // verify the class loader name and in the stack trace 83 if (!cls.getClassLoader().getName().equals("myloader.hacked")) { 84 throw new RuntimeException("Unexpected loader name: " + 85 cls.getClassLoader().getName()); 86 } 87 if (!ste.getClassLoaderName().equals("myloader")) { 88 throw new RuntimeException("Unexpected loader name: " + 89 ste.getClassLoaderName()); 90 } 91 } 92 test(StackTraceElement ste)93 private static void test(StackTraceElement ste) { 94 test(ste, null); 95 } 96 test(StackTraceElement ste, ClassLoader loader)97 private static void test(StackTraceElement ste, ClassLoader loader) { 98 try { 99 SerialTest serialTest = new SerialTest(ste); 100 StackTraceElement ste2 = serialTest.serialize().deserialize(); 101 System.out.println(ste2); 102 // verify StackTraceElement::toString returns the same string 103 if (!ste.equals(ste2) || !ste.toString().equals(ste2.toString())) { 104 throw new RuntimeException(ste + " != " + ste2); 105 } 106 107 String mn = ste.getModuleName(); 108 if (mn != null) { 109 switch (mn) { 110 case JAVA_BASE: 111 case JAVA_LOGGING: 112 checkNamedModule(ste, loader, false); 113 break; 114 default: // ignore 115 } 116 } else { 117 checkUnnamedModule(ste, loader); 118 } 119 } catch (IOException e) { 120 throw new UncheckedIOException(e); 121 } 122 } 123 checkUnnamedModule(StackTraceElement ste, ClassLoader loader)124 private static void checkUnnamedModule(StackTraceElement ste, ClassLoader loader) { 125 String mn = ste.getModuleName(); 126 String s = ste.toString(); 127 int i = s.indexOf('/'); 128 129 if (mn != null) { 130 throw new RuntimeException("expected null but got " + mn); 131 } 132 133 if (loader != null) { 134 // Expect <loader>//<classname>.<method>(<src>:<ln>) 135 if (i <= 0) { 136 throw new RuntimeException("loader name missing: " + s); 137 } 138 if (!getLoaderName(loader).equals(s.substring(0, i))) { 139 throw new RuntimeException("unexpected loader name: " + s); 140 } 141 int j = s.substring(i+1).indexOf('/'); 142 if (j != 0) { 143 throw new RuntimeException("unexpected element for unnamed module: " + s); 144 } 145 } 146 } 147 148 /* 149 * Loader::getName is overridden to return some other name 150 */ getLoaderName(ClassLoader loader)151 private static String getLoaderName(ClassLoader loader) { 152 if (loader == null) 153 return ""; 154 155 if (loader instanceof Loader) { 156 return ((Loader) loader).name; 157 } else { 158 return loader.getName(); 159 } 160 } 161 checkNamedModule(StackTraceElement ste, ClassLoader loader, boolean showVersion)162 private static void checkNamedModule(StackTraceElement ste, 163 ClassLoader loader, 164 boolean showVersion) { 165 String loaderName = getLoaderName(loader); 166 String mn = ste.getModuleName(); 167 String s = ste.toString(); 168 int i = s.indexOf('/'); 169 170 if (mn == null) { 171 throw new RuntimeException("expected module name: " + s); 172 } 173 174 if (i <= 0) { 175 throw new RuntimeException("module name missing: " + s); 176 } 177 178 // Expect <module>/<classname>.<method>(<src>:<ln>) 179 if (!loaderName.isEmpty()) { 180 throw new IllegalArgumentException(loaderName); 181 } 182 183 // <module>: name@version 184 int j = s.indexOf('@'); 185 if ((showVersion && j <= 0) || (!showVersion && j >= 0)) { 186 throw new RuntimeException("unexpected version: " + s); 187 } 188 189 String name = j < 0 ? s.substring(0, i) : s.substring(0, j); 190 if (!name.equals(mn)) { 191 throw new RuntimeException("unexpected module name: " + s); 192 } 193 } 194 195 private final Path ser; 196 private final StackTraceElement ste; 197 SerialTest(StackTraceElement ste) throws IOException { 198 this.ser = Files.createTempFile(SER_DIR, "SerialTest", ".ser"); 199 this.ste = ste; 200 } 201 202 private StackTraceElement deserialize() throws IOException { 203 try (InputStream in = Files.newInputStream(ser); 204 BufferedInputStream bis = new BufferedInputStream(in); 205 ObjectInputStream ois = new ObjectInputStream(bis)) { 206 return (StackTraceElement)ois.readObject(); 207 } catch (ClassNotFoundException e) { 208 throw new RuntimeException(e); 209 } 210 } 211 212 private SerialTest serialize() throws IOException { 213 try (OutputStream out = Files.newOutputStream(ser); 214 BufferedOutputStream bos = new BufferedOutputStream(out); 215 ObjectOutputStream oos = new ObjectOutputStream(bos)) { 216 oos.writeObject(ste); 217 } 218 return this; 219 } 220 221 222 public static StackTraceElement throwException() { 223 try { 224 Integer.parseInt(null); 225 } catch (NumberFormatException e) { 226 return Arrays.stream(e.getStackTrace()) 227 .filter(ste -> ste.getMethodName().equals("throwException")) 228 .findFirst().get(); 229 } 230 return null; 231 } 232 233 public static class Loader extends URLClassLoader { 234 final String name; Loader(String name)235 Loader(String name) throws MalformedURLException { 236 super(name, new URL[] { testClassesURL() } , null); 237 this.name = name; 238 } 239 testClassesURL()240 private static URL testClassesURL() throws MalformedURLException { 241 Path path = Paths.get(System.getProperty("test.classes")); 242 return path.toUri().toURL(); 243 } 244 getName()245 public String getName() { 246 return name + ".hacked"; 247 } 248 } 249 } 250