640a2afda3
Co-authored-by: Lance Andersen <lancea@openjdk.org> Co-authored-by: Weijun Wang <weijun@openjdk.org> Reviewed-by: dholmes, alanb, dfuchs, mchung, mullan, prr
188 lines
7.4 KiB
Java
188 lines
7.4 KiB
Java
/*
|
|
* 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/othervm -Djava.security.manager=allow 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();
|
|
}
|
|
}
|
|
}
|