1/*
2 * Copyright (c) 2003, 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 4950756
27 * @summary Test that RequiredModelMBean.invoke will not invoke methods
28 * from the RequiredModelMBean class itself if they are not in the
29 * ModelMBeanInfo
30 * @author Eamonn McManus
31 *
32 * @run clean RequiredModelMBeanMethodTest
33 * @run build RequiredModelMBeanMethodTest
34 * @run main RequiredModelMBeanMethodTest
35 */
36
37import java.lang.reflect.*;
38import javax.management.*;
39import javax.management.modelmbean.*;
40
41/*
42 * We do the same test with a number of different operations:
43 *
44 * - A plain operation that is directed to the managed resource in the
45 * usual way.  We give it some parameters so we can test that the
46 * class loading logic for signature parameters is reasonable.
47 *
48 * - An operation (removeNotificationListener) that is directed to the
49 * RequiredModelMBean itself.  We use this particular operation because
50 * it will throw an exception, which allows us to check that it did in
51 * fact execute.
52 *
53 * - An operation (load()) that has the same signature as a
54 * RequiredModelMBean operation but is directed to the resource
55 * because of a "class" field in the descriptor.
56 *
57 * - An operation (store()) that has the same signature as a
58 * RequiredModelMBean operation but is directed to the resource
59 * because of a "targetObject" field in the descriptor.
60 *
61 * In each case we check that the operation does not work if it is not
62 * in the ModelMBeanInfo, and does work if it is.
63 */
64public class RequiredModelMBeanMethodTest {
65    public static void main(String[] args) throws Exception {
66        boolean ok = true;
67        MBeanServer mbs = MBeanServerFactory.createMBeanServer();
68
69        Descriptor tralalaDescriptor =
70            new DescriptorSupport(new String[] {
71                "name=tralala",
72                "descriptorType=operation",
73                "role=operation",
74                "targetType=ObjectReference",
75            });
76        Method tralalaMethod =
77            Resource.class.getMethod("tralala",
78                                     new Class[] {int.class, Resource.class});
79        ModelMBeanOperationInfo tralalaInfo =
80            new ModelMBeanOperationInfo("tralala descr", tralalaMethod,
81                                        tralalaDescriptor);
82
83        Method remACNLMethod =
84            RequiredModelMBean.class.getMethod("removeAttributeChangeNotificationListener",
85                                               new Class[] {
86                                                   NotificationListener.class,
87                                                   String.class
88                                               });
89        ModelMBeanOperationInfo remACNLInfo =
90            new ModelMBeanOperationInfo("remACNL descr", remACNLMethod);
91
92        Descriptor loadDescriptor =
93            new DescriptorSupport(new String[] {
94                "name=load",
95                "descriptorType=operation",
96                "role=operation",
97                "targetType=ObjectReference",
98                "class=" + Resource.class.getName(),
99            });
100        ModelMBeanOperationInfo loadInfo =
101            new ModelMBeanOperationInfo("load", "load descr",
102                                        new MBeanParameterInfo[0],
103                                        "void", ModelMBeanOperationInfo.ACTION,
104                                        loadDescriptor);
105
106        Descriptor storeDescriptor =
107            new DescriptorSupport(new String[] {
108                "name=store",
109                "descriptorType=operation",
110                "role=operation",
111                "targetType=ObjectReference",
112            });
113        storeDescriptor.setField("targetObject", resource);
114        ModelMBeanOperationInfo storeInfo =
115            new ModelMBeanOperationInfo("store", "store descr",
116                                        new MBeanParameterInfo[0],
117                                        "void", ModelMBeanOperationInfo.ACTION,
118                                        storeDescriptor);
119
120        ModelMBeanInfo emptyMMBI =
121            new ModelMBeanInfoSupport(Resource.class.getName(),
122                                      "empty descr",
123                                      null, null, null, null);
124        ModelMBean emptyMMB = new RequiredModelMBean(emptyMMBI);
125        emptyMMB.setManagedResource(resource, "ObjectReference");
126        ObjectName emptyMMBName = new ObjectName("test:type=Empty");
127        mbs.registerMBean(emptyMMB, emptyMMBName);
128
129        System.out.println("Testing that we cannot call methods not in the " +
130                           "ModelMBeanInfo");
131        try {
132            boolean thisok = test(mbs, emptyMMBName, false);
133            if (thisok)
134                System.out.println("...OK");
135            else
136                ok = false;
137        } catch (Exception e) {
138            System.out.println("TEST FAILED: Caught exception:");
139            e.printStackTrace(System.out);
140            ok = false;
141        }
142
143        ModelMBeanOperationInfo[] opInfos = {
144            tralalaInfo, remACNLInfo, loadInfo, storeInfo,
145        };
146        ModelMBeanInfo fullMMBI =
147            new ModelMBeanInfoSupport(Resource.class.getName(),
148                                      "full descr",
149                                      null, null, opInfos, null);
150        ModelMBean fullMMB = new RequiredModelMBean(fullMMBI);
151        fullMMB.setManagedResource(resource, "ObjectReference");
152        ObjectName fullMMBName = new ObjectName("test:type=Full");
153        mbs.registerMBean(fullMMB, fullMMBName);
154
155
156        System.out.println();
157        System.out.println("Testing that we can call methods in the " +
158                           "ModelMBeanInfo");
159        System.out.println("  and that \"class\" or \"targetObject\" in " +
160                           "descriptor directs methods to resource");
161        try {
162            boolean thisok = test(mbs, fullMMBName, true);
163            if (thisok)
164                System.out.println("...OK");
165            else
166                ok = false;
167        } catch (Exception e) {
168            System.out.println("TEST FAILED: Caught exception:");
169            e.printStackTrace(System.out);
170            ok = false;
171        }
172
173        if (ok) {
174            if (!resource.loadCalled || !resource.storeCalled) {
175                System.out.println("TEST FAILED: not called:" +
176                                   (resource.loadCalled ? "" : " load") +
177                                   (resource.storeCalled ? "" : " store"));
178                ok = false;
179            }
180        }
181
182        // Test the invoke("class.method") form
183        if (ok) {
184            System.out.println("Testing invoke(\"class.method\")");
185            resource.loadCalled = false;
186            mbs.invoke(fullMMBName, Resource.class.getName() + ".load",
187                       null, null);
188            if (!resource.loadCalled) {
189                System.out.println("TEST FAILED: load not called");
190                ok = false;
191            }
192            try {
193                mbs.invoke(fullMMBName,
194                           RequiredModelMBean.class.getName() +
195                           ".removeAttributeChangeNotificationListener",
196                           new Object[] {boringListener, null},
197                           new String[] {
198                                   NotificationListener.class.getName(),
199                                   String.class.getName(),
200                           });
201                System.out.println("TEST FAILED: removeNotificationListener" +
202                                   " returned successfully but " +
203                                   "should not have");
204                        ok = false;
205            } catch (MBeanException e) {
206                final Exception target = e.getTargetException();
207                if (target instanceof ListenerNotFoundException) {
208                    // OK: there is no such listener
209                } else
210                    throw e;
211            }
212        }
213
214        if (ok)
215            System.out.println("Test passed");
216        else {
217            System.out.println("TEST FAILED");
218            System.exit(1);
219        }
220    }
221
222    private static boolean test(MBeanServer mbs, ObjectName name,
223                                boolean shouldWork)
224            throws Exception {
225
226        boolean ok = true;
227
228        final String[] names = {
229            "tralala",
230            "removeAttributeChangeNotificationListener",
231            "load",
232            "store",
233        };
234
235        for (int i = 0; i < 4; i++) {
236            boolean thisok = true;
237            try {
238                switch (i) {
239                case 0:
240                    String tralala = (String)
241                        mbs.invoke(name, names[i],
242                                   new Object[] {new Integer(5), resource},
243                                   new String[] {"int",
244                                                 Resource.class.getName()});
245                    if (!"tralala".equals(tralala)) {
246                        System.out.println("TEST FAILED: tralala returned: " +
247                                           tralala);
248                        thisok = false;
249                    }
250                    break;
251                case 1:
252                    try {
253                        mbs.invoke(name,
254                                   names[i],
255                                   new Object[] {boringListener, null},
256                                   new String[] {
257                                       NotificationListener.class.getName(),
258                                       String.class.getName(),
259                                   });
260                        System.out.println("TEST FAILED: " + names[i] +
261                                           " returned successfully but " +
262                                           "should not have");
263                        thisok = false;
264                    } catch (MBeanException e) {
265                        final Exception target = e.getTargetException();
266                        if (target instanceof ListenerNotFoundException) {
267                            // OK: there is no such listener
268                        } else
269                            throw e;
270                    }
271                    break;
272                case 2:
273                case 3:
274                    mbs.invoke(name,
275                               names[i],
276                               new Object[0],
277                               new String[0]);
278                    break;
279                default:
280                    throw new AssertionError();
281                }
282
283                thisok = shouldWork;
284                if (!shouldWork) {
285                    System.out.println("TEST FAILED: " + names[i] +
286                                       " worked but should not");
287                }
288            } catch (MBeanException e) {
289                if (shouldWork) {
290                    System.out.println("TEST FAILED: " + names[i] + ": " + e);
291                    e.printStackTrace(System.out);
292                    thisok = false;
293                } else {
294                    Exception target = e.getTargetException();
295                    if (!(target instanceof ServiceNotFoundException)) {
296                        System.out.println("TEST FAILED: " + names[i] +
297                                           ": wrong exception: " + target);
298                        thisok = false;
299                    }
300                }
301            } catch (Exception e) {
302                System.out.println("TEST FAILED: " + names[i] + ": " + e);
303                e.printStackTrace(System.out);
304                thisok = false;
305            }
306
307            if (thisok)
308                System.out.println("OK: " + names[i]);
309            else
310                ok = false;
311        }
312
313        return ok;
314    }
315
316    public static class Resource {
317        public String tralala(int x, Resource y) {
318            if (x != 5 || y != this)
319                return "wrong params: " + x + " " + y;
320            return "tralala";
321        }
322
323        public void load() {
324            loadCalled = true;
325        }
326
327        public void store() {
328            storeCalled = true;
329        }
330
331        boolean loadCalled, storeCalled;
332    }
333
334    private static Resource resource = new Resource();
335
336    private static NotificationListener boringListener =
337        new NotificationListener() {
338            public void handleNotification(Notification n, Object h) {
339            }
340        };
341}
342