57 import javax.management.AttributeNotFoundException; |
54 import javax.management.AttributeNotFoundException; |
58 import javax.management.InstanceAlreadyExistsException; |
55 import javax.management.InstanceAlreadyExistsException; |
59 import javax.management.InstanceNotFoundException; |
56 import javax.management.InstanceNotFoundException; |
60 import javax.management.IntrospectionException; |
57 import javax.management.IntrospectionException; |
61 import javax.management.InvalidAttributeValueException; |
58 import javax.management.InvalidAttributeValueException; |
62 import javax.management.JMX; |
|
63 import javax.management.ListenerNotFoundException; |
59 import javax.management.ListenerNotFoundException; |
64 import javax.management.MBeanException; |
60 import javax.management.MBeanException; |
65 import javax.management.MBeanInfo; |
61 import javax.management.MBeanInfo; |
66 import javax.management.MBeanRegistrationException; |
62 import javax.management.MBeanRegistrationException; |
67 import javax.management.MBeanServer; |
63 import javax.management.MBeanServer; |
68 import javax.management.NotCompliantMBeanException; |
64 import javax.management.NotCompliantMBeanException; |
69 import javax.management.Notification; |
|
70 import javax.management.NotificationFilter; |
65 import javax.management.NotificationFilter; |
71 import javax.management.ObjectInstance; |
66 import javax.management.ObjectInstance; |
72 import javax.management.ObjectName; |
67 import javax.management.ObjectName; |
73 import javax.management.QueryExp; |
68 import javax.management.QueryExp; |
74 import javax.management.ReflectionException; |
69 import javax.management.ReflectionException; |
75 import javax.management.RuntimeOperationsException; |
70 import javax.management.RuntimeOperationsException; |
76 import javax.management.event.EventClientDelegate; |
|
77 import javax.management.event.EventClientDelegateMBean; |
|
78 import javax.management.event.EventClientNotFoundException; |
|
79 import javax.management.event.FetchingEventForwarder; |
|
80 import javax.management.namespace.JMXNamespaces; |
|
81 import javax.management.remote.JMXServerErrorException; |
71 import javax.management.remote.JMXServerErrorException; |
82 import javax.management.remote.NotificationResult; |
72 import javax.management.remote.NotificationResult; |
83 import javax.management.remote.TargetedNotification; |
73 import javax.management.remote.TargetedNotification; |
84 import javax.security.auth.Subject; |
74 import javax.security.auth.Subject; |
85 |
75 |
1245 |
1245 |
1246 } |
1246 } |
1247 final long csn = clientSequenceNumber; |
1247 final long csn = clientSequenceNumber; |
1248 final int mn = maxNotifications; |
1248 final int mn = maxNotifications; |
1249 final long t = timeout; |
1249 final long t = timeout; |
1250 |
1250 PrivilegedAction<NotificationResult> action = |
1251 final PrivilegedExceptionAction<NotificationResult> action = |
1251 new PrivilegedAction<NotificationResult>() { |
1252 new PrivilegedExceptionAction<NotificationResult>() { |
1252 public NotificationResult run() { |
1253 public NotificationResult run() throws IOException { |
1253 return getServerNotifFwd().fetchNotifs(csn, t, mn); |
1254 return doFetchNotifs(csn, t, mn); |
|
1255 } |
1254 } |
1256 }; |
1255 }; |
1257 try { |
1256 if (acc == null) |
1258 if (acc == null) |
1257 return action.run(); |
1259 return action.run(); |
1258 else |
1260 else |
1259 return AccessController.doPrivileged(action, acc); |
1261 return AccessController.doPrivileged(action, acc); |
|
1262 } catch (IOException x) { |
|
1263 throw x; |
|
1264 } catch (RuntimeException x) { |
|
1265 throw x; |
|
1266 } catch (Exception x) { |
|
1267 // should not happen |
|
1268 throw new UndeclaredThrowableException(x); |
|
1269 } |
|
1270 |
|
1271 } finally { |
1260 } finally { |
1272 serverCommunicatorAdmin.rspOutgoing(); |
1261 serverCommunicatorAdmin.rspOutgoing(); |
1273 } |
1262 } |
1274 } |
1263 } |
1275 |
|
1276 /** |
|
1277 * This is an abstraction class that let us use the legacy |
|
1278 * ServerNotifForwarder and the new EventClientDelegateMBean |
|
1279 * indifferently. |
|
1280 **/ |
|
1281 private static interface SubscriptionManager { |
|
1282 public void removeNotificationListener(ObjectName name, Integer id) |
|
1283 throws InstanceNotFoundException, ListenerNotFoundException, IOException; |
|
1284 public void removeNotificationListener(ObjectName name, Integer[] ids) |
|
1285 throws Exception; |
|
1286 public NotificationResult fetchNotifications(long csn, long timeout, int maxcount) |
|
1287 throws IOException; |
|
1288 public Integer addNotificationListener(ObjectName name, NotificationFilter filter) |
|
1289 throws InstanceNotFoundException, IOException; |
|
1290 public void terminate() |
|
1291 throws IOException; |
|
1292 } |
|
1293 |
|
1294 /** |
|
1295 * A SubscriptionManager that uses a ServerNotifForwarder. |
|
1296 **/ |
|
1297 private static class LegacySubscriptionManager implements SubscriptionManager { |
|
1298 private final ServerNotifForwarder forwarder; |
|
1299 LegacySubscriptionManager(ServerNotifForwarder forwarder) { |
|
1300 this.forwarder = forwarder; |
|
1301 } |
|
1302 |
|
1303 public void removeNotificationListener(ObjectName name, Integer id) |
|
1304 throws InstanceNotFoundException, ListenerNotFoundException, |
|
1305 IOException { |
|
1306 if (!JMXNamespaces.getContainingNamespace(name).equals("")) { |
|
1307 logger.debug("removeNotificationListener", |
|
1308 "This connector server is not configured to support " + |
|
1309 "forwarding of notification subscriptions to name spaces"); |
|
1310 throw new RuntimeOperationsException( |
|
1311 new UnsupportedOperationException( |
|
1312 "removeNotificationListener on name space MBeans. ")); |
|
1313 } |
|
1314 forwarder.removeNotificationListener(name,id); |
|
1315 } |
|
1316 |
|
1317 public void removeNotificationListener(ObjectName name, Integer[] ids) |
|
1318 throws Exception { |
|
1319 if (!JMXNamespaces.getContainingNamespace(name).equals("")) { |
|
1320 logger.debug("removeNotificationListener", |
|
1321 "This connector server is not configured to support " + |
|
1322 "forwarding of notification subscriptions to name spaces"); |
|
1323 throw new RuntimeOperationsException( |
|
1324 new UnsupportedOperationException( |
|
1325 "removeNotificationListener on name space MBeans. ")); |
|
1326 } |
|
1327 forwarder.removeNotificationListener(name,ids); |
|
1328 } |
|
1329 |
|
1330 public NotificationResult fetchNotifications(long csn, long timeout, int maxcount) { |
|
1331 return forwarder.fetchNotifs(csn,timeout,maxcount); |
|
1332 } |
|
1333 |
|
1334 public Integer addNotificationListener(ObjectName name, |
|
1335 NotificationFilter filter) |
|
1336 throws InstanceNotFoundException, IOException { |
|
1337 if (!JMXNamespaces.getContainingNamespace(name).equals("")) { |
|
1338 logger.debug("addNotificationListener", |
|
1339 "This connector server is not configured to support " + |
|
1340 "forwarding of notification subscriptions to name spaces"); |
|
1341 throw new RuntimeOperationsException( |
|
1342 new UnsupportedOperationException( |
|
1343 "addNotificationListener on name space MBeans. ")); |
|
1344 } |
|
1345 return forwarder.addNotificationListener(name,filter); |
|
1346 } |
|
1347 |
|
1348 public void terminate() { |
|
1349 forwarder.terminate(); |
|
1350 } |
|
1351 } |
|
1352 |
|
1353 /** |
|
1354 * A SubscriptionManager that uses an EventClientDelegateMBean. |
|
1355 **/ |
|
1356 private static class EventSubscriptionManager |
|
1357 implements SubscriptionManager { |
|
1358 private final MBeanServer mbeanServer; |
|
1359 private final EventClientDelegateMBean delegate; |
|
1360 private final NotificationAccessController notifAC; |
|
1361 private final boolean checkNotificationEmission; |
|
1362 private final String clientId; |
|
1363 private final String connectionId; |
|
1364 private volatile String mbeanServerName; |
|
1365 |
|
1366 EventSubscriptionManager( |
|
1367 MBeanServer mbeanServer, |
|
1368 EventClientDelegateMBean delegate, |
|
1369 Map<String, ?> env, |
|
1370 String clientId, |
|
1371 String connectionId) { |
|
1372 this.mbeanServer = mbeanServer; |
|
1373 this.delegate = delegate; |
|
1374 this.notifAC = EnvHelp.getNotificationAccessController(env); |
|
1375 this.checkNotificationEmission = |
|
1376 EnvHelp.computeBooleanFromString( |
|
1377 env, "jmx.remote.x.check.notification.emission", false); |
|
1378 this.clientId = clientId; |
|
1379 this.connectionId = connectionId; |
|
1380 } |
|
1381 |
|
1382 private String mbeanServerName() { |
|
1383 if (mbeanServerName != null) return mbeanServerName; |
|
1384 else return (mbeanServerName = getMBeanServerName(mbeanServer)); |
|
1385 } |
|
1386 |
|
1387 @SuppressWarnings("serial") // no serialVersionUID |
|
1388 private class AccessControlFilter implements NotificationFilter { |
|
1389 private final NotificationFilter wrapped; |
|
1390 private final ObjectName name; |
|
1391 |
|
1392 AccessControlFilter(ObjectName name, NotificationFilter wrapped) { |
|
1393 this.name = name; |
|
1394 this.wrapped = wrapped; |
|
1395 } |
|
1396 |
|
1397 public boolean isNotificationEnabled(Notification notification) { |
|
1398 try { |
|
1399 if (checkNotificationEmission) { |
|
1400 ServerNotifForwarder.checkMBeanPermission( |
|
1401 mbeanServerName(), mbeanServer, name, |
|
1402 "addNotificationListener"); |
|
1403 } |
|
1404 notifAC.fetchNotification( |
|
1405 connectionId, name, notification, getSubject()); |
|
1406 return (wrapped == null) ? true : |
|
1407 wrapped.isNotificationEnabled(notification); |
|
1408 } catch (InstanceNotFoundException e) { |
|
1409 return false; |
|
1410 } catch (SecurityException e) { |
|
1411 return false; |
|
1412 } |
|
1413 } |
|
1414 |
|
1415 } |
|
1416 |
|
1417 public Integer addNotificationListener( |
|
1418 ObjectName name, NotificationFilter filter) |
|
1419 throws InstanceNotFoundException, IOException { |
|
1420 if (notifAC != null) { |
|
1421 notifAC.addNotificationListener(connectionId, name, getSubject()); |
|
1422 filter = new AccessControlFilter(name, filter); |
|
1423 } |
|
1424 try { |
|
1425 return delegate.addListener(clientId,name,filter); |
|
1426 } catch (EventClientNotFoundException x) { |
|
1427 throw new IOException("Unknown clientId: "+clientId,x); |
|
1428 } |
|
1429 } |
|
1430 |
|
1431 public void removeNotificationListener(ObjectName name, Integer id) |
|
1432 throws InstanceNotFoundException, ListenerNotFoundException, |
|
1433 IOException { |
|
1434 if (notifAC != null) |
|
1435 notifAC.removeNotificationListener(connectionId, name, getSubject()); |
|
1436 try { |
|
1437 delegate.removeListenerOrSubscriber(clientId, id); |
|
1438 } catch (EventClientNotFoundException x) { |
|
1439 throw new IOException("Unknown clientId: "+clientId,x); |
|
1440 } |
|
1441 } |
|
1442 |
|
1443 public void removeNotificationListener(ObjectName name, Integer[] ids) |
|
1444 throws InstanceNotFoundException, ListenerNotFoundException, |
|
1445 IOException { |
|
1446 if (notifAC != null) |
|
1447 notifAC.removeNotificationListener(connectionId, name, getSubject()); |
|
1448 try { |
|
1449 for (Integer id : ids) |
|
1450 delegate.removeListenerOrSubscriber(clientId, id); |
|
1451 } catch (EventClientNotFoundException x) { |
|
1452 throw new IOException("Unknown clientId: "+clientId,x); |
|
1453 } |
|
1454 } |
|
1455 |
|
1456 public NotificationResult fetchNotifications(long csn, long timeout, |
|
1457 int maxcount) |
|
1458 throws IOException { |
|
1459 try { |
|
1460 // For some reason the delegate doesn't accept a negative |
|
1461 // sequence number. However legacy clients will always call |
|
1462 // fetchNotifications with a negative sequence number, when |
|
1463 // they call it for the first time. |
|
1464 // In that case, we will use 0 instead. |
|
1465 // |
|
1466 return delegate.fetchNotifications( |
|
1467 clientId, Math.max(csn, 0), maxcount, timeout); |
|
1468 } catch (EventClientNotFoundException x) { |
|
1469 throw new IOException("Unknown clientId: "+clientId,x); |
|
1470 } |
|
1471 } |
|
1472 |
|
1473 public void terminate() |
|
1474 throws IOException { |
|
1475 try { |
|
1476 delegate.removeClient(clientId); |
|
1477 } catch (EventClientNotFoundException x) { |
|
1478 throw new IOException("Unknown clientId: "+clientId,x); |
|
1479 } |
|
1480 } |
|
1481 |
|
1482 private static Subject getSubject() { |
|
1483 return Subject.getSubject(AccessController.getContext()); |
|
1484 } |
|
1485 } |
|
1486 |
|
1487 /** |
|
1488 * Creates a SubscriptionManager that uses either the legacy notifications |
|
1489 * mechanism (ServerNotifForwarder) or the new event service |
|
1490 * (EventClientDelegateMBean) depending on which option was passed in |
|
1491 * the connector's map. |
|
1492 **/ |
|
1493 private SubscriptionManager createSubscriptionManager() |
|
1494 throws IOException { |
|
1495 if (EnvHelp.delegateToEventService(env) && |
|
1496 mbeanServer.isRegistered(EventClientDelegate.OBJECT_NAME)) { |
|
1497 final EventClientDelegateMBean mbean = |
|
1498 JMX.newMBeanProxy(mbeanServer, |
|
1499 EventClientDelegate.OBJECT_NAME, |
|
1500 EventClientDelegateMBean.class); |
|
1501 String clientId; |
|
1502 try { |
|
1503 clientId = |
|
1504 mbean.addClient( |
|
1505 FetchingEventForwarder.class.getName(), |
|
1506 new Object[] {EnvHelp.getNotifBufferSize(env)}, |
|
1507 new String[] {int.class.getName()}); |
|
1508 } catch (Exception e) { |
|
1509 if (e instanceof IOException) |
|
1510 throw (IOException) e; |
|
1511 else |
|
1512 throw new IOException(e); |
|
1513 } |
|
1514 |
|
1515 // we're going to call remove client... |
|
1516 try { |
|
1517 mbean.lease(clientId, Long.MAX_VALUE); |
|
1518 } catch (EventClientNotFoundException x) { |
|
1519 throw new IOException("Unknown clientId: "+clientId,x); |
|
1520 } |
|
1521 return new EventSubscriptionManager(mbeanServer, mbean, env, |
|
1522 clientId, connectionId); |
|
1523 } else { |
|
1524 final ServerNotifForwarder serverNotifForwarder = |
|
1525 new ServerNotifForwarder(mbeanServer, |
|
1526 env, |
|
1527 rmiServer.getNotifBuffer(), |
|
1528 connectionId); |
|
1529 return new LegacySubscriptionManager(serverNotifForwarder); |
|
1530 } |
|
1531 } |
|
1532 |
|
1533 /** |
|
1534 * Lazy creation of a SubscriptionManager. |
|
1535 **/ |
|
1536 private synchronized SubscriptionManager getSubscriptionManager() |
|
1537 throws IOException { |
|
1538 // Lazily created when first use. Mainly when |
|
1539 // addNotificationListener is first called. |
|
1540 |
|
1541 if (subscriptionManager == null) { |
|
1542 subscriptionManager = createSubscriptionManager(); |
|
1543 } |
|
1544 return subscriptionManager; |
|
1545 } |
|
1546 |
|
1547 // calls SubscriptionManager. |
|
1548 private void doRemoveListener(ObjectName name, Integer id) |
|
1549 throws InstanceNotFoundException, ListenerNotFoundException, |
|
1550 IOException { |
|
1551 getSubscriptionManager().removeNotificationListener(name,id); |
|
1552 } |
|
1553 |
|
1554 // calls SubscriptionManager. |
|
1555 private void doRemoveListener(ObjectName name, Integer[] ids) |
|
1556 throws Exception { |
|
1557 getSubscriptionManager().removeNotificationListener(name,ids); |
|
1558 } |
|
1559 |
|
1560 // calls SubscriptionManager. |
|
1561 private NotificationResult doFetchNotifs(long csn, long timeout, int maxcount) |
|
1562 throws IOException { |
|
1563 return getSubscriptionManager().fetchNotifications(csn, timeout, maxcount); |
|
1564 } |
|
1565 |
|
1566 // calls SubscriptionManager. |
|
1567 private Integer doAddListener(ObjectName name, NotificationFilter filter) |
|
1568 throws InstanceNotFoundException, IOException { |
|
1569 return getSubscriptionManager().addNotificationListener(name,filter); |
|
1570 } |
|
1571 |
|
1572 |
1264 |
1573 /** |
1265 /** |
1574 * <p>Returns a string representation of this object. In general, |
1266 * <p>Returns a string representation of this object. In general, |
1575 * the <code>toString</code> method returns a string that |
1267 * the <code>toString</code> method returns a string that |
1576 * "textually represents" this object. The result should be a |
1268 * "textually represents" this object. The result should be a |