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