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 }