1/*
2 * Copyright (c) 2005, 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 5106721
27 * @key intermittent
28 * @summary Check the emission of notifications when a Security Manager is
29 * installed. Test the property "jmx.remote.x.check.notification.emission".
30 * @author Luis-Miguel Alventosa
31 *
32 * @run clean NotificationEmissionTest
33 * @run build NotificationEmissionTest
34 * @run main NotificationEmissionTest 1
35 * @run main NotificationEmissionTest 2
36 * @run main NotificationEmissionTest 3
37 * @run main NotificationEmissionTest 4
38 * @run main NotificationEmissionTest 5
39 */
40
41import java.io.File;
42import java.util.ArrayList;
43import java.util.Collections;
44import java.util.HashMap;
45import java.util.List;
46import java.util.Map;
47import javax.management.MBeanServer;
48import javax.management.MBeanServerConnection;
49import javax.management.MBeanServerFactory;
50import javax.management.Notification;
51import javax.management.NotificationBroadcasterSupport;
52import javax.management.NotificationListener;
53import javax.management.ObjectName;
54import javax.management.remote.JMXAuthenticator;
55import javax.management.remote.JMXConnector;
56import javax.management.remote.JMXConnectorFactory;
57import javax.management.remote.JMXConnectorServer;
58import javax.management.remote.JMXConnectorServerFactory;
59import javax.management.remote.JMXPrincipal;
60import javax.management.remote.JMXServiceURL;
61import javax.security.auth.Subject;
62
63public class NotificationEmissionTest {
64
65    public class CustomJMXAuthenticator implements JMXAuthenticator {
66        public Subject authenticate(Object credentials) {
67            String role = ((String[]) credentials)[0];
68            echo("Create principal with name = " + role);
69            return new Subject(true,
70                               Collections.singleton(new JMXPrincipal(role)),
71                               Collections.EMPTY_SET,
72                               Collections.EMPTY_SET);
73        }
74    }
75
76    public interface NBMBean {
77        public void emitNotification(int seqnum, ObjectName name);
78    }
79
80    public static class NB
81        extends NotificationBroadcasterSupport
82        implements NBMBean {
83        public void emitNotification(int seqnum, ObjectName name) {
84            if (name == null) {
85                sendNotification(new Notification("nb", this, seqnum));
86            } else {
87                sendNotification(new Notification("nb", name, seqnum));
88            }
89        }
90    }
91
92    public class Listener implements NotificationListener {
93        public List<Notification> notifs = new ArrayList<Notification>();
94        public void handleNotification(Notification n, Object h) {
95            echo("handleNotification:");
96            echo("\tNotification = " + n);
97            echo("\tNotification.SeqNum = " + n.getSequenceNumber());
98            echo("\tHandback = " + h);
99            notifs.add(n);
100        }
101    }
102
103    public int checkNotifs(int size,
104                           List<Notification> received,
105                           List<ObjectName> expected) {
106        if (received.size() != size) {
107            echo("Error: expecting " + size + " notifications, got " +
108                    received.size());
109            return 1;
110        } else {
111            for (Notification n : received) {
112                echo("Received notification: " + n);
113                if (!n.getType().equals("nb")) {
114                    echo("Notification type must be \"nb\"");
115                    return 1;
116                }
117                ObjectName o = (ObjectName) n.getSource();
118                int index = (int) n.getSequenceNumber();
119                ObjectName nb = expected.get(index);
120                if (!o.equals(nb)) {
121                    echo("Notification source must be " + nb);
122                    return 1;
123                }
124            }
125        }
126        return 0;
127    }
128
129    public int runTest(int testcase) throws Exception {
130        echo("\n=-=-= Running testcase " + testcase + " =-=-=");
131        switch (testcase) {
132            case 1:
133                return testNotificationEmissionProperty();
134            case 2:
135                return testNotificationEmissionPositive(false);
136            case 3:
137                return testNotificationEmissionNegative(false);
138            case 4:
139                return testNotificationEmissionPositive(true);
140            case 5:
141                return testNotificationEmissionNegative(true);
142            default:
143                echo("Invalid testcase");
144                return 1;
145        }
146    }
147
148    public int testNotificationEmissionProperty(boolean exception,
149                                                Object propValue)
150        throws Exception {
151        try {
152            testNotificationEmission(propValue);
153            if (exception) {
154                echo("Did not get expected exception for value: " + propValue);
155                return 1;
156            } else {
157                echo("Property has been correctly set to value: " + propValue);
158            }
159        } catch (Exception e) {
160            if (exception) {
161                echo("Got expected exception for value: " + propValue);
162                echo("Exception: " + e);
163            } else {
164                echo("Got unexpected exception for value: " + propValue);
165                echo("Exception: " + e);
166                return 1;
167            }
168        }
169        return 0;
170    }
171
172    public int testNotificationEmissionProperty() throws Exception {
173        int error = 0;
174        error += testNotificationEmissionProperty(true, new Boolean(false));
175        error += testNotificationEmissionProperty(true, new Boolean(true));
176        error += testNotificationEmissionProperty(true, "dummy");
177        error += testNotificationEmissionProperty(false, "false");
178        error += testNotificationEmissionProperty(false, "true");
179        error += testNotificationEmissionProperty(false, "FALSE");
180        error += testNotificationEmissionProperty(false, "TRUE");
181        return error;
182    }
183
184    public int testNotificationEmissionPositive(boolean prop) throws Exception {
185        return testNotificationEmission(prop, "true", true, true);
186    }
187
188    public int testNotificationEmissionNegative(boolean prop) throws Exception {
189        return testNotificationEmission(prop, "true", true, false);
190    }
191
192    public int testNotificationEmission(Object propValue) throws Exception {
193        return testNotificationEmission(true, propValue, false, true);
194    }
195
196    public int testNotificationEmission(boolean prop,
197                                        Object propValue,
198                                        boolean sm,
199                                        boolean policyPositive)
200        throws Exception {
201
202        JMXConnectorServer server = null;
203        JMXConnector client = null;
204
205        // Set policy file
206        //
207        String policyFile =
208            System.getProperty("test.src") + File.separator +
209            (policyPositive ? "policy.positive" : "policy.negative");
210        echo("\nSetting policy file " + policyFile);
211        System.setProperty("java.security.policy", policyFile);
212
213        // Create a new MBeanServer
214        //
215        final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
216
217        try {
218            // Create server environment map
219            //
220            final Map<String,Object> env = new HashMap<String,Object>();
221            env.put("jmx.remote.authenticator", new CustomJMXAuthenticator());
222            if (prop)
223                env.put("jmx.remote.x.check.notification.emission", propValue);
224
225            // Create the JMXServiceURL
226            //
227            final JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
228
229            // Create a JMXConnectorServer
230            //
231            server = JMXConnectorServerFactory.newJMXConnectorServer(url,
232                                                                     env,
233                                                                     mbs);
234
235            // Start the JMXConnectorServer
236            //
237            server.start();
238
239            // Create server environment map
240            //
241            final Map<String,Object> cenv = new HashMap<String,Object>();
242            String[] credentials = new String[] { "role" , "password" };
243            cenv.put("jmx.remote.credentials", credentials);
244
245            // Create JMXConnector and connect to JMXConnectorServer
246            //
247            client = JMXConnectorFactory.connect(server.getAddress(), cenv);
248
249            // Get non-secure MBeanServerConnection
250            //
251            final MBeanServerConnection mbsc =
252                client.getMBeanServerConnection();
253
254            // Create NB MBean
255            //
256            ObjectName nb1 = ObjectName.getInstance("domain:type=NB,name=1");
257            ObjectName nb2 = ObjectName.getInstance("domain:type=NB,name=2");
258            ObjectName nb3 = ObjectName.getInstance("domain:type=NB,name=3");
259            mbsc.createMBean(NB.class.getName(), nb1);
260            mbsc.createMBean(NB.class.getName(), nb2);
261            mbsc.createMBean(NB.class.getName(), nb3);
262
263            // Add notification listener
264            //
265            Listener li = new Listener();
266            mbsc.addNotificationListener(nb1, li, null, null);
267            mbsc.addNotificationListener(nb2, li, null, null);
268
269            // Set security manager
270            //
271            if (sm) {
272                echo("Setting SM");
273                System.setSecurityManager(new SecurityManager());
274            }
275
276            // Invoke the "sendNotification" method
277            //
278            mbsc.invoke(nb1, "emitNotification",
279                new Object[] {0, null},
280                new String[] {"int", "javax.management.ObjectName"});
281            mbsc.invoke(nb2, "emitNotification",
282                new Object[] {1, null},
283                new String[] {"int", "javax.management.ObjectName"});
284            mbsc.invoke(nb2, "emitNotification",
285                new Object[] {2, nb3},
286                new String[] {"int", "javax.management.ObjectName"});
287
288            // If the check is effective and we're using policy.negative,
289            // then we should see the two notifs sent by nb2 (of which one
290            // has a getSource() that is nb3), but not the notif sent by nb1.
291            // Otherwise we should see all three notifs.  The check is only
292            // effective if the property jmx.remote.x.check.notification.emission
293            // is explicitly true and there is a security manager.
294            int expectedNotifs =
295                    (prop && sm && !policyPositive) ? 2 : 3;
296
297            // Wait for notifications to be emitted
298            //
299            long deadline = System.currentTimeMillis() + 2000;
300            while (li.notifs.size() < expectedNotifs &&
301                    System.currentTimeMillis() < deadline)
302                Thread.sleep(1);
303
304            // Remove notification listener
305            //
306            mbsc.removeNotificationListener(nb1, li);
307            mbsc.removeNotificationListener(nb2, li);
308
309            int result = 0;
310            List<ObjectName> sources = new ArrayList<ObjectName>();
311            sources.add(nb1);
312            sources.add(nb2);
313            sources.add(nb3);
314
315            result = checkNotifs(expectedNotifs, li.notifs, sources);
316            if (result > 0) {
317                echo("...SecurityManager=" + sm + "; policy=" + policyPositive);
318                return result;
319            }
320        } finally {
321            // Close the connection
322            //
323            if (client != null)
324                client.close();
325
326            // Stop the connector server
327            //
328            if (server != null)
329                server.stop();
330
331            // Release the MBeanServer
332            //
333            if (mbs != null)
334                MBeanServerFactory.releaseMBeanServer(mbs);
335        }
336
337        return 0;
338    }
339
340    private static void echo(String message) {
341        System.out.println(message);
342    }
343
344    public static void main(String[] args) throws Exception {
345
346        echo("\n--- Check the emission of notifications " +
347             "when a Security Manager is installed");
348
349        NotificationEmissionTest net = new NotificationEmissionTest();
350
351        int error = 0;
352
353        error += net.runTest(Integer.parseInt(args[0]));
354
355        if (error > 0) {
356            final String msg = "\nTest FAILED! Got " + error + " error(s)";
357            echo(msg);
358            throw new IllegalArgumentException(msg);
359        } else {
360            echo("\nTest PASSED!");
361        }
362    }
363}
364