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