1 /* 2 * Copyright (c) 2008, 2015, 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 6713777 27 * @summary Test that exception messages include all relevant information 28 * @author Eamonn McManus 29 */ 30 31 import javax.management.ConstructorParameters; 32 import java.io.File; 33 import java.lang.reflect.InvocationTargetException; 34 import java.lang.reflect.Method; 35 import java.lang.reflect.Type; 36 import java.util.ArrayList; 37 import java.util.HashMap; 38 import java.util.List; 39 import javax.management.JMX; 40 import javax.management.MBeanServer; 41 import javax.management.MBeanServerFactory; 42 import javax.management.NotCompliantMBeanException; 43 import javax.management.ObjectName; 44 45 public class ExceptionDiagnosisTest { 46 private static volatile String failure; 47 48 // ------ Illegal MXBeans ------ 49 50 // Test that all of BdelloidMXBean, Rotifer, and File appear in the 51 // exception messages. File is not an allowed type because of recursive 52 // getters like "File getParentFile()". 53 public static interface BdelloidMXBean { 54 public Rotifer getRotifer(); 55 } 56 57 public static class Bdelloid implements BdelloidMXBean { 58 public Rotifer getRotifer() { 59 return null; 60 } 61 } 62 63 public static class Rotifer { 64 public File getFile() { 65 return null; 66 } 67 } 68 69 // Test that all of IndirectHashMapMXBean, HashMapContainer, and 70 // HashMap<String,String> appear in the exception messages. 71 // HashMap<String,String> is not an allowed type because only the 72 // java.util interface such as Map are allowed with generic parameters, 73 // not their concrete implementations like HashMap. 74 public static interface IndirectHashMapMXBean { 75 public HashMapContainer getContainer(); 76 } 77 78 public static class IndirectHashMap implements IndirectHashMapMXBean { 79 public HashMapContainer getContainer() { 80 return null; 81 } 82 } 83 84 public static class HashMapContainer { 85 public HashMap<String, String> getHashMap() {return null;} 86 } 87 88 // ------ MXBeans that are legal but where proxies are not ------ 89 90 // Test that all of BlimMXBean, BlimContainer, Blim, and Blam appear 91 // in the exception messages for a proxy for this MXBean. Blam is 92 // legal in MXBeans but is not reconstructible so you cannot make 93 // a proxy for BlimMXBean. 94 public static interface BlimMXBean { 95 public BlimContainer getBlimContainer(); 96 } 97 98 public static class BlimImpl implements BlimMXBean { 99 public BlimContainer getBlimContainer() { 100 return null; 101 } 102 } 103 104 public static class BlimContainer { 105 public Blim getBlim() {return null;} 106 public void setBlim(Blim blim) {} 107 } 108 109 public static class Blim { 110 public Blam getBlam() {return null;} 111 public void setBlam(Blam blam) {} 112 } 113 114 public static class Blam { 115 public Blam(int x) {} 116 117 public int getX() {return 0;} 118 } 119 120 121 // ------ Property name differing only in case ------ 122 123 public static interface CaseProbMXBean { 124 public CaseProb getCaseProb(); 125 } 126 127 public static class CaseProbImpl implements CaseProbMXBean { 128 public CaseProb getCaseProb() {return null;} 129 } 130 131 public static class CaseProb { 132 @ConstructorParameters({"urlPath"}) 133 public CaseProb(String urlPath) {} 134 135 public String getURLPath() {return null;} 136 } 137 138 139 public static void main(String[] args) throws Exception { 140 testMXBeans(new Bdelloid(), BdelloidMXBean.class, Rotifer.class, File.class); 141 testMXBeans(new IndirectHashMap(), 142 IndirectHashMapMXBean.class, HashMapContainer.class, 143 HashMapContainer.class.getMethod("getHashMap").getGenericReturnType()); 144 145 testProxies(new BlimImpl(), BlimMXBean.class, BlimMXBean.class, 146 BlimContainer.class, Blim.class, Blam.class); 147 148 testCaseProb(); 149 150 if (failure == null) 151 System.out.println("TEST PASSED"); 152 else 153 throw new Exception("TEST FAILED: " + failure); 154 } 155 156 private static void testMXBeans(Object mbean, Type... expectedTypes) 157 throws Exception { 158 try { 159 MBeanServer mbs = MBeanServerFactory.newMBeanServer(); 160 ObjectName name = new ObjectName("a:b=c"); 161 mbs.registerMBean(mbean, name); 162 fail("No exception from registerMBean for " + mbean); 163 } catch (NotCompliantMBeanException e) { 164 checkExceptionChain("MBean " + mbean, e, expectedTypes); 165 } 166 } 167 168 private static <T> void testProxies( 169 Object mbean, Class<T> mxbeanClass, Type... expectedTypes) 170 throws Exception { 171 MBeanServer mbs = MBeanServerFactory.newMBeanServer(); 172 ObjectName name = new ObjectName("a:b=c"); 173 mbs.registerMBean(mbean, name); 174 T proxy = JMX.newMXBeanProxy(mbs, name, mxbeanClass); 175 List<Method> methods = new ArrayList<Method>(); 176 for (Method m : mxbeanClass.getMethods()) { 177 if (m.getDeclaringClass() == mxbeanClass) 178 methods.add(m); 179 } 180 if (methods.size() != 1) { 181 fail("TEST BUG: expected to find exactly one method in " + 182 mxbeanClass.getName() + ": " + methods); 183 } 184 Method getter = methods.get(0); 185 try { 186 try { 187 getter.invoke(proxy); 188 fail("No exception from proxy method " + getter.getName() + 189 " in " + mxbeanClass.getName()); 190 } catch (InvocationTargetException e) { 191 Throwable cause = e.getCause(); 192 if (cause instanceof Exception) 193 throw (Exception) cause; 194 else 195 throw (Error) cause; 196 } 197 } catch (IllegalArgumentException e) { 198 checkExceptionChain( 199 "Proxy for " + mxbeanClass.getName(), e, expectedTypes); 200 } 201 } 202 203 private static void testCaseProb() throws Exception { 204 MBeanServer mbs = MBeanServerFactory.newMBeanServer(); 205 ObjectName name = new ObjectName("a:b=c"); 206 mbs.registerMBean(new CaseProbImpl(), name); 207 CaseProbMXBean proxy = JMX.newMXBeanProxy(mbs, name, CaseProbMXBean.class); 208 try { 209 CaseProb prob = proxy.getCaseProb(); 210 fail("No exception from proxy method getCaseProb"); 211 } catch (IllegalArgumentException e) { 212 String messageChain = messageChain(e); 213 if (messageChain.contains("URLPath")) { 214 System.out.println("Message chain contains URLPath as required: " 215 + messageChain); 216 } else { 217 fail("Exception chain for CaseProb does not mention property" + 218 " URLPath differing only in case"); 219 System.out.println("Full stack trace:"); 220 e.printStackTrace(System.out); 221 } 222 } 223 } 224 225 private static void checkExceptionChain( 226 String what, Throwable e, Type[] expectedTypes) { 227 System.out.println("Exceptions in chain for " + what + ":"); 228 for (Throwable t = e; t != null; t = t.getCause()) 229 System.out.println(".." + t); 230 231 String messageChain = messageChain(e); 232 233 // Now check that each of the classes is mentioned in those messages 234 for (Type type : expectedTypes) { 235 String name = (type instanceof Class) ? 236 ((Class<?>) type).getName() : type.toString(); 237 if (!messageChain.contains(name)) { 238 fail("Exception chain for " + what + " does not mention " + 239 name); 240 System.out.println("Full stack trace:"); 241 e.printStackTrace(System.out); 242 } 243 } 244 245 System.out.println(); 246 } 247 248 private static String messageChain(Throwable t) { 249 String msg = "//"; 250 for ( ; t != null; t = t.getCause()) 251 msg += " " + t.getMessage() + " //"; 252 return msg; 253 } 254 255 private static void fail(String why) { 256 failure = why; 257 System.out.println("FAIL: " + why); 258 } 259 }