| /* |
| * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * @test |
| * @bug 6515161 |
| * @summary checks the behaviour of mbeanServerConnection.removeNotificationListener |
| * operation when there is a exception thrown during removal |
| * @modules java.management |
| * @run main NoPermToRemoveTest |
| */ |
| |
| import java.lang.management.ManagementFactory; |
| import java.security.AllPermission; |
| import java.security.CodeSource; |
| import java.security.Permission; |
| import java.security.PermissionCollection; |
| import java.security.Permissions; |
| import java.security.Policy; |
| import java.security.ProtectionDomain; |
| import java.util.concurrent.Semaphore; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import javax.management.ListenerNotFoundException; |
| import javax.management.MBeanPermission; |
| import javax.management.MBeanServer; |
| import javax.management.MBeanServerConnection; |
| import javax.management.Notification; |
| import javax.management.NotificationBroadcasterSupport; |
| import javax.management.NotificationFilter; |
| import javax.management.NotificationListener; |
| import javax.management.ObjectName; |
| import javax.management.remote.JMXConnector; |
| import javax.management.remote.JMXConnectorFactory; |
| import javax.management.remote.JMXConnectorServer; |
| import javax.management.remote.JMXConnectorServerFactory; |
| import javax.management.remote.JMXServiceURL; |
| |
| public class NoPermToRemoveTest { |
| public static void main(String[] args) throws Exception { |
| Policy.setPolicy(new NoRemovePolicy()); |
| System.setSecurityManager(new SecurityManager()); |
| |
| JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///"); |
| MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); |
| ObjectName name = new ObjectName("foo:type=Sender"); |
| mbs.registerMBean(new Sender(), name); |
| JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer( |
| url, null, mbs); |
| cs.start(); |
| try { |
| JMXServiceURL addr = cs.getAddress(); |
| JMXConnector cc = JMXConnectorFactory.connect(addr); |
| MBeanServerConnection mbsc = cc.getMBeanServerConnection(); |
| SnoopListener listener = new SnoopListener(); |
| mbsc.addNotificationListener(name, listener, null, null); |
| mbsc.invoke(name, "send", null, null); |
| if (!listener.waitForNotification(60)) |
| throw new Exception("Did not receive expected notification"); |
| |
| try { |
| mbsc.removeNotificationListener(name, listener); |
| throw new Exception("RemoveNL did not get SecurityException"); |
| } catch (SecurityException e) { |
| System.out.println("removeNL got expected exception: " + e); |
| } |
| mbsc.invoke(name, "send", null, null); |
| if (!listener.waitForNotification(60)) { |
| int listenerCount = |
| (Integer) mbsc.getAttribute(name, "ListenerCount"); |
| System.out.println("Listener count: " + listenerCount); |
| if (listenerCount != 0) |
| throw new Exception("TEST FAILED"); |
| /* We did not receive the notification, but the MBean still |
| * has a listener coming from the connector server, which |
| * means the connector server still thinks there is a |
| * listener. If we retained the listener after the failing |
| * removeNL that would be OK, and if the listener were |
| * dropped by both client and server that would be OK too, |
| * but the inconsistency is not OK. |
| */ |
| } |
| cc.close(); |
| } finally { |
| cs.stop(); |
| } |
| } |
| |
| private static class SnoopListener implements NotificationListener { |
| private Semaphore sema = new Semaphore(0); |
| |
| public void handleNotification(Notification notification, Object handback) { |
| System.out.println("Listener got: " + notification); |
| sema.release(); |
| } |
| |
| boolean waitForNotification(int seconds) throws InterruptedException { |
| return sema.tryAcquire(seconds, TimeUnit.SECONDS); |
| } |
| } |
| |
| private static class NoRemovePolicy extends Policy { |
| public PermissionCollection getPermissions(CodeSource codesource) { |
| PermissionCollection pc = new Permissions(); |
| pc.add(new AllPermission()); |
| return pc; |
| } |
| |
| public void refresh() { |
| } |
| |
| public boolean implies(ProtectionDomain domain, Permission permission) { |
| if (!(permission instanceof MBeanPermission)) |
| return true; |
| MBeanPermission jmxp = (MBeanPermission) permission; |
| if (jmxp.getActions().contains("removeNotificationListener")) { |
| System.out.println("DENIED"); |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| public static interface SenderMBean { |
| public void send(); |
| public int getListenerCount(); |
| } |
| |
| public static class Sender extends NotificationBroadcasterSupport |
| implements SenderMBean { |
| private AtomicInteger listenerCount = new AtomicInteger(); |
| |
| public void send() { |
| System.out.println("Sending notif"); |
| sendNotification(new Notification("type", this, 0L)); |
| } |
| |
| public synchronized int getListenerCount() { |
| return listenerCount.get(); |
| } |
| |
| public void removeNotificationListener( |
| NotificationListener listener, |
| NotificationFilter filter, |
| Object handback) throws ListenerNotFoundException { |
| System.out.println("Sender.removeNL(3)"); |
| super.removeNotificationListener(listener, filter, handback); |
| listenerCount.decrementAndGet(); |
| } |
| |
| public void addNotificationListener( |
| NotificationListener listener, |
| NotificationFilter filter, |
| Object handback) { |
| System.out.println("Sender.addNL(3)"); |
| super.addNotificationListener(listener, filter, handback); |
| listenerCount.incrementAndGet(); |
| } |
| |
| public void removeNotificationListener(NotificationListener listener) |
| throws ListenerNotFoundException { |
| System.out.println("Sender.removeNL(1)"); |
| super.removeNotificationListener(listener); |
| listenerCount.decrementAndGet(); |
| } |
| } |
| } |