1. /*
  2. * @(#)ServerNotifForwarder.java 1.45 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.jmx.remote.internal;
  8. import java.io.IOException;
  9. import java.security.AccessControlContext;
  10. import java.security.AccessController;
  11. import java.security.PrivilegedAction;
  12. import java.security.PrivilegedActionException;
  13. import java.security.PrivilegedExceptionAction;
  14. import java.util.Set;
  15. import java.util.HashSet;
  16. import java.util.Map;
  17. import javax.management.ObjectInstance;
  18. import javax.management.ObjectName;
  19. import javax.management.MBeanPermission;
  20. import javax.management.MBeanServer;
  21. import javax.management.Notification;
  22. import javax.management.NotificationBroadcaster;
  23. import javax.management.NotificationListener;
  24. import javax.management.NotificationFilter;
  25. import javax.management.MBeanServerNotification;
  26. import javax.management.NotificationFilterSupport;
  27. import javax.management.ListenerNotFoundException;
  28. import javax.management.InstanceNotFoundException;
  29. import javax.management.remote.TargetedNotification;
  30. import javax.management.remote.NotificationResult;
  31. import javax.security.auth.Subject;
  32. import com.sun.jmx.remote.util.ClassLogger;
  33. import com.sun.jmx.remote.util.EnvHelp;
  34. import com.sun.jmx.remote.internal.ListenerInfo;
  35. public class ServerNotifForwarder {
  36. public ServerNotifForwarder(MBeanServer mbeanServer,
  37. Map env,
  38. NotificationBuffer notifBuffer) {
  39. this.mbeanServer = mbeanServer;
  40. this.notifBuffer = notifBuffer;
  41. connectionTimeout = EnvHelp.getServerConnectionTimeout(env);
  42. }
  43. public Integer addNotificationListener(final ObjectName name,
  44. final NotificationFilter filter)
  45. throws InstanceNotFoundException, IOException {
  46. if (logger.traceOn()) {
  47. logger.trace("addNotificationListener",
  48. "Add a listener at " + name);
  49. }
  50. checkState();
  51. // Explicitly check MBeanPermission for addNotificationListener
  52. //
  53. checkMBeanPermission(name, "addNotificationListener");
  54. try {
  55. Boolean instanceOf = (Boolean)
  56. AccessController.doPrivileged(new PrivilegedExceptionAction() {
  57. public Object run() throws InstanceNotFoundException {
  58. return new Boolean(
  59. mbeanServer.isInstanceOf(name,
  60. broadcasterClass));
  61. }
  62. });
  63. if (!instanceOf.booleanValue()) {
  64. throw new IllegalArgumentException("The specified MBean [" +
  65. name + "] is not a " +
  66. "NotificationBroadcaster " +
  67. "object.");
  68. }
  69. } catch (PrivilegedActionException e) {
  70. throw (InstanceNotFoundException) extractException(e);
  71. }
  72. final Integer id = getListenerID();
  73. synchronized(listenerList) {
  74. listenerList.add(new ListenerInfo(id, name, filter));
  75. }
  76. return id;
  77. }
  78. public void removeNotificationListener(ObjectName name,
  79. Integer[] listenerIDs)
  80. throws Exception {
  81. if (logger.traceOn()) {
  82. logger.trace("removeNotificationListener",
  83. "Remove some listeners from " + name);
  84. }
  85. checkState();
  86. // Explicitly check MBeanPermission for removeNotificationListener
  87. //
  88. checkMBeanPermission(name, "removeNotificationListener");
  89. Exception re = null;
  90. for (int i = 0 ; i < listenerIDs.length ; i++) {
  91. try {
  92. removeNotificationListener(name, listenerIDs[i]);
  93. } catch (Exception e) {
  94. // Give back the first exception
  95. //
  96. if (re != null) {
  97. re = e;
  98. }
  99. }
  100. }
  101. if (re != null) {
  102. throw re;
  103. }
  104. }
  105. public void removeNotificationListener(ObjectName name, Integer listenerID)
  106. throws
  107. InstanceNotFoundException,
  108. ListenerNotFoundException,
  109. IOException {
  110. if (logger.traceOn()) {
  111. logger.trace("removeNotificationListener",
  112. "Remove the listener " + listenerID + " from " + name);
  113. }
  114. checkState();
  115. if (name != null && !name.isPattern()) {
  116. if (!mbeanServer.isRegistered(name)) {
  117. throw new InstanceNotFoundException("The MBean " + name +
  118. " is not registered.");
  119. }
  120. }
  121. synchronized(listenerList) {
  122. if (!listenerList.remove(new ListenerInfo(listenerID,name,null))) {
  123. throw new ListenerNotFoundException("Listener not found!");
  124. }
  125. }
  126. }
  127. public NotificationResult fetchNotifs(long startSequenceNumber,
  128. long timeout,
  129. int maxNotifications) {
  130. if (logger.traceOn()) {
  131. logger.trace("fetchNotifs", "Fetching notifications, the " +
  132. "startSequenceNumber is " + startSequenceNumber +
  133. ", the timeout is " + timeout +
  134. ", the maxNotifications is " + maxNotifications);
  135. }
  136. NotificationResult nr = null;
  137. final long t = Math.min(connectionTimeout, timeout);
  138. try {
  139. nr = notifBuffer.fetchNotifications(listenerList,
  140. startSequenceNumber,
  141. t, maxNotifications);
  142. } catch (InterruptedException ire) {
  143. nr = new NotificationResult(0L, 0L, new TargetedNotification[0]);
  144. }
  145. if (logger.traceOn()) {
  146. logger.trace("fetchNotifs", "Forwarding the notifs: "+nr);
  147. }
  148. return nr;
  149. }
  150. public void terminate() {
  151. if (logger.traceOn()) {
  152. logger.trace("terminate", "Be called.");
  153. }
  154. synchronized(terminationLock) {
  155. if (terminated) {
  156. return;
  157. }
  158. terminated = true;
  159. synchronized(listenerList) {
  160. listenerList.clear();
  161. }
  162. }
  163. if (logger.traceOn()) {
  164. logger.trace("terminate", "Terminated.");
  165. }
  166. }
  167. //----------------
  168. // PRIVATE METHODS
  169. //----------------
  170. private void checkState() throws IOException {
  171. synchronized(terminationLock) {
  172. if (terminated) {
  173. throw new IOException("The connection has been terminated.");
  174. }
  175. }
  176. }
  177. private Integer getListenerID() {
  178. synchronized(listenerCounterLock) {
  179. return new Integer(listenerCounter++);
  180. }
  181. }
  182. /**
  183. * Explicitly check the MBeanPermission for
  184. * the current access control context.
  185. */
  186. private void checkMBeanPermission(final ObjectName name,
  187. final String actions)
  188. throws InstanceNotFoundException, SecurityException {
  189. SecurityManager sm = System.getSecurityManager();
  190. if (sm != null) {
  191. AccessControlContext acc = AccessController.getContext();
  192. ObjectInstance oi = null;
  193. try {
  194. oi = (ObjectInstance) AccessController.doPrivileged(
  195. new PrivilegedExceptionAction() {
  196. public Object run()
  197. throws InstanceNotFoundException {
  198. return mbeanServer.getObjectInstance(name);
  199. }
  200. });
  201. } catch (PrivilegedActionException e) {
  202. throw (InstanceNotFoundException) extractException(e);
  203. }
  204. String classname = oi.getClassName();
  205. MBeanPermission perm = new MBeanPermission(classname,
  206. null,
  207. name,
  208. actions);
  209. sm.checkPermission(perm, acc);
  210. }
  211. }
  212. /**
  213. * Iterate until we extract the real exception
  214. * from a stack of PrivilegedActionExceptions.
  215. */
  216. private static Exception extractException(Exception e) {
  217. while (e instanceof PrivilegedActionException) {
  218. e = ((PrivilegedActionException)e).getException();
  219. }
  220. return e;
  221. }
  222. //------------------
  223. // PRIVATE VARIABLES
  224. //------------------
  225. private MBeanServer mbeanServer;
  226. private final long connectionTimeout;
  227. private static int listenerCounter = 0;
  228. private final static int[] listenerCounterLock = new int[0];
  229. private NotificationBuffer notifBuffer;
  230. private Set listenerList = new HashSet();
  231. private boolean terminated = false;
  232. private final int[] terminationLock = new int[0];
  233. static final String broadcasterClass =
  234. NotificationBroadcaster.class.getName();
  235. private static final ClassLogger logger =
  236. new ClassLogger("javax.management.remote.misc", "ServerNotifForwarder");
  237. }