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 }