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