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 8058865
  27  * @summary Checks that exceptions are correctly wired (compared to reference).
  28  * @author Olivier Lagneau
  29  *
  30  * @modules java.management.rmi
  31  *
  32  * @run main/othervm/timeout=300 -DDEBUG_STANDARD ExceptionTest
  33  */
  34 
  35 import java.util.Map;
  36 import java.util.HashMap;
  37 import java.util.Properties;
  38 import java.lang.reflect.Method;
  39 
  40 import java.lang.management.ManagementFactory;
  41 import javax.management.ObjectName;
  42 import javax.management.MBeanServer;
  43 import javax.management.MBeanServerConnection;
  44 import javax.management.remote.JMXConnector;
  45 import javax.management.remote.JMXConnectorFactory;
  46 import javax.management.remote.JMXConnectorServer;
  47 import javax.management.remote.JMXConnectorServerFactory;
  48 import javax.management.remote.JMXServiceURL;
  49 
  50 
  51 public class ExceptionTest {
  52 
  53     /*
  54      * First Debug properties and arguments are collect in expected
  55      * map  (argName, value) format, then calls original test's run method.
  56      */
  57     public static void main(String args[]) throws Exception {
  58 
  59         System.out.println("=================================================");
  60 
  61         // Parses parameters
  62         Utils.parseDebugProperties();
  63         Map<String, Object> map = Utils.parseParameters(args) ;
  64 
  65         // Run test
  66         ExceptionTest test = new ExceptionTest();
  67         test.run(map);
  68 
  69     }
  70 
  71     public void run(Map<String, Object> args) {
  72 
  73         System.out.println("ExceptionTest::run: Start");
  74         int errorCount = 0;
  75 
  76         JMXConnectorServer cs = null;
  77         JMXConnector cc = null;
  78 
  79         try {
  80             // JMX MbeanServer used inside single VM as if remote.
  81             MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
  82 
  83             JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
  84             cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
  85             cs.start();
  86 
  87             JMXServiceURL addr = cs.getAddress();
  88             cc = JMXConnectorFactory.connect(addr);
  89             MBeanServerConnection mbsc = cc.getMBeanServerConnection();
  90 
  91             // ----
  92             ObjectName objName =
  93                 new ObjectName(ExceptionThrower.EXCEPTION_THROWER_NAME);
  94             System.out.println("ExceptionTest::run: Create and register MBean " + objName);
  95             mbsc.createMBean("ExceptionThrower", objName);
  96             System.out.println("---- OK\n");
  97 
  98             // ----
  99             System.out.println("ExceptionTest::run: Ask for exception(s)");
 100             Object[] throwExceptionParam = new Object[1];
 101             String[] throwExceptionSig = new String[]{"int"};
 102 
 103             for (int i = 0; i < ExceptionFactory.exceptions.size(); i++) {
 104                 throwExceptionParam[0] = new Integer(i);
 105 
 106                 Exception ex =
 107                     (Exception)mbsc.invoke(objName,
 108                         "throwException", throwExceptionParam, throwExceptionSig);
 109 
 110                 if ( ! matches(ex, ExceptionFactory.exceptions.get(i)) ) {
 111                     errorCount++;
 112                     System.out.println("ExceptionTest::run: (ERROR) Received \n["
 113                             + ex + "]\nin place of\n["
 114                             + ExceptionFactory.exceptions.get(i) + "]");
 115                 } else {
 116                     System.out.println("OK [" + ex + "]");
 117                 }
 118             }
 119 
 120             System.out.println("---- DONE\n");
 121 
 122         } catch (Exception e) {
 123             Utils.printThrowable(e, true);
 124             throw new RuntimeException();
 125         } finally {
 126             try {
 127                 // Close JMX Connector Client
 128                 cc.close();
 129                 // Stop connertor server
 130                 cs.stop();
 131 
 132             } catch (Exception e) {
 133                 Utils.printThrowable(e, true);
 134                 throw new RuntimeException(
 135                     "Unable to either close connector client or stop connector server");
 136             }
 137         }
 138 
 139         if (errorCount == 0) {
 140             System.out.println("ExceptionTest::run: Done without any error");
 141         } else {
 142             System.out.println("ExceptionTest::run: Done with " + errorCount
 143                     + " error(s)");
 144             throw new RuntimeException("errorCount = " + errorCount);
 145         }
 146 
 147         System.out.println("ExceptionTest::run: Done");
 148     }
 149 
 150     // Check both Exception are identical.
 151     // That means:
 152     // - none is null.
 153     // - they are of the same Class.
 154     // - if their respective messages aren't null they're equal.
 155     // - if the message of one is null the message of the other is null too.
 156     private boolean matches(Exception ex, Exception refex) {
 157         if ( ex == null || refex == null ) {
 158             System.out.println("(ERROR) Called with one or more null parameter; check "
 159                     + ex + " against " + refex);
 160             return false;
 161         }
 162 
 163         String exClass = ex.getClass().getName();
 164         String refexClass = refex.getClass().getName();
 165 
 166         if ( ! exClass.equals(refexClass) ) {
 167             System.out.println("(ERROR) Class names don't match; check ["
 168                         + exClass + "] against [" + refexClass + "]");
 169             return false;
 170         }
 171 
 172         String exMes = ex.getMessage();
 173         String refexMes = refex.getMessage();
 174 
 175         if ( exMes != null && refexMes != null ) {
 176             if ( ! exMes.equals(refexMes) ) {
 177                 System.out.println("(ERROR) Non null messages don't match; check ["
 178                         + exMes + "] against [" + refexMes + "]");
 179                 return false;
 180             }
 181         } else if ( (exMes == null && refexMes != null)
 182                 || (exMes != null && refexMes == null) ) {
 183                 System.out.println("(ERROR) Messages don't match; check [" + exMes
 184                         + "] against [" + refexMes + "]");
 185         }
 186 
 187         return true;
 188     }
 189 
 190     // Utility inner class coming from JMX Tonga test suite.
 191     // Also used by ExceptionFactory.
 192     static class Utils {
 193 
 194         // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property
 195         static final String DEBUG_HEADER = "[debug] ";
 196 
 197         // DEBUG levels
 198         static int selectedDebugLevel = 0;
 199         static final int DEBUG_STANDARD = 1;
 200         static final int DEBUG_VERBOSE = 2;  // Mainly used for stress tests
 201         static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE;
 202 
 203         static void parseDebugProperties() {
 204             int level = 0;
 205             Properties p = System.getProperties();
 206 
 207             // get selected levels
 208             if (p.getProperty("DEBUG_STANDARD") != null) {
 209                 level |= DEBUG_STANDARD;
 210             }
 211 
 212             if (p.getProperty("DEBUG_VERBOSE") != null) {
 213                 level |= DEBUG_VERBOSE;
 214             }
 215 
 216             if (p.getProperty("DEBUG_ALL") != null) {
 217                 level |= DEBUG_ALL;
 218             }
 219 
 220             selectedDebugLevel = level;
 221         }
 222 
 223         /**
 224          * Reproduces the original parsing and collection of test parameters
 225          * from the DTonga JMX test suite.
 226          *
 227          * Collects passed args and returns them in a map(argname, value) structure,
 228          * which will be then propagated as necessary to various called methods.
 229          */
 230         static Map<String, Object> parseParameters(String args[])
 231         throws Exception {
 232             debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start");
 233             HashMap<String, Object> map = new HashMap<>();
 234 
 235             for ( int i = 0; i < args.length; i++ ) {
 236                 if ( args[i].trim().startsWith("-") ) {
 237                     if ((i+1) < args.length && !args[i+1].startsWith("-") ) {
 238                         debug(DEBUG_STANDARD,
 239                             "TestRoot::parseParameters: added in map = " +
 240                             args[i] +
 241                             " with value " +
 242                             args[i+1]) ;
 243                         map.put(args[i].trim(), args[i+1].trim()) ;
 244                     } else if ((i+1) < args.length && args[i+1].startsWith("-") ||
 245                                (i+1) == args.length ) {
 246                         debug(DEBUG_STANDARD,
 247                                 "TestRoot::parseParameters: added in map = " +
 248                                 args[i] +
 249                                 " with null value") ;
 250                         map.put(args[i].trim(), null) ;
 251                     } else {
 252                         System.out.println(
 253                             "TestRoot::parseParameters: (WARNING) not added in map = " +
 254                             args[i]) ;
 255                     }
 256                 }
 257             }
 258 
 259             debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ;
 260             return map ;
 261         }
 262 
 263         /**
 264          * This method is to be used in all tests to print anything
 265          * that is temporary.
 266          * Printing is done only when debug is activated by the property DEBUG.
 267          * Printing depends also on the DEBUG_LEVEL property.
 268          * Here it encapsulates a System.out.println.
 269          */
 270         static void debug(int level, String line) {
 271             if ((selectedDebugLevel & level) != 0) {
 272                 System.out.println(DEBUG_HEADER + line);
 273             }
 274         }
 275 
 276         /**
 277          * Do print stack trace when withStack is true.
 278          * Does try to call getTargetException() and getTargetError() then
 279          * print embedded stacks in the case of an Exception wrapping
 280          * another Exception or an Error. Recurse until no more wrapping
 281          * is found.
 282          */
 283         static void printThrowable(Throwable theThro, boolean withStack) {
 284             try {
 285                 if (withStack) {
 286                     theThro.printStackTrace(System.out);
 287                 }
 288                 if (theThro instanceof Exception) {
 289                     Exception t = (Exception) theThro;
 290                     Method target = null;
 291                     String blank = " ";
 292                     try {
 293                         target = t.getClass().getMethod("getTargetException",
 294                                 (java.lang.Class<?>[]) null);
 295                     } catch (Exception ee) {
 296                     // OK: getTargetException method could be there or not
 297                     }
 298                     System.out.println(blank + t.getClass() + "==>" + t.getMessage());
 299                     while (target != null) {
 300                         try {
 301                             t = (Exception) target.invoke(t,
 302                                     (java.lang.Object[]) null);
 303                         } catch (Exception ee) {
 304                             t = null;
 305                         }
 306                         try {
 307                             if (t != null) {
 308                                 blank = blank + "  ";
 309                                 System.out.println(blank + t.getClass() + "==>" +
 310                                         t.getMessage());
 311                                 try {
 312                                     target =
 313                                             t.getClass().getMethod("getTargetException",
 314                                             (java.lang.Class<?>[]) null);
 315                                 } catch (Exception ee) {
 316                                 // OK: getTargetException method could be there or not                            }
 317                                 }
 318                             } else {
 319                                 target = null;
 320                             }
 321                         } catch (Exception ee) {
 322                             target = null;
 323                         }
 324                     }
 325 
 326                     // We may have exceptions wrapping an Error then it is
 327                     // getTargetError that is likely to be called
 328                     try {
 329                         target = ((Exception) theThro).getClass().getMethod("getTargetError",
 330                                 (java.lang.Class<?>[]) null);
 331                     } catch (Exception ee) {
 332                     // OK: getTargetError method could be there or not
 333                     }
 334                     Throwable err = theThro;
 335                     while (target != null) {
 336                         try {
 337                             err = (Error) target.invoke(err,
 338                                     (java.lang.Object[]) null);
 339                         } catch (Exception ee) {
 340                             err = null;
 341                         }
 342                         try {
 343                             if (err != null) {
 344                                 blank = blank + "  ";
 345                                 System.out.println(blank + err.getClass() + "==>" +
 346                                         err.getMessage());
 347                                 if (withStack) {
 348                                     err.printStackTrace(System.out);
 349                                 }
 350                                 try {
 351                                     target = err.getClass().getMethod("getTargetError",
 352                                             (java.lang.Class<?>[]) null);
 353                                 } catch (Exception ee) {
 354                                 // OK: getTargetError method could be there or not
 355                                 }
 356                             } else {
 357                                 target = null;
 358                             }
 359                         } catch (Exception ee) {
 360                             target = null;
 361                         }
 362                     }
 363                 } else {
 364                     System.out.println("Throwable is : " + theThro);
 365                 }
 366             } catch (Throwable x) {
 367                 System.out.println("Exception : raised in printException : " + x);
 368             }
 369         }
 370     }
 371 
 372 }