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