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 {
getRotifer()54         public Rotifer getRotifer();
55     }
56 
57     public static class Bdelloid implements BdelloidMXBean {
getRotifer()58         public Rotifer getRotifer() {
59             return null;
60         }
61     }
62 
63     public static class Rotifer {
getFile()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 {
getContainer()75         public HashMapContainer getContainer();
76     }
77 
78     public static class IndirectHashMap implements IndirectHashMapMXBean {
getContainer()79         public HashMapContainer getContainer() {
80             return null;
81         }
82     }
83 
84     public static class HashMapContainer {
getHashMap()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 {
getBlimContainer()95         public BlimContainer getBlimContainer();
96     }
97 
98     public static class BlimImpl implements BlimMXBean {
getBlimContainer()99         public BlimContainer getBlimContainer() {
100             return null;
101         }
102     }
103 
104     public static class BlimContainer {
getBlim()105         public Blim getBlim() {return null;}
setBlim(Blim blim)106         public void setBlim(Blim blim) {}
107     }
108 
109     public static class Blim {
getBlam()110         public Blam getBlam() {return null;}
setBlam(Blam blam)111         public void setBlam(Blam blam) {}
112     }
113 
114     public static class Blam {
Blam(int x)115         public Blam(int x) {}
116 
getX()117         public int getX() {return 0;}
118     }
119 
120 
121     // ------ Property name differing only in case ------
122 
123     public static interface CaseProbMXBean {
getCaseProb()124         public CaseProb getCaseProb();
125     }
126 
127     public static class CaseProbImpl implements CaseProbMXBean {
getCaseProb()128         public CaseProb getCaseProb() {return null;}
129     }
130 
131     public static class CaseProb {
132         @ConstructorParameters({"urlPath"})
CaseProb(String urlPath)133         public CaseProb(String urlPath) {}
134 
getURLPath()135         public String getURLPath() {return null;}
136     }
137 
138 
main(String[] args)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 
testMXBeans(Object mbean, Type... expectedTypes)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 
testProxies( Object mbean, Class<T> mxbeanClass, Type... expectedTypes)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 
testCaseProb()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 
checkExceptionChain( String what, Throwable e, Type[] expectedTypes)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 
messageChain(Throwable t)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 
fail(String why)255     private static void fail(String why) {
256         failure = why;
257         System.out.println("FAIL: " + why);
258     }
259 }
260