/* * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @bug 6713777 * @summary Test that exception messages include all relevant information * @author Eamonn McManus * @modules java.desktop * java.management */ import java.beans.ConstructorProperties; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import javax.management.JMX; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; public class ExceptionDiagnosisTest { private static volatile String failure; // ------ Illegal MXBeans ------ // Test that all of BdelloidMXBean, Rotifer, and File appear in the // exception messages. File is not an allowed type because of recursive // getters like "File getParentFile()". public static interface BdelloidMXBean { public Rotifer getRotifer(); } public static class Bdelloid implements BdelloidMXBean { public Rotifer getRotifer() { return null; } } public static class Rotifer { public File getFile() { return null; } } // Test that all of IndirectHashMapMXBean, HashMapContainer, and // HashMap appear in the exception messages. // HashMap is not an allowed type because only the // java.util interface such as Map are allowed with generic parameters, // not their concrete implementations like HashMap. public static interface IndirectHashMapMXBean { public HashMapContainer getContainer(); } public static class IndirectHashMap implements IndirectHashMapMXBean { public HashMapContainer getContainer() { return null; } } public static class HashMapContainer { public HashMap getHashMap() {return null;} } // ------ MXBeans that are legal but where proxies are not ------ // Test that all of BlimMXBean, BlimContainer, Blim, and Blam appear // in the exception messages for a proxy for this MXBean. Blam is // legal in MXBeans but is not reconstructible so you cannot make // a proxy for BlimMXBean. public static interface BlimMXBean { public BlimContainer getBlimContainer(); } public static class BlimImpl implements BlimMXBean { public BlimContainer getBlimContainer() { return null; } } public static class BlimContainer { public Blim getBlim() {return null;} public void setBlim(Blim blim) {} } public static class Blim { public Blam getBlam() {return null;} public void setBlam(Blam blam) {} } public static class Blam { public Blam(int x) {} public int getX() {return 0;} } // ------ Property name differing only in case ------ public static interface CaseProbMXBean { public CaseProb getCaseProb(); } public static class CaseProbImpl implements CaseProbMXBean { public CaseProb getCaseProb() {return null;} } public static class CaseProb { @ConstructorProperties({"urlPath"}) public CaseProb(String urlPath) {} public String getURLPath() {return null;} } public static void main(String[] args) throws Exception { testMXBeans(new Bdelloid(), BdelloidMXBean.class, Rotifer.class, File.class); testMXBeans(new IndirectHashMap(), IndirectHashMapMXBean.class, HashMapContainer.class, HashMapContainer.class.getMethod("getHashMap").getGenericReturnType()); testProxies(new BlimImpl(), BlimMXBean.class, BlimMXBean.class, BlimContainer.class, Blim.class, Blam.class); testCaseProb(); if (failure == null) System.out.println("TEST PASSED"); else throw new Exception("TEST FAILED: " + failure); } private static void testMXBeans(Object mbean, Type... expectedTypes) throws Exception { try { MBeanServer mbs = MBeanServerFactory.newMBeanServer(); ObjectName name = new ObjectName("a:b=c"); mbs.registerMBean(mbean, name); fail("No exception from registerMBean for " + mbean); } catch (NotCompliantMBeanException e) { checkExceptionChain("MBean " + mbean, e, expectedTypes); } } private static void testProxies( Object mbean, Class mxbeanClass, Type... expectedTypes) throws Exception { MBeanServer mbs = MBeanServerFactory.newMBeanServer(); ObjectName name = new ObjectName("a:b=c"); mbs.registerMBean(mbean, name); T proxy = JMX.newMXBeanProxy(mbs, name, mxbeanClass); List methods = new ArrayList(); for (Method m : mxbeanClass.getMethods()) { if (m.getDeclaringClass() == mxbeanClass) methods.add(m); } if (methods.size() != 1) { fail("TEST BUG: expected to find exactly one method in " + mxbeanClass.getName() + ": " + methods); } Method getter = methods.get(0); try { try { getter.invoke(proxy); fail("No exception from proxy method " + getter.getName() + " in " + mxbeanClass.getName()); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof Exception) throw (Exception) cause; else throw (Error) cause; } } catch (IllegalArgumentException e) { checkExceptionChain( "Proxy for " + mxbeanClass.getName(), e, expectedTypes); } } private static void testCaseProb() throws Exception { MBeanServer mbs = MBeanServerFactory.newMBeanServer(); ObjectName name = new ObjectName("a:b=c"); mbs.registerMBean(new CaseProbImpl(), name); CaseProbMXBean proxy = JMX.newMXBeanProxy(mbs, name, CaseProbMXBean.class); try { CaseProb prob = proxy.getCaseProb(); fail("No exception from proxy method getCaseProb"); } catch (IllegalArgumentException e) { String messageChain = messageChain(e); if (messageChain.contains("URLPath")) { System.out.println("Message chain contains URLPath as required: " + messageChain); } else { fail("Exception chain for CaseProb does not mention property" + " URLPath differing only in case"); System.out.println("Full stack trace:"); e.printStackTrace(System.out); } } } private static void checkExceptionChain( String what, Throwable e, Type[] expectedTypes) { System.out.println("Exceptions in chain for " + what + ":"); for (Throwable t = e; t != null; t = t.getCause()) System.out.println(".." + t); String messageChain = messageChain(e); // Now check that each of the classes is mentioned in those messages for (Type type : expectedTypes) { String name = (type instanceof Class) ? ((Class) type).getName() : type.toString(); if (!messageChain.contains(name)) { fail("Exception chain for " + what + " does not mention " + name); System.out.println("Full stack trace:"); e.printStackTrace(System.out); } } System.out.println(); } private static String messageChain(Throwable t) { String msg = "//"; for ( ; t != null; t = t.getCause()) msg += " " + t.getMessage() + " //"; return msg; } private static void fail(String why) { failure = why; System.out.println("FAIL: " + why); } }