/*
* @(#)ServerNotifForwarder.java 1.45 03/12/19
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.sun.jmx.remote.internal;
import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.MBeanPermission;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationListener;
import javax.management.NotificationFilter;
import javax.management.MBeanServerNotification;
import javax.management.NotificationFilterSupport;
import javax.management.ListenerNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.remote.TargetedNotification;
import javax.management.remote.NotificationResult;
import javax.security.auth.Subject;
import com.sun.jmx.remote.util.ClassLogger;
import com.sun.jmx.remote.util.EnvHelp;
import com.sun.jmx.remote.internal.ListenerInfo;
public class ServerNotifForwarder {
public ServerNotifForwarder(MBeanServer mbeanServer,
Map env,
NotificationBuffer notifBuffer) {
this.mbeanServer = mbeanServer;
this.notifBuffer = notifBuffer;
connectionTimeout = EnvHelp.getServerConnectionTimeout(env);
}
public Integer addNotificationListener(final ObjectName name,
final NotificationFilter filter)
throws InstanceNotFoundException, IOException {
if (logger.traceOn()) {
logger.trace("addNotificationListener",
"Add a listener at " + name);
}
checkState();
// Explicitly check MBeanPermission for addNotificationListener
//
checkMBeanPermission(name, "addNotificationListener");
try {
Boolean instanceOf = (Boolean)
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws InstanceNotFoundException {
return new Boolean(
mbeanServer.isInstanceOf(name,
broadcasterClass));
}
});
if (!instanceOf.booleanValue()) {
throw new IllegalArgumentException("The specified MBean [" +
name + "] is not a " +
"NotificationBroadcaster " +
"object.");
}
} catch (PrivilegedActionException e) {
throw (InstanceNotFoundException) extractException(e);
}
final Integer id = getListenerID();
synchronized(listenerList) {
listenerList.add(new ListenerInfo(id, name, filter));
}
return id;
}
public void removeNotificationListener(ObjectName name,
Integer[] listenerIDs)
throws Exception {
if (logger.traceOn()) {
logger.trace("removeNotificationListener",
"Remove some listeners from " + name);
}
checkState();
// Explicitly check MBeanPermission for removeNotificationListener
//
checkMBeanPermission(name, "removeNotificationListener");
Exception re = null;
for (int i = 0 ; i < listenerIDs.length ; i++) {
try {
removeNotificationListener(name, listenerIDs[i]);
} catch (Exception e) {
// Give back the first exception
//
if (re != null) {
re = e;
}
}
}
if (re != null) {
throw re;
}
}
public void removeNotificationListener(ObjectName name, Integer listenerID)
throws
InstanceNotFoundException,
ListenerNotFoundException,
IOException {
if (logger.traceOn()) {
logger.trace("removeNotificationListener",
"Remove the listener " + listenerID + " from " + name);
}
checkState();
if (name != null && !name.isPattern()) {
if (!mbeanServer.isRegistered(name)) {
throw new InstanceNotFoundException("The MBean " + name +
" is not registered.");
}
}
synchronized(listenerList) {
if (!listenerList.remove(new ListenerInfo(listenerID,name,null))) {
throw new ListenerNotFoundException("Listener not found!");
}
}
}
public NotificationResult fetchNotifs(long startSequenceNumber,
long timeout,
int maxNotifications) {
if (logger.traceOn()) {
logger.trace("fetchNotifs", "Fetching notifications, the " +
"startSequenceNumber is " + startSequenceNumber +
", the timeout is " + timeout +
", the maxNotifications is " + maxNotifications);
}
NotificationResult nr = null;
final long t = Math.min(connectionTimeout, timeout);
try {
nr = notifBuffer.fetchNotifications(listenerList,
startSequenceNumber,
t, maxNotifications);
} catch (InterruptedException ire) {
nr = new NotificationResult(0L, 0L, new TargetedNotification[0]);
}
if (logger.traceOn()) {
logger.trace("fetchNotifs", "Forwarding the notifs: "+nr);
}
return nr;
}
public void terminate() {
if (logger.traceOn()) {
logger.trace("terminate", "Be called.");
}
synchronized(terminationLock) {
if (terminated) {
return;
}
terminated = true;
synchronized(listenerList) {
listenerList.clear();
}
}
if (logger.traceOn()) {
logger.trace("terminate", "Terminated.");
}
}
//----------------
// PRIVATE METHODS
//----------------
private void checkState() throws IOException {
synchronized(terminationLock) {
if (terminated) {
throw new IOException("The connection has been terminated.");
}
}
}
private Integer getListenerID() {
synchronized(listenerCounterLock) {
return new Integer(listenerCounter++);
}
}
/**
* Explicitly check the MBeanPermission for
* the current access control context.
*/
private void checkMBeanPermission(final ObjectName name,
final String actions)
throws InstanceNotFoundException, SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
AccessControlContext acc = AccessController.getContext();
ObjectInstance oi = null;
try {
oi = (ObjectInstance) AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run()
throws InstanceNotFoundException {
return mbeanServer.getObjectInstance(name);
}
});
} catch (PrivilegedActionException e) {
throw (InstanceNotFoundException) extractException(e);
}
String classname = oi.getClassName();
MBeanPermission perm = new MBeanPermission(classname,
null,
name,
actions);
sm.checkPermission(perm, acc);
}
}
/**
* Iterate until we extract the real exception
* from a stack of PrivilegedActionExceptions.
*/
private static Exception extractException(Exception e) {
while (e instanceof PrivilegedActionException) {
e = ((PrivilegedActionException)e).getException();
}
return e;
}
//------------------
// PRIVATE VARIABLES
//------------------
private MBeanServer mbeanServer;
private final long connectionTimeout;
private static int listenerCounter = 0;
private final static int[] listenerCounterLock = new int[0];
private NotificationBuffer notifBuffer;
private Set listenerList = new HashSet();
private boolean terminated = false;
private final int[] terminationLock = new int[0];
static final String broadcasterClass =
NotificationBroadcaster.class.getName();
private static final ClassLogger logger =
new ClassLogger("javax.management.remote.misc", "ServerNotifForwarder");
}
|