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