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  * @summary Check the emission of notifications when a Security Manager is
  28  * installed. Test the property "jmx.remote.x.check.notification.emission".
  29  * @author Luis-Miguel Alventosa
  30  * @key intermittent
  31  * @modules java.management.rmi
  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 
  41 import java.io.File;
  42 import java.util.ArrayList;
  43 import java.util.Collections;
  44 import java.util.HashMap;
  45 import java.util.List;
  46 import java.util.Map;
  47 import javax.management.MBeanServer;
  48 import javax.management.MBeanServerConnection;
  49 import javax.management.MBeanServerFactory;
  50 import javax.management.Notification;
  51 import javax.management.NotificationBroadcasterSupport;
  52 import javax.management.NotificationListener;
  53 import javax.management.ObjectName;
  54 import javax.management.remote.JMXAuthenticator;
  55 import javax.management.remote.JMXConnector;
  56 import javax.management.remote.JMXConnectorFactory;
  57 import javax.management.remote.JMXConnectorServer;
  58 import javax.management.remote.JMXConnectorServerFactory;
  59 import javax.management.remote.JMXPrincipal;
  60 import javax.management.remote.JMXServiceURL;
  61 import javax.security.auth.Subject;
  62 
  63 public 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 }