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
33import java.util.Map;
34import java.util.HashMap;
35import java.util.Properties;
36import java.lang.reflect.Method;
37
38import java.lang.management.ManagementFactory;
39import javax.management.ObjectName;
40import javax.management.MBeanServer;
41import javax.management.MBeanServerConnection;
42import javax.management.remote.JMXConnector;
43import javax.management.remote.JMXConnectorFactory;
44import javax.management.remote.JMXConnectorServer;
45import javax.management.remote.JMXConnectorServerFactory;
46import javax.management.remote.JMXServiceURL;
47
48
49public 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}
371