【翻译JMX1.4】III JMX Remote API Specification(持续修改)

第三部分 JMX远程API规范

13.连接器(Connectors)

JMX规范定义了连接器的概念。连接器连接到JMX API MBean服务器,并使其可以通过远程Java技术进行访问客户端。连接器的客户端与本地导出基本相同的接口MBean服务器。

连接器由连接器客户端和连接器服务器组成。

连接器服务器连接到MBean服务器并侦听连接来自客户的要求。

连接器客户端负责查找服务器并建立连接它。连接器客户端通常位于不同的Java虚拟机1(JVMTM)中从连接器服务器,并将经常在不同的机器上运行。

给定的连接器服务器可以建立许多不同的并发连接客户端。

给定的连接器客户端只连接到一个连接器服务器。一个客户应用程序可以包含许多连接到不同连接器的连接器客户服务器。给定客户端应用程序之间可以有多个连接和一个给定的服务器。

连接器的许多不同的实现是可能的。特别是有用于通过连接进行通信的协议有许多可能性客户端和服务器。该标准定义了基于Remote的标准协议方法调用(RMI)必须由每个符合性支持实现。它还定义了一个直接基于TCP套接字的可选协议,称为JMX消息传递协议(JMXMP)。本标准的实施可以省略JMXMP连接器。

13.1会话和连接(Sessions and Connections)

会话和连接有区别。 连接器客户端看到一个会话。 在该会话的整个生命周期中,可以有许多连续的连接到连接器服务器。 在极端情况下,每个客户端可能有一个连接请求,例如,如果连接器使用诸如用户的无状态传输数据报协议(UDP)或Java消息服务(JMS)。

会话在客户端具有状态,特别是其听众(参见第13.4节“添加”远程监听器“)。 会话不一定具有状态服务器,并且对于本规范定义的两个连接器,它不会。

连接不一定在客户端或服务器上有状态,但对于这里定义了两个连接器。

……
FIGURE 13-1 A Session Can Contain Many Successive Connections

13.2连接建立(Connection Establishment)

在图13-2中,连接器客户端通过地址连接到连接器服务器“service:jmx:jmxmp://host1:9876”。 成功连接请求返回连接到客户端的客户端。
……
FIGURE 13-2 Connector Client and Server Communicate to Make a Connection

13.3通过连接执行MBean服务器操作(MBean Server Operations Through a Connection)

从连接的客户端,用户代码可以获取实现的对象MBeanServerConnection接口。 这个界面非常类似于用户代码将用于与MBean服务器交互的MBeanServer接口如果它运行在同一个Java虚拟机中。

MBeanServerConnection是MBeanServer的父接口。 它包含所有除少数方法适用于本地外,其他方法相同访问MBean服务器。 MBeanServerConnection中的所有方法除了声明的异常外,还在它们的“throws”子句中声明IOException在MBeanServer中。

由于MBeanServer扩展了MBeanServerConnection,客户端代码可以是无论它是在本地MBean服务器上运行还是运行在同一服务器上通过连接器的远程MBean服务器。

在图13-3中,操作getMBeanInfo(“a:b = c”)将远程客户端中的MBeanServerConnection转换为getMBeanInfo请求通过连接器协议发送到连接的服务器端。服务器通过执行相应的操作来响应此请求本地MBean服务器,并将结果发送回客户端。 如果操作成功,客户端的getMBeanInfo调用正常返回。 如果操作产生一个异常,连接器安排客户端的getMBeanInfo调用收到相同的异常。 如果在通讯中出现问题请求,客户端的getMBeanInfo调用将获得IOException。

……
FIGURE 13-3 An Operation on the Client Results in the Same Operation on the MBean Server

13.4添加远程监听器(Adding Remote Listeners)

MBeanServerConnection接口中的一个操作是addNotificationListener操作。 就像本地的情况一样,这个操作为由命名MBean发出的通知注册侦听器。 连接器会安排将通知从连接的服务器端发送到服务器客户端,并从那里到听众。

通知发送方式的细节取决于连接器协议。 他们俩本规范中定义的连接器使用无状态通知缓冲区,如第210页第13.4.3节“通知缓冲区”中所述。

13.4.1过滤器和回传(Filters and Handbacks)

MBeanServerConnection中的addNotificationListener方法接口有四个参数:对象名称,侦听器,过滤器和handback。对象名称指定将侦听器添加到哪个MBean。听众是在通知发生时将调用handleNotification方法的对象由MBean发出。如第13.4节“添加远程监听器”中所述这个监听器对象是客户端本地的。

可选过滤器选择此侦听器感兴趣的通知。给定连接器可以在通知到达客户端时执行过滤器,或者它可以将过滤器发送到服务器以在那里执行。执行过滤器服务器效率更高,因为它避免了通过发送通知网络只有在到达时才将其丢弃。过滤器的设计应该使它们能够在客户端或服务器上运行。特别是,过器应该是服务器已知的可序列化类的实例。第13.11节“类正在加载“(第218页),更详细地介绍了类加载。

此标准定义的连接器在服务器上执行过滤器。要强制在客户端上完成筛选,可以将筛选逻辑移动到监听器。

addNotificationListener的可选handback参数是任意的当通知到达时将会给予侦听器的对象。这允许相同的侦听器对象要注册几个MBean。可以使用handback在通知到达时确定适当的上下文。手里拿着对象保留在客户端上 - 它不会传输到服务器,也不需要可序列化。

MBeanServerConnection接口也有一个将监听器指定为ObjectName的addNotificationListener变体,另一个将接收通知的MBean的名称。有了这个变体,过滤器和handback被发送到远程服务器。

13.4.2删除监听器(Removing Listeners)

通常,添加了以下方法的侦听器是唯一的由三元组(由听众,过滤器,handback)为给定名称标识:

addNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback)

随后可以使用双参数将其删除removeNotificationListener,指定只是侦听器,或与四个参数removeNotificationListener具有相同的参数。

在远程情况下,四参数方法会出现问题。过滤器在removeNotificationListener方法中反序列化的对象不是通常与被反序列化的过滤器对象相同是addNotificationListener。由于通知广播者MBeans通常会检查在(听众,过滤器,回传)三重身份使用身份而不是三等同等于方法,通常不可能只删除一个(监听器,过滤器,handback)三重远程。

标准连接器通过使用侦听器标识符避免了这个问题。当一个连接器客户端向MBean(连接器)添加(侦听器,筛选器,手动)三元组服务器在该MBean上返回该三元组的唯一标识符。当连接器客户随后想要删除三元组,它使用标识符而不是传递三元组本身。要实现双参数removeNotificationListener表单,连接器客户机将查找所有三元组它具有相同的侦听器并发送removeNotificationListener请求与每个人的听众标识符。

这种技术具有远程客户端甚至可以从中删除三元组的副作用实现NotificationBroadcaster的MBean,但不是NotificationEmitter中。 MBeanServer接口的本地客户端无法完成此操作这个。

13.4.3通知缓冲区(Notification Buffer)

本规范定义的两个连接器将通知和侦听器处理为如下。每个连接器服务器都有通知缓冲区。从概念上讲,这是一个列表MBean服务器中任何MBean发出的连接器发出的每个通知服务器已连接到。在实践中,这个列表的大小是有限的,所以必要时最老通知被丢弃。

通知缓冲区中的条目由一个通知对象和一个对象名。 ObjectName是发出的MBean的名称通知。

对于每个可以发送通知的MBean(实现NotificationBroadcaster接口),连接器服务器注册一个监听器它将每个通知添加到通知缓冲区。连接器服务器跟踪创建MBean,以及何时新的NotificationBroadcaster MBean创建,监听器被添加到它。

通知缓冲区中的条目具有序列号。序列号是正。后面的通知总是比先前的序列号更大一。序列号不一定是连续的,而是通知缓冲区总是知道下一个序号是什么。

图13-4显示了一个连接器服务器及其通知缓冲区。通知缓冲区已保存四个通知,序号为40至43.下一个通知将具有序列号44。

会话的客户端状态包含下一个通知的序列号客户还没有看到。在图13-4中,会话1的客户尚未见过通知从第41开始。第2次会话的客户已经看到了全部通知,所以下一个通知将会看到下一个可用的序列数字,44。

……
FIGURE 13-4 Notification Buffer Saves Notifications From All MBeans

当一个新的会话被创建时,客户端会询问下一个序列号使用。 它只对具有该数字或更大数量的通知感兴趣,而不是任意旧的通知已经存在。

通知缓冲区没有与连接器服务器相关的状态。 所以实现可以为多个连接器使用相同的通知缓冲区服务器。

13.4.4从通知缓冲区获取通知(Getting Notifications From the Notification Buffer)

从概念上讲,连接器客户端通过发送提取通知来接收通知请求连接器服务器。 请求看起来像这样:
“给我的通知以匹配的序列号开头我的过滤器。“
这里是客户期望看到的下一个序列号。 在图13-4中,is41会议1和44会议2。

“我的过滤器”表示每个对象的ObjectName和NotificationFilter值已在连接器客户端上完成的addNotificationListener操作。此过滤器列表或者与每个提取通知请求一起发送,或者保存为连接状态的一部分。 两种方案都遵循后一种方法由此规范定义,因为过滤器列表可能非常大。

提取通知请求将一直等到满足以下条件之一:

  • 缓冲区中至少有一个符合客户标准的通知,即至少有一个序列号并与客户端的过滤器匹配。
  • 达到客户端指定的超时时间。
  • 连接器服务器决定终止操作,通常是因为a它自己的超时。

提取通知呼叫的结果包含以下信息:

  • 符合客户标准的零个或多个通知。 结果没有必须包含所有可用的通知。 它可能被限制为最大值数字,例如。 但如果有通知,他们将是最早的可用的。
  • 序号,即客户在下一次胎儿切除术中应使用的编号呼叫。
    这是匹配的第一个通知的序列号调用者的标准,但未包含在结果中,或者是下一个可用的如果包含所有匹配的通知,则为序列号。
  • 通知仍然是最小序列号的序列号在缓冲区中。 如果,客户可能感兴趣的通知已经丢失。
    然而,这并不确定,因为和之间的通知可能没有匹配调用者的过滤器。

这些信息封装在API的NotificationResult类中。作为例子,假设在图13-4中,通知41和43符合会话1的过滤器。它的fetch-notifications调用将会返回立即通知41和43,和。 没有通知()和下一次获取通知将会有。

13.5并发性(Concurrency)

JMX Remote API连接器必须支持并发请求。 如果一个线程调用一个可能很慢的操作,比如在连接器的客户端调用,另一个在执行之前,线程不应该被迫等待该操作完成一个手术。

13.6正常终止(Normal Termination)

会话的任何一端都可以随时终止会话。

如果客户端终止会话,服务器将清除与此相关的任何状态客户。 如果在客户端终止时正在进行客户端操作会话,那么调用它们的线程将收到IOException。

如果服务器终止会话,则客户端将为任何远程程序获得IOException正在进行的操作以及随后尝试的任何远程操作。

没有指定在何时运行的MBean服务器操作会发生什么情况导致它们的远程会话被关闭。 通常情况下,他们会跑完成,因为通常没有可靠的方法来阻止它们。

13.7异常终止(Abnormal Termination)

会话的客户端可以检测到服务器端异常终止。这可能会发生,例如因为服务器是JVM软件在退出时运行,或者因为它正在运行的计算机崩溃。连接器协议(或其底层传输)也可能确定服务器是无法访问,因为它的通信在一段时间内没有成功时间。如果有物理或配置问题,可能会发生这种情况网络。

在所有这些情况下,客户端可以终止会话。代码看到的行为使用客户端应该与服务器终止会话相同通常情况下,除了客户看到的例外情况的细节可能不同。

同样,会话的服务器端或会话内的连接可以检测到客户端异常终止或无法访问。这应该表现得好像客户端正常地终止了连接一样,除了连接终止的通知表明失败。

13.7.1检测异常终止(Detecting Abnormal Termination)

传输协议(如TCP)通常具有内置的异常检测功能终止。 当Java虚拟机退出时,它具有的任何TCP连接由TCP协议明确关闭,意味着连接的另一端会立即通知连接已关闭。 但是当一台机器崩溃或网络连接失败,则检测不到及时。 例如,TCP只会注意到如果尝试写入连接,连接就会中断,即使如此,它通常只会在订单超时后发出信号分钟。 连接器应该关闭接收错误的连接,但是如果连接大多处于空闲状态或检测到时间,则需要额外的机制连接失败太长。

对于本规范定义的两个连接器,实现不是需要及时发现故障。 但是,以下方法是推荐的:

  1. .来自客户端的提取通知调用应该以零结束如果没有在一定时间内到达通知。
  2. .连接器服务器应关闭尚未收到任何客户端的连接请求(包括提取通知)一段时间。
  3. .客户端应在每个提取通知调用中指定超时。 如果通话没有在超时后返回(加上一些延迟保证金),客户应该关闭连接。

这种方法是基于这样的假设,即客户总是会做一个新的技术在前一次返回后不久打电话。 所以情况2从来没有发生过工作连接。

如果会话没有监听器,则不需要执行提取通知调用。 在在这种情况下,遵循此处详述的方法的服务器将关闭空闲连接。客户端将在下次需要对其执行操作时重新打开该连接。

13.8连接器服务器地址(Connector Server Addresses)

连接器服务器通常有一个地址,客户可以使用该地址来建立连接到连接器服务器。 一些连接器可以提供替代方式建立连接,如通过连接存根(见第13.9.2节)“连接桩”(第215页))。

当连接器服务器有一个地址时,这个地址通常由类JMXServiceURL。 该类和标准的API文档连接器解释了这些地址的语义。

用户定义的连接器可以选择使用另一种地址格式,但它是建议尽可能使用JMXServiceURL。

下面显示了连接器服务器地址的示例:service:jmx:jmxmp://host1:9876

所有的JMXServiceURL地址都以“service:jmx:”开头。 以下jmxmp指示要使用的连接器,在本例中为JMXMP连接器(请参阅第15章“通用连接器”)。 host1和9876分别是主机和端口连接器服务器正在监听。

13.9创建连接器客户端(Creating a Connector Client)

连接器客户端由实现JMXConnector的对象表示接口。 有两种方法可以创建连接器客户端:

  • 使用地址,如第13.9.1节“JMXConnectorFactory”中所述第215页
  • 使用连接存根,如第13.9.2节“连接存根”所述第215页

应用程序使用哪种方式主要取决于用于的基础架构找到客户端想要连接的连接器服务器。

13.9.1 JMXConnectorFactory

如果客户端具有它所连接的连接器服务器的地址(JMXServiceURL)想要连接,它可以使用JMXConnectorFactory进行连接。当客户端通过基于文本的方式找到服务器时,这是常用的技术发现或目录服务,如SLP。
例如,包含MBean服务器的应用程序app1可能导出该应用程序服务器到远程管理器如下:
1. .创建一个连接器服务器cServer
2.获取cServer的地址addr,或者使用的是JMXServiceURL提供给它的构造函数来告诉它要使用什么地址或通过调用cServer.getAddress()
例如,将地址放在管理应用程序可以找到的地方在目录或SLP服务代理中
经理可以开始管理app1,如下所示:
1.从上面第3步中存储的地址中检索addr
2.调用JMXConnectorFactory.connect(addr)

13.9.2连接桩(Connection Stubs)

客户端连接到服务器的另一种方法是获取连接器存根。 一个连接器存根是由连接器服务器生成的JMXConnector对象。 它是可串行化,以便它可以传输到远程客户端。 一个客户端检索一个连接器存根可以调用存根连接方法连接到连接器生成它的服务器。

例如,包含MBean服务器的应用程序app1可能导出该应用程序服务器到远程管理器如下:
1.创建一个连接器服务器cServer
2.通过调用cServer.toJMXConnector来获取连接器存根cStub
3.将存根放在管理应用程序可以找到的地方,例如JiniTM查找服务或HTTP服务器中的目录

经理可以开始管理app1,如下所示:
1.从上面第3步中存储的位置检索cStub
2.调用cStub.connect通过cServer连接到远程MBean服务器

在某些情况下,连接器服务器可能没有所需的全部信息生成任何客户端可以使用的连接器存根。 连接的细节可能取决于客户的环境。 在这种情况下,连接器存根需要由第三方生成,例如通过知道的管理工具客户端和服务器环境的相关细节。

13.9.3查找服务器(Finding a Server)

第17章“查找服务的绑定”定义了基于JMX的代理技术可以通过现有的查找和发现来注册其连接器服务器基础设施,以便JMX Remote API客户端可以创建或获取JMXConnector对象连接到通告的服务器。特别是,那章节提供了以下信息:

  • 第254页的第17.3节“使用服务定位协议”介绍了a客户端可以从SLP检索JMX服务URL,并使用它连接到相应服务器
  • 第258页的第17.4节“使用Jini网络技术”介绍了a客户端可以从Jini查找服务(LUS)中检索连接器存根并进行连到相应的服务器
  • 第17.5节“使用Java命名和目录接口(LDAP后端)”第264页介绍了客户端如何从中检索JMX服务URL轻量级目录访问协议(LDAP)目录,并用它来连接相应的服务器

13.10创建连接器服务器(Creating a Connector Server)

连接器服务器由一个子类的对象表示的JMXConnectorServer。 创建连接器服务器的常用方法是通过JMXConnectorServerFactory建立。 使用提供的JMXServiceURL作为参数,工厂以类似的方式确定要实例化的类在第13.9.1节“JMXConnectorFactory”中描述的JMXConnectorFactory第215页。

连接器服务器也可以通过实例化一个子类来创建JMXConnectorServer显式。

为了有用,连接器服务器必须连接到MBean服务器,并且它必须是活性。

连接器服务器可以通过以下两种方式之一连接到MBean服务器。或它连接到的MBean服务器在连接器服务器处于指定状态时指定构造,或者连接器服务器在MBean服务器中注册为MBean它与之相连。

连接器服务器不必在MBean服务器中注册。 它是平坦的虽然不常见,但可以在MBean服务器中注册连接器服务器与附加的不同。

代码示例13-1显示了如何创建一个连接器服务器来监听一个本地主机上未指定的端口。 它连接到MBean服务器mbs,但不是注册:

CODE EXAMPLE 13-1 Creating a Connector Server attached to an MBean Server

MBeanServer mbs = MBeanServerFactory.createMBeanServer();
JMXServiceURL addr = new JMXServiceURL(“jmxmp”, null, 0);
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(addr, null, mbs);
cs.start();

连接器服务器实际正在侦听的地址,包括端口可以通过调用cs.getAddress()来获得分配的号码。

代码示例13-2显示了如何做同样的事情,但使用连接器服务器在其所连接的MBean服务器中注册为MBean:

CODE EXAMPLE 13-2 Creating a Connector Server Registered in an MBean Server

MBeanServer mbs = MBeanServerFactory.createMBeanServer();
JMXServiceURL addr = new JMXServiceURL(“jmxmp”, null, 0);
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(addr, null, null);
ObjectName csName = new ObjectName(“:type=cserver,name=mycserver”);
mbs.registerMBean(cs, csName);
cs.start();

13.10.1发布服务器(Publishing a Server)

第17章“查找服务的绑定”定义了代理如何发布它连接器服务器与现有的查找和发现基础设施,以便一个JMX远程API客户端不知道这样的服务器可以找到它并连接到它。特别是,该部分提供了以下信息:

  • 第254页的第17.3节“使用服务定位协议”介绍了代理使用SLP注册连接器服务器的JMX服务URL,以便JMX远程API客户端可以检索并使用它连接到服务器
  • 第258页的第17.4节“使用Jini网络技术”介绍了如何使用Jini网络技术代理使用Jini查找来注册连接器服务器的连接器存根服务,以便JMX Remote API客户端可以检索该存根并连接到该存根服务器
  • 第17.5节“使用Java命名和目录接口(LDAP后端)”(第264页)介绍代理如何注册连接器的JMX服务URL服务器放在LDAP目录中,以便JMX Remote API客户端可以检索这个服务器URL并使用它连接到服务器。

13.11类加载(Class Loading)

每个非原始Java对象都有一个类,每个类都有一个类加载器。一个类加载的微妙缺陷是类加载器cl1创建的类a.b.C是不同于类加载器cl2创建的类a.b.C。这里“创建”是指到实际用defineClass方法创建类的类加载器。如果cl1和cl2都通过委托给另一个类加载器cl3来找到a.b.C,这是相同的类。

由cl1创建的“a.b.C”类型的值不能分配给变量或参数类型“由cl2创建的a.b.C”。试图这样做会导致例外作为ClassCastException。

当连接的一端从另一端接收一个序列化的对象时,它就是重要的是该对象用正确的类加载器进行反序列化。本节解释了确定每种情况下使用的类加载器的规则。

当属性类型或操作类型需要这些类加载规则时参数和返回值是特定于应用程序的Java类型。避免必须处理这些规则,最好只使用由Java定义的标准类型平台或由JMX和JMX Remote API提供。为Open MBeans定义的类型在JMX API中允许无需描述任意复杂的数据结构需要应用程序特定的类型。一个重要的副作用是互操作与非Java客户端大大简化。

当应用特定于应用程序的通知过滤器时,还需要这些规则。(请参阅第208页上的第13.4.1节“过滤器和回手”。)为了避免必须管理类加载规则,请考虑只使用三个标准通知过滤器来自JMX API的类型,NotificationFilterSupport,MBeanServerNotificationFilter和AttributeChangeNotificationFilter。另一种方法是在客户端进行筛选监听器,尽管这可能会增加网络通信量并丢弃通知只要他们收到。

13.11.1客户端上的类加载(Class Loading on the Client End)

连接器客户端可以在连接到a时指定默认的类加载器服务器。这个类加载器在反序列化从服务器接收到的对象时使用,无论它们是从MBeanServerConnection方法返回的值,这些方法抛出的异常或由MBeans发出的通知客户正在聆听。

默认的类加载器是属性的值JMXConnector环境中的jmx.remote.default.class.loader。JMXConnector首先在环境Map中查找此属性在连接JMXConnector时提供。如果没有,或者没有属性没有找到,它然后看在提供的环境地图创作时间。如果没有,或者找不到属性,那么就是默认的类loader是上下文类加载器(Thread.currentThread()。getContextClassLoader())当JMXConnector连接时。没有说明如果发生什么情况由这些规则确定的默认类加载器为null。

如果jmx.remote.default.class.loader属性的值不是一个类加载器,那么尝试连接JMXConnector就会得到一个IllegalArgumentException异常。

注 - 序列化:当一个JMXConnector被序列化时,环境Map在创建JMXConnector时所提供的内容会丢失:Map不是序列化,因为它预计包含对象,如类加载器,而不是序列化。因此,当需要一个特定的默认类加载器时JMXConnector,建议始终在时提供的地图中指定它连接。

13.11.2服务器端的类加载(Class Loading on the Server End)

反序列化从客户端接收的参数时要使用的类加载器取决于操作。 有时适当的类加载器是那个
属于目标MBean,因为该MBean可能具有未由JMX API或JMX Remote API定义的参数类型。 有时适当类加载器是在创建连接器服务器期间配置的,因为连接器服务器旨在用于特定的管理应用。 这样的应用程序可能会定义它自己的MBean的子类参数类型,或者它可能为其定义自己的NotificationFilter类听众。 一个被管理的MBean不能期望每一个MBean通知过滤器管理应用程序可能要使用,所以它不会有意义的是只使用MBean的类加载器来反序列化通知过滤器将监听器添加到MBean中。

与连接器客户端一样,连接器服务器具有已确定的默认类加载器当连接器服务器启动时。 默认的类加载器被确定为如下:

  • 如果连接器服务器的环境映射包含该属性jmx.remote.default.class.loader,该属性的值是默认值类加载器
  • 如果环境贴图包含该属性jmx.remote.default.class.loader.name,该属性的值是作为默认类加载器的MBean的ObjectName。 这允许连接器服务器用一个类加载器创建,该类加载器是一个管理小程序(m-let)相同的MBean服务器
  • 如果上述属性都未定义,则默认的类加载器是该线程的上下文类加载器在JMXConnectorServer启动时

如果jmx.remote.default.class.loader和jmx.remote.default.class.loader.name被定义,或者如果值jmx.remote.default.class.loader不是ClassLoader,或者如果值为jmx.remote.default.class.loader.name不是名称为a的ObjectNameClassLoader,试图启动连接器服务器得到一个IllegalArgumentException异常。

对于与单个“目标”MBean,M对象进行交互的某些操作使用M的扩展类加载器进行反序列化。 这是一个加载每个类的类加载器X,如下所示:
1.加载或正在加载M的类加载器被要求加载X.
2.如果失败并带有ClassNotFoundException,则会询问默认的类加载器加载X.
3.如果第1步失败并出现ClassNotFoundException以外的异常,或者步骤2失败并出现任何异常,该异常是加载X的结果

MBeanServerConnection操作的反序列化规则为如下:
- setAttribute和setAttributes的参数使用反序列化目标MBean的扩展类加载器
- 调用中的Object数组使用目标MBean的扩展进行反序列化类加载器
- createMBean表单中的Object数组使用反序列化目标MBean的扩展类加载器。在这里,“加载或加载的类加载器加载M“是API文档中描述的类加载器特别是createMBean形式。在使用Class Loader的表单的情况下存储库,它是一个总是委托给该存储库的类加载器
- queryNames和queryMBeans操作中的QueryExp被反序列化使用默认的类加载器
- 中的NotificationFilter和Object handbackaddNotificationListener和removeNotificationListener操作(所有表单)都使用目标(通知广播公司)MBean进行反序列化扩展类加载器

剩余的参数是String类型的(这是一个已知的最终类引导类加载器),String []或ObjectName。

如果ObjectName的用户定义的子类从客户端发送到服务器,则不是指定了它是如何反序列化的,所以这不能保证一般工作。

13.12连接器服务器安全性(Connector Server Security)

连接器服务器通常有一些认证远程客户端的方式。为了RMI连接器,这是通过提供一个实现该对象的对象来完成的连接器服务器创建时的JMXAuthenticator接口。对于JMXMP连接器,这是使用SASL完成的。

在这两种情况下,身份验证的结果都是表示JAAS的JAAS主体认证身份。从客户端收到的请求使用这个来执行身份。借助JAAS,您可以定义身份拥有的权限。尤其是,您可以使用MBeanPermission控制对MBean服务器操作的访问类。但是,为了这个工作,你必须有一个SecurityManager。

如果连接器服务器不支持身份验证或者未使用身份验证身份验证,则使用创建的相同身份执行客户端请求连接器服务器。

作为JAAS的替代方法,您可以通过以下方式控制对MBean服务器操作的访问使用MBeanServerForwarder。这是一个实现该对象的对象MBeanServer接口通过将其方法转发给另一个MBeanServer对象,可能在转发之前或之后执行额外的工作。特别是,对象可以进行任意访问检查。您可以插入MBeanServerForwarder在连接器服务器和它的MBean服务器之间使用该方法setMBeanServerForwarder。

13.12.1主题委派(Subject Delegation)

与连接器服务器的任何给定连接最多只有一个经过身份验证的主题。这意味着如果客户以几种不同的方式执行操作身份,它必须为每个人建立一个单独的连接。

但是,这两个标准连接器也支持主题委派的概念。一个使用经过身份验证的客户端和服务器之间建立单一连接身份,像往常一样。对于每个请求,客户端指定一个每个请求的Subject。该请求是使用此每个请求标识执行的,只要经过身份验证每连接身份有权这样做。该权限是用指定的许可SubjectDelegationPermission。

对于每个委托的主题,客户端从中获取MBeanServerConnection已验证Subject的JMXConnector。请求使用这个MBeanServerConnection与委托的主体一起发送。任何数目的委托身份的MBeanServerConnection对象都可以从同一个JMXConnector获得并同时使用。

13.12.2访问控制上下文(Access Control Context)

代表远程客户端的MBean Server操作在访问中执行控制上下文(请参阅java.security.AccessControlContext),其中检查权限必须由认证主体(或委托)来持有主题)以及创建连接器服务器的主题。没有后者检查,一个有权创建连接器服务器但不是其他人的实体权限可能可以通过创建连接器来获得其他权限服务器并向它发送请求。

如果创建连接器服务器的主题有一个SubjectDelegationPermission中的每个委托人都在经过身份验证(或)委派)主题,则不检查其MBean Server的权限操作。这意味着有两种方法可以配置该权限连接器服务器创建者。它必须具有任何远程的所有权限客户将需要其业务;或者它必须有一个SubjectDelegationPermission适用于远程客户端的每个Principal认证或委托。

假设一个安全上下文(主题和/或代码库和/或代码签名者)说创建者创建一个JMXConnectorServer。稍后,连接到达并且是通过本地远程进行身份验证。一个MBeanServer.getAttribute通过连接执行操作,并需要访问文件。意即被检查的权限是MBeanPermission和FilePermission。如果满足以下任一条件,则操作将成功:

  • 远程和创建者都拥有MBeanPermission和FilePermission,要么
  • remote具有MBeanPermission和FilePermission,并且创建者具有SubjectDelegationPermission(远程)。

第二种情况的策略文件可能如下所示:

grant codebase "file:/agent.jar" {
permission javax.management.remote.SubjectDelegationPermission
"javax.management.remote.JMXPrincipal.remote";
}
grant principal javax.management.remote.JMXPrincipal "remote" {
permission javax.management.MBeanPermission "Stats", "getAttribute";
permission java.io.FilePermission "stats.txt", "read";
}

14. RMI连接器(RMI Connector)

RMI连接器是必须在所有实现中出现的唯一连接器本规范。 它使用RMI基础结构在客户端之间进行通信和服务器。

14.1 RMI传输(RMI Transports)

RMI定义了两个标准传输,即Java远程方法协议(JRMP)和Internet Inter-ORB协议(IIOP)。

JRMP是默认的传输。 如果你只使用这个,这是你得到的运输来自Java 2 Platform Standard Edition(J2SETM)的java.rmi。*类平台)。

IIOP是由CORBA定义的协议。 在IIOP上使用RMI允许与其他编程语言的互操作性。 它被覆盖来自J2SE平台的javax.rmi。和org.omg。类。

这两个传输的RMI被称为RMI / JRMP和RMI / IIOP。

RMI连接器支持两种传输。 请参阅API文档(在特别是对javax.management.remote.rmi包的描述)细节。

14.2 RMI连接器的机制(Mechanics of the RMI Connector)

对于每个RMI连接器服务器,都有一个实现的远程导出对象远程接口RMIServer。 一个想要与之通信的客户端连接器服务器需要获取连接到此的远程引用或存根远程对象(第14.3节“How to”中描述了如何获得存根连接到RMI连接器服务器“)。 RMI安排任何方被调用的存根被转发给远程对象。 所以,有一个存根的客户端RMIServer对象可以调用它的方法,导致相同的方法在服务器的对象中调用。

图14-1显示了两个客户端,这两个客户端都具有相同服务器对象的存根。该服务器对象被标记为impl,因为它是实现该功能的对象的RMIServer界面。
……
FIGURE 14-1 Several Clients can Have Stubs Connected to the Same Server Object

除了表示连接器服务器的远程对象外,还有一个远程对象,用于通过连接器创建到MBean的每个客户端连接服务器。 当客户想要调用远程MBean服务器上的方法时,在其服务器存根中调用newClient方法。 这导致newClient方法在要调用的远程服务器对象中。 此方法创建一个新的远程实现远程接口RMIConnection的对象,如图所示图14-2。 该接口包含MBean的所有远程访问方法服务器。 从客户端的newClient方法返回的值是一个存根连接到这个新的对象。 当客户端调用MBean服务器方法时,如getAttribute,这会产生一个对相应方法的调用RMIConnection存根,并因此远程调用相应的实现对象在服务器中。

14.2.1包装RMI对象(Wrapping the RMI Objects)

用户代码通常不直接与RMIServer交互RMIConnection对象。

在服务器端,RMIServer对象由创建并导出RMIConnectorServer。 RMIConnectorServer是的一个子类JMXConnectorServer,就此而言,它是一个连接器服务器标准。 RMIConnection对象由RMIServer在内部创建实现,但服务器中的用户代码从不会看到它们。

在客户端,可以显式获取RMIServer存根,如下所述第14.3节“如何连接到RMI连接器服务器”(第223页)。更多通常,它是作为查找RMI的URL的过程的一部分而获得的连接器,但包装在R​​MIConnector对象中。用户代码通常只处理与此RMIConnector对象。 RMIConnector实现了JMXConnector接口,通过这个接口它通常被访问。

在正常使用中,用户代码永远不会从RMIServer调用任何方法,也不会调用可以看到RMIConnection类型的任何对象。这些对象被隐藏RMIConnector类。

14.2.2 RMIConnection

RMIConnection接口与MBeanServerConnection类似接口由JMX规范定义,但有一些重要的区别:

  • 受13.11节中详述的类加载规则支配的参数第218页的“类加载”包装在MarshalledObject中,以便于在确定适当的类后,它们可以由服务器解包装载机使用
  • addNotificationListeners和removeNotificationListener方法使用侦听器ID而不是侦听器,详见第13.4节“添加”远程监听器“(第208页)
  • 还有其他方法可以获取连接ID并关闭连接
  • 还有一种获取未完成通知的方法

RMIConnection对象代表术语中的连接,而不是会话第13.1节“会话和连接”,第206页。连接的任何一端可以随时关闭它而不影响会话。 服务器关闭了通过导出RMIConnection对象进行连接。 正在进行的RMI呼吁对象运行完成并正常返回,但新的调用将失败。 当客户看到这样的失败,它将获得一个新的RMIConnection对象,如下所述第225页的第14.2节“RMI连接器的机制”。

14.2.3通知(Notifications)

RMI连接器使用第13.4节“添加远程”中介绍的技术监听器“。连接器服务器具有无状态通知缓冲区(第210页的第13.4.3节)。 如果连接器客户端具有侦听器,则使用该侦听器对RMIConnection对象执行fetchNotifications调用以接收通知为他们。

与客户端对应的(ObjectName,NotificationFilter)对的列表侦听器不会在每次调用fetchNotifications时传递。 相反,它是当使用单个addNotificationListeners调用建立时RMIConnection对象已创建。 更改通知列表时的情况连接打开后会进一步调用addNotificationListeners并removeNotificationListener。

14.3如何连接到RMI连接器服务器(How to Connect to an RMI Connector Server)

一般来说,有三种方法可以连接到RMI连接器服务器:

  1. 将JMXServiceURL提供给指定rmi的JMXConnectorFactory 或iiop协议。这是最常用的连接方式。 JMXServiceURL 要么以编码形式包含存根,要么在其中指示目录条目哪一个RMIServer存根可以找到。这在API中有进一步描述 javax.management.remote.rmi包的规范。细节查找此目录条目并从中创建一个JMXConnector是隐藏的 来自主叫方
  2. 从某处获得JMXConnector存根,例如目录如 LDAP,Jini查找服务,或作为RMI方法调用的返回值。 此存根控件是由RMIConnectorServer.toJMXConnector生成的对象。它是JMXConnector类型的对象。它不是一个RMI存根,不应该是与RMIServer或RMIConnection类型的RMI存根混淆。然而,它引用一个RMIServer存根,当它的连接方法被调用时它会使用它
  3. 从某处获取RMIServer存根,并将其用作参数 RMIConnector的构造函数

14.4使用RMI连接器的基本安全性(Basic SecurityWith the RMI Connector)

RMI连接器为保护和认证提供了一个简单的机制客户端和服务器之间的连接。这个机制并不打算
处理每种可能的安全配置,但提供基本的安全级别适用于使用RMI连接器的环境。更高级的安全要求是更好地由JMXMP连接器进行处理(参见第15.3.3节“安全功能”)JMXMP连接器“)。

为了使RMI连接器服务器安全,在创建时提供环境必须包含属性jmx.remote.authenticator,它的关联值是一个实现接口JMXAuthenticator的对象。这个对象是负责检查客户提供的认证信息要么派生代表客户的JAAS主题,要么拒绝连接请求与SecurityException。

连接到具有JMXAuthenticator的服务器的客户端必须提供JMXAuthenticator将检查的认证信息。该提供给连接操作的环境必须包含该属性jmx.remote.credentials,其关联值是身份验证信息。该对象必须是可序列化的。

本规范不包含任何预定义的认证系统。该这种系统最简单的例子是客户端和服务器之间共享的秘密字符串服务器。客户端提供此字符串作为它的jmx.remote.credentials,而服务器的JMXAuthenticator会检查它是否具有正确的值。

作为一个稍微复杂的例子,认证信息可能是a包含用户名和密码的字符串[2]。 JMXAuthenticator例如通过查询密码文件或通过登录进行验证一些依赖于系统的机制,并且如果成功派生出一个Subject的话给定的用户名。

14.4.1安全性如何影响RMI连接器协议(How Security Affects the RMI Connector Protocol)

客户端提供的身份验证信息作为参数传递给newClient呼叫(见图14-2)。 连接器服务器给它JMXAuthenticator。 如果JMXAuthenticator引发异常,那异常传播到客户端。 如果JMXAuthenticator成功,则返回一个主题,并且该主题作为参数传递给新的构造函数RMIConnection对象。 RMIConnection中的所有MBean服务器方法作为这个特定的主题来执行特权的工作,以便他们拥有适用于已认证客户端的权限。

14.4.2实现真正的安全(Achieving Real Security)

上述解决方案足以提供基本的安全级别。 一个号码必须解决问题才能达到真正的安全水平,但是:

  1. 如果认证信息包含密码,并且网络不是那么攻击者可能会看到从客户端发送到服务器的密码
  2. 攻击者可能能够将自己的服务器替换为客户端的服务器认为它正在交谈,并检索客户端发送给的密码验证自己
  3. 攻击者可能能够看到合法创建的RMI对象IDRMIConnection对象,因为它是远程访问的。 然后他们可以使用RMI来调用该对象,使用曾经的主题执行MBean服务器方法对象创建时进行身份验证
  4. 攻击者可能能够猜出这个RMI对象ID,例如对象ID是否是分配为连续的小整数

前三个问题可以通过使用RMI套接字工厂来解决,客户端和服务器之间的连接使用安全套接字层(SSL)。 这是在其他地方有更详细的介绍(例如,参见“使用带有SSL的RMI”[RMI /SSL])。

问题2的一个特例是攻击者可能能够修改内容在连接建立期间使用的目录或查找服务。 这个可能是用于查找RMIServer存根的目录,或者是用于查找URL的目录。 如果RMI注册表用于RMIServer存根,它应该用SSL保护。

第四个问题可以通过设置标准的RMI系统属性来解决java.rmi.server.randomIDs为“true”。 这导致每个的64位对象ID导出使用密码强的随机数生成的RMI对象发电机。 (请参阅类java.rmi.server.ObjID的文档。)

14.5协议版本(Protocol Versioning)

远程RMIServer接口包含getVersion方法,该方法返回一个包含协议版本号的字符串。本标准规定了1.0的版本RMI连接器协议,这是目前唯一的版本。任何未来该标准的版本可能包括或不包括该版本的更新版本协议。

每个协议版本都会有一个与版本相同的版本号这个标准首先定义了它。例如,如果这个标准的版本是1.1的话不改变协议,但版本1.2确实,然后是下一个RMI连接器协议版本号将是1.2。

所有未来版本的RMI连接器都将包含远程RMIServer对象至少有与当前版本1.0相同的方法,特别是getVersion方法。未来的版本可能会增加更多的方法。

如果将来的版本将方法添加到RMIServer接口,它必须确保1.0客户端调用的方法按预期工作。

如果未来版本中定义的RMI连接器的客户端使用方法添加到该版本的服务器中,它必须使用getVersion检查它与之通信的服务器支持该版本。否则,它必须限制自己到服务器支持的方法,可能会失去一些功能后果。

15.通用连接器(Generic Connector)

JMX Remote API包含一个通用连接器作为API的可选部分。 这个连接器被设计成可以通过插入模块来定义的以下:

  • 用于将请求从客户端发送到服务器和传输协议的传输协议将响应和通知从服务器发送到客户端
  • 从客户端发送到其类加载器的服务器的对象封装可以依赖于目标MBean

JMXMP连接器是通用连接器的配置,传输协议基于TCP,对象封装是本地Java序列化(由java.io.ObjectOutputStream等定义)。 安全是基于JSSE [JSSE],JAAS [JAAS]和SASL [JSR28] [RFC2222]。

通用连接器及其JMXMP配置是可选的,这意味着一个实现可以选择不包含它们。 一个实现包括他们必须符合他们的规格在这里和API文档。

15.1可插入传输协议(Pluggable Transport Protocol)

通用连接器的每个配置都包含一个传输协议,它是一个实现接口MessageConnection。连接的每一端有这个接口的一个实例。该接口定义了三种主要方法:

  • writeMessage方法将Java对象写入到的另一端 连接。 Java对象是由连接器定义的类型消息。它可以引用任意Java类型的其他Java对象。对于JMXMP连接器, 包中包含可能的消息类型javax.management.remote.message。
  • readMessage方法从该对象的另一端读取一个Java对象 连接。 Java对象的类型是Message,并可以引用对象任意其他类型。
  • close方法关闭连接

连接是客户端和服务器之间的全双工连接。一个请求流从客户端发送到服务器,以及一串响应和通知从服务器发送到客户端。参见图15-1。
……
FIGURE 15-1 MessageConnection Defines a Full-Duplex Transport Between Client and
Server

当客户端代码发出一个MBeanServerConnection请求时,例如getMBeanInfo,请求被包装在一个MBeanServerRequestMessage中对象并使用MessageConnection.writeMessage写入服务器。该客户端代码然后等待相应的响应。 同时,另一个线程在客户可以写另一个请求。 响应到达时,使用其消息ID将它与它所属的请求匹配,并且发出请求的线程是醒来后回应。

15.2可插入对象包装(Pluggable ObjectWrapping)

通过MBeanServer.invoke调用的MBean方法的参数,以及提供给setAttribute或setAttributes的属性值可以是Java的目标MBean已知但不连接到连接器服务器的类。如果这些对象与其他对象一样,连接器服务器会得到一个当它尝试反序列化包含它们的请求时发生ClassNotFoundException。

为了避免这个问题,连接服务器端的反序列化进入两个阶段。首先,连接器已知的类对服务器被反序列化。这足以确定是哪种请求收到,它注定要用于哪个MBean(如果有的话),因此是什么类加载器适合使用。然后剩下的对象(调用的参数或属性setAttribute(s)的值)可以使用这个类加载器进行反序列化。

ObjectWrapping接口允许自定义对象封装。通过默认情况下,它构造一个包含输出的字节数组对象上的ObjectOutputStream.writeObject或要包装的对象。但例如,如果MessageConnection使用可扩展标记语言(XML),这将是不适当的。所以,在这种情况下一个ObjectWrapping对象可以插入用XML包装对象的连接器中。这个XML可以然后被包含在由MessageConnection构造的更大的XML文本中。

15.3通用连接器协议(Generic Connector Protocol)

通用连接器协议定义了一组交换的协议消息客户端和服务器端之间的连接,以及这些顺序消息交换必须遵循。 本规范的实现必须以定义的顺序交换这些消息,以便它们可以互操作其他实现。 图15-2描述了th
……
FIGURE 15-2 Generic Connector Protocol Messages

通用连接器协议消息可以分为四类:
Handshake messages:
HandshakeBeginMessage
HandshakeEndMessage
HandshakeErrorMessage
VersionMessage
Profile messages:
TLSMessage (JMXMP Connector only)
SASLMessage (JMXMP Connector only)
MBean server operation messages:
MBeanServerRequestMessage
MBeanServerResponseMessage
NotificationRequestMessage
NotificationResponseMessage
Connection messages
CloseMessage

15.3.1握手和配置文件消息交换(Handshake and Profile Message Exchanges)

握手消息交换由连接的服务器端开始一旦JMXConnector类的connect方法被客户端调用,客户端和服务器之间的连接就建立起来了。

连接的服务器端向客户端发送HandshakeBeginMessage与服务器支持的配置文件。这些配置文件是从中检索环境映射通过jmx.remote.profiles属性。当时的客户启动从服务器中选择的配置文件的配置文件消息交换支持的配置。

JMXMP配置文件用于协商要使用的JMXMP的版本。这个配置文件是始终隐式启用,但仅在客户端和服务器不同时协商他们的默认版本。请参见第243页的章节15.3.5“协议版本控制”。

对于其他配置文件,客户端将首先检查在其中请求的所有配置文件环境地图由服务器支持。如果没有,它会发送一个HandshakeErrorMessage发送到服务器并关闭连接。 (这是标准JMX Remote API的行为。 JMXMP的其他API可以提供方法选择使用哪个建议的配置文件。)

然后,对于客户环境地图中要求的每个配置文件,客户端都会这样做谈判该配置文件。协商配置文件的顺序就是它们的顺序出现在客户的环境地图中。这个顺序可能很重要。例如,如果客户端在TLS配置文件之前协商SASL / PLAIN配置文件,它将发送一个密码以明文形式通过连接。如果它首先协商TLS,则连接将在密码发送之前变为加密。

没有指定服务器如何接受或拒绝运行的配置文件序列客户端。但是,建议如果服务器中的配置文件
环境映射意味着一定程度的安全性,服务器应拒绝其协商配置文件不能确保安全级别的连接。对于例如,如果服务器仅配置了TLS配置文件,则应拒绝该服务器不协商TLS的连接。如果服务器配置了TLS配置文件和SASL / DIGEST-MD5配置文件指定相同的安全级别关于认证和加密,那么它应该拒绝连接谈判既不配置文件。

配置文件交换一次执行一次,并始终由客户端启动。一旦配置文件交换完成,客户端发送一个HandshakeEndMessage到服务器。当时没有进一步的资料交流可能。如果服务器使用相同的HandshakeEndMessage,则该服务器应答接受已协商的配置文件或使用HandshakeErrorMessage如果没有。在后一种情况下,连接关闭。

握手阶段完成后,客户可以得到一个参考远程MBean服务器,发送MBean服务器请求以及注册侦听器
接收通知。服务器将向客户端MBean服务器发送响应请求并将通知转发给感兴趣的客户。图15-3描述初始握手和配置文件消息交换。

……
FIGURE 15-3 Handshake and Profile Message Exchanges

请注意,只有握手开始和握手结束消息是强制性的。配置文件消息交换取决于服务器的配置和
客户端通过环境映射中的jmx.remote.profiles属性在创建JMXConnector和JMXConnectorServer时传入。

在握手阶段的任何时间,如果任何一方都遇到错误(客户端或服务器),它必须发送一个指示(HandshakeErrorMessage)来说明原因操作失败。 遇到问题的对等方将发送错误消息发送给另一方,并立即关闭连接。 那个同伴接收到的连接另一端的消息也将关闭在接收到握手错误消息时立即连接。 图15-描述了客户端或服务器向其他对等方指示错误的方式
在初始握手消息交换期间。

……
FIGURE 15-4 Handshake Error Message Exchanges

15.3.2 MBean服务器操作和连接消息交换(MBean Server Operation and Connection Message Exchanges)

一旦初始握手阶段已经终止,并且所有配置文件都进行了协商,客户端可以通过调用该方法来检索对远程MBean服务器的引JMXConnector实例上的getMBeanServerConnection方法。通过
MBeanServerConnection接口客户端可以对其执行操作已注册的MBean,包括接收通知的注册。这些MBean服务器操作将由协议映射到MBeanServerRequestMessage消息。对于服务器每个这样的消息接收它,解码它,在MBean服务器上执行操作,然后返回MBeanServerResponseMessage消息中的操作结果。

如果多个客户端线程同时执行MBean服务器操作,可能会有几个MBeanServerRequestMessages在没有发送的情况下发送但已收到相应的MBeanServerResponseMessages。有不要求客户在发送之前收到每个请求的响应下一个请求。

每个MBeanServerRequestMessage都包含一个匹配的标识符MBeanServerResponseMessage也必须包含。在任何时候,客户都有一个为其发送的尚未收到请求的请求设置标识符{id1,id2,…}响应。每个新请求都必须有一个不在集合中的标识符,也就是说发送请求时添加到集合中。每个响应都必须有一个标识符在该集合中,并且在收到响应时从集合中移除。它是一个协议错误违反了这些条件。检测到错误的对等体必须关闭连接,可选地在发送CloseMessage到另一个之后同行。

通知使用第13.4节“添加”中描述的技术进行处理远程监听器“。连接器服务器具有无状态通知缓冲区(第210页的章节13.4.3)。如果连接器客户端具有侦听器,则使用该侦听器NotificationRequestMessage接收通知。每个这样的消息请求NotificationReplyMessage。

与客户端对应的(ObjectName,NotificationFilter)对的列表侦听器不会在每个NotificationRequestMessage中传递。相反,它是在一个单独的addNotificationListeners中建立连接建立时的MBeanServerRequestMessage。更改连接处于打开状态时的通知列表将进一步进行包含addNotificationListeners或MBeanServerRequestMessages的MBeanServerRequestMessages中的removeNotificationListener。图15-5描述了MBean服务器操作消息交换。

……
FIGURE 15-5 MBean Server Operations Message Exchanges

在握手阶段之后和MBean服务器操作期间的任何时候消息交换,客户端或服务器可能要关闭连接。
一方面,客户端可以通过调用close方法来实现JMXConnector实例。另一方面,服务器可以通过调用来实现JMXConnectorServer实例上的stop方法。此外,客户端或服务器可以随时关闭连接,例如第13.7.1节所述“检测异常终止”(第213页)。发起连接的对等方close动作会发送一条CloseMessage类型的消息来通知对方必须关闭连接并进行必要的清理出。

当客户端发送或接收一个CloseMessage时,它不能再发送通过该连接向服务器发送请求。服务器将继续处理现有的请求并在关闭连接之前发送相应的回复。

图15-6描述了关闭连接消息交换。
……
FIGURE 15-6 Close-Connection Message Exchanges

15.3.3 JMXMP连接器中的安全功能(Security Features in the JMXMP Connector)

JMXMP连接器提供对身份验证和授权的支持通过TLS和SASL配置文件。 JMX Remote API不会强制执行实施和支持任何特定的SASL机制。 它只是依靠可以使用标准SASL插入的第三方实现界面[JSR28]。

15.3.3.1 TLS配置文件(TLS Profile)

TLS配置文件允许连接的客户端和服务器端协商TLS加密层。 基于证书的认证和共同的客户端/服务器身份验证是可选的功能,可通过中的属性进行配置环境映射(请参见第15.3.6节“控制客户端和服务器的属性”)第244页)。

15.3.3.2 SASL配置文件(SASL Profile)

当使用SASL配置文件进行认证的方式是由选定的SASL机制,并可以从一个机制到另一个机制。

但是,在SASL握手交换结束时,授权标识已经存在已经在SASL客户端和SASL服务器之间进行协商。 因此,SASL配置文件必须使此标识可用于允许MBean服务器和基础MBean根据此标识执行访问控制检查。

SASL配置文件实施使用JAAS框架来构建一个基于此授权标识的JMXPrincipal,并存储此JMXPrincipal在一个主题。 然后,当JMXMPConnectorServer执行任何一个随后的MBean服务器操作,它必须为给定的主题执行此操作需要使用适当的访问控制上下文的特权操作。

有兴趣检索授权信息的MBean可以这样做(如果有的话)适当的权限)通过调用:

AccessControlContext acc = AccessController.getContext();
Subject subject = Subject.getSubject(acc);
Set principals = subject.getPrincipals();

15.3.4违反协议(Protocol Violations)

如果对方收到来自另一个不尊重协议的对等方的消息在这里描述,其行为是未指定的。 推荐的行为是发送一个关闭消息,指示检测到的违规并关闭连接之后立即。

15.3.5协议版本(Protocol Versioning)

该标准指定了JMXMP协议的版本1.0,它是目前的版本只有版本。此标准的任何未来版本可能包含或可能不包含该协议的更新版本。

每个协议版本都会有一个与版本相同的版本号这个标准首先定义了它。例如,如果这个标准的版本是1.1的话不会更改协议,但版本1.2会更改,然后是下一个JMXMP协议版本数字将是1.2。

通过新打开的连接发送的第一条消息是握手开始从服务器到客户端的消息。此消息包含最新的JMXMP服务器可以理解的版本。如果客户也理解那个版本,那么随后的通信将使用该版本进行。如果客户端只理解早期版本,那么它会发送一个VersionMessage请求使用较早的版本。如果服务器理解这个早期版本,那么它会用相同的VersionMessage回复,随后的通信将会使用该版本进行。否则,服务器将发送一个HandshakeErrorMessage和通信将被中止。

换句话说,假设服务器版本是S,客户端版本是C.那么用于通信的版本V被确定如下:
Server to client: “Version S”
If client understands S, V = S
Otherwise:
Client to server: “Version C”
If server understands C:
- Server to client: “Version C”
- V = C
Otherwise (server does not understand C):
- Server to client: “Handshake error.”
- Connection aborted

这种谈判的结果是协议的每个版本都必须这样做了解每个其他版本的HandshakeBeginMessage和
VersionMessage。 只要Java串行兼容性如此,这将是真实的尊重。 请参见[序列号]中的类型更改影响序列化部分。

预计但不要求每个版本的任何实现标准了解以前版本标准的所有协议版本。

15.3.6属性控制客户端和服务器(Properties Controlling Client and Server)

创建JMXConnector或JMXConnectorServer时,使用环境映射可以提供。 这种环境的功能之一是提供配置底层配置文件的参数。 以下小节描述了这些参数。

15.3.6.1通用连接器的全局属性(Global Properties of the Generic Connector)

这些属性控制连接的全局方面,也就是说它们是有效的不管选择的配置文件如何。

  • jmx.remote.profiles
    一个字符串,它是由空间分隔的配置文件名称列表,由该配置文件支持 客户端和/或服务器。配置文件名称的示例包括:JMXMP,TLS,SASL / EXTERNAL,SASL / OTP。 如果此属性未指定,则不会使用配置文件。
  • jmx.remote.context
    通过来自一个对等方的握手消息传送的任意对象到另一个。该对象应该是可序列化的,并且是一个已知的类其他同行。如果此属性未指定,则会传送空的上下文。JMXMP Connector目前不使用此对象,并且不公开它用于客户端或服务器上的用户代码。
  • jmx.remote.authenticator
    在握手阶段结束时使用的JMXAuthenticator进行验证新的连接。此对象的身份验证方法使用a调用两元素Object []作为参数。第一个元素是连接ID新的连接。第二个元素是已验证的Subject,如果有的话。该方法返回用于连接的已认证Subject,或者返回null如果没有经过身份验证的ID。返回的主题通常与主题相同主题作为参数传递,但它可以有不同的主体。如果authenticator不接受连接id或Subject,它可以抛出一个SecurityException异常。
15.3.6.2 TLS属性(TLS Properties)

以下属性控制TLS配置文件:

  • jmx.remote.tls.socket.factory
    已经是javax.net.ssl.SSLSocketFactory类型的对象初始化TLS套接字工厂。 SSLSocketFactory可以被创建和通过SSLContext工厂初始化。如果这个属性的值不是指定,TLS套接字工厂默认为SSLSocketFactory.getDefault()。
  • jmx.remote.tls.enabled.protocols
    一个字符串,它是要启用的TLS协议的空格分隔列表。如果值此属性未指定,启用TLS的协议默认为SSLSocket.getEnabledProtocols()。
  • jmx.remote.tls.enabled.cipher.suites
    一个字符串,它是要启用的TLS密码套件的空格分隔列表。如果值此属性未指定启用TLS的密码套件默认值SSLSocket.getEnabledCipherSuites()。
  • jmx.remote.tls.need.client.authentication
    根据连接器服务器是“真”还是“假”的字符串需要客户端认证。如果为true,则在此期间不验证的客户端握手序列将被拒绝。
  • jmx.remote.tls.want.client.authentication
    根据连接器服务器是“真”还是“假”的字符串如果适合协商的密码套件,则需要客户端认证。如果属实,那么如果一个客户端协商一个支持认证的密码套件,那么客户端不认证自己,连接将被拒绝。
15.3.6.3 SASL属性(SASL Properties)

以下属性控制SASL配置文件:

  • jmx.remote.sasl.authorization.id
    一个字符串,它是连接器客户端的授权身份不同于认证身份。 如果这个属性是未指定的,那么提供者从认证身份派生授权身份。
  • jmx.remote.sasl.callback.handler
    一个类型为javax.security.auth.callback.CallbackHandler的对象是由SASL机制调用的回调处理程序来检索用户信息。 如果此属性未指定,则不会使用回调处理程序。

16.确定新的运输(Defining a New Transport)

本规范定义的标准协议可能并不对应于全部可能的环境。其他可能感兴趣的协议的例子是:

  • 通过串行线路运行的协议,用于管理设备中的JMX API代理没有联网
  • 使用HTTP / S的协议,因为它是系统熟悉的协议管理员可能更愿意通过防火墙而不是RMI或JMXMP
  • 使用XML格式化消息的协议(可能位于基于XML的RPC中协议(如SOAP))构建在现有的基于XML的基础架构上。这样的传输可能会被非Java客户端使用

有两种方法来实现用户定义的协议。一个是定义一个使用MessageConnection传输通用连接器
MessageConnectionServer类,如第15章“通用”中所述连接器”。另一个是为JMXConnectorFactory定义一个新的提供者。

为通用连接器定义传输具有以下优点:
更棘手的实施细节,特别是关于听众的细节已经存在处理。交通工具必须注意建立连接和序列化并反序列化各种Message类。可能的话,运输可以包括其他交换,例如建立一个安全的连接,这不是由于一个MessageConnection.writeMessage并且永远不会被一个MessageConnection.readMessage。例如,TL​​S和TLS就是这种情况SASX交换在JMXMP连接器。

在API中解释了定义JMXConnectorFactory的提供者该类文档。提供者可以基于通用连接器,或者它可以从头开始完全实现协议。

17.查找服务的绑定(Bindings to Lookup Services)

此标准指定使JMX Remote API客户端成为可能的连接器访问和管理通过JMX API代理(MBean服务器)公开的MBean在远程JVM中运行。它还定义了一个JMXServiceURL类,它表示JMX Remote API连接器服务器的地址,并使客户端成为可能以获取连接到该服务器的JMX Remote API连接器。但是,这个标准没有提供任何特定的API来使客户端成为可能找到连接到它所了解的代理的连接器服务器的地址发现哪些代理正在运行,以及连接器服务器的地址是否可以连接到它们。这不是重新发明轮子标准,而不是详细说明如何使用现有的发现做广告和查找代理和查找基础设施。

本规范讨论了三种这样的基础设施:

  • 由[RFC 2608]和[RFC 2609]定义的服务定位协议[SLP]
  • Jini网络技术[Jini]
  • 带有LDAP的Java命名和目录接口TM(“J.N.D.I”)API [JNDI]后端

本章的目标是指定JMX API代理如何注册其连接器具有这些基础结构的服务器以及JMX Remote API客户端如何查询这些基础结构基础设施,以便找到并连接到通告的服务器。

本章对JMX Remote API的实现没有要求。它详细介绍了要遵循的约定,以便可以注册和找到服务器由客户,而不必分享客户端和服务器之间的特殊知识。

17.1术语(Terminology)

术语JMX Remote API Agent(或代理)在本节中用于标识一个逻辑服务器应用程序组成:

  • 一个MBean服务器
  • 一个或多个允许远程客户端访问的JMX Remote API连接器服务器该MBean服务器中包含的MBean

术语JMX Remote API客户端(或客户端)用于标识逻辑客户端应用程序,它使用JMX Remote API代理打开客户端连接。

请注意,单个JVM机器可以包含许多代理和/或客户端。

17.2总则(General Principles)

尽管使用a来注册和查询服务器访问点的API查询服务因基础设施而异,总体原则依然存在一样:

  • 代理创建一个或多个JMX Remote API连接器服务器
  • 然后为每个连接器公开JMXServiceURL(SLP,JNDI / LDAP)或JMXConnector存根(Jini网络技术,JNDI / LDAP)已注册与查找服务,可能提供额外的属性,资格的代理和/或连接器
  • 客户端查询查找服务,并检索一个或多个JMXServiceURL地址(或JMXConnector存根)与查询匹配
  • 然后,它使用JMXConnectorFactory来获取JMXConnector与检索到的JMXServiceURL(SLP,JNDI /LDAP),或者它使用提供的JMXConnector直接连接到服务器存根(Jini,JNDI / LDAP)

17.2.1 JMXServiceURL与JMXConnector存根(JMXServiceURL Versus JMXConnector Stubs)

使用SLP时,从查找中注册和检索服务URL是很自然的服务。但是,使用Jini等网络技术并不那么自然。在Jini网络技术中,您注册并从中获取的Service对象查找服务通常是一个存根,直接实现了接口底层服务,而不是一个对象,可以让你回想一些关于如何的信息连接到服务。因此本标准规定了不同的方式广告连接器服务器,具体取决于所使用的底层查找服务:

  • SLP:注册JMX服务URL的URL字符串表示(JMXServiceURL.toString())。这很自然,因SLP是基于URL的协议。请参见第25.3节“使用服务定位协议”。
  • Jini网络技术:注册一个JMXConnector存根。 JMXConnector接口直接是JMX连接器服务的接口。参见17.4节第258页上的“使用Jini网络技术”
  • JNDI API / LDAP:注册JMX服务URL的URL字符串表示(JMXServiceURL.toString())。 JNDI API可以在客户端上配置(通过StateFactories和ObjectFactories - 参见[JNDI - Java对象])从DirContext自动创建并返回一个新的JMXConnector包含JMX服务URL,或者直接返回其中的DirContext可以提取JMX服务URL。请参见第17.5节“使用Java命名和目录接口(LDAP后端)“。使用JNDI / LDAP的另一种方法是直接存储JMXConnector存根,如Jini所述。这个规范没有定义一个标准的方法来做到这一点。

17.2.2查找属性(Lookup Attributes)

本规范中考虑的所有三种基础设施都具有查找的概念属性。这些属性是符合注册服务的属性。他们
在服务注册时传递给基础架构,并可用作在执行查找时进行过滤。

然后,客户端可以查询查找服务以查找与其匹配的所有连接器或更多属性。由于查找查询而获得多个服务的客户端还可以进一步查询为这些服务注册的查找属性确定它想要使用哪个返回的匹配服务。

对于客户端而言,能够独立于查询服务格式化查询在代理端使用JMX Remote API实现,并了解
检索属性的含义,这个标准指定了一组通用的JMX远程API查找属性,其语义将由所有代理程序知道客户端。在本规范的其余部分中,我们将使用术语查找属性对于这些。

在使用查找服务注册连接器服务器时,代理将执行以下操作:

  1. 构建描述其连接器服务器(SLP,JNDI / LDAP)的JMXServiceURL或从该服务器获取JMXConnector存根(使用Jini网络技术)
  2. 注册该URL(SLP,JNDI / LDAP)或JMXConnector存根(使用Jini网络技术)与查找服务
  3. 提供任何可能有助于客户端查找的额外查找属性服务器

表17-1定义了可以在其上提供的一组常用查找属性连接器注册并可用于过滤查找。大多数这属性是可选的:代理可以选择是否在它指定它时指定它们用查找服务注册JMXServiceURL。

…..
TABLE 17-1 Lookup Attributes for Connectors
AgentName String No Mandatory
ProtocolType String No Optional
AgentHost String Yes Optional
Property String Yes Optional

17.3使用服务位置协议(Using the Service Location Protocol)

服务定位协议[SLP]是IETF标准跟踪协议[RFC2608],[RFC 2609]提供了一个允许网络应用程序使用的框架发现网络服务的存在,位置和配置企业网络。 您可能希望简要阅读[SLP白皮书]SLP的描述,以及它相对于其他技术的定位,比如DNSSRV和LDAP。

17.3.1 SLP实施(SLP Implementation)

Java SLP API是[JSR 140]的对象。 在写这篇文章的时候,这个JSR还没有最终确定。 本节中的代码提取基于Sun专有的JavaSLP的实施紧随[RFC 2614]。 基于其他的代码该RFC的实现将以类似的方式工作。

17.3.2 SLP服务URL (SLP Service URL)

本标准定义的JMXServiceURL直接符合[RFC2609。 因此,JMX服务URL和SLP之间存在直接映射服务URL,因为它们的字符串表示是相同的。

17.3.3 SLP查找属性(SLP Lookup Attributes)

SLP支持多值属性注册; 这些属性提供在注册时间,注册连接器服务器的服务URL。该用于查找的过滤方法是LDAPv3过滤器字符串。 必须的属性或者可以由代理在注册连接器服务器URL时提供在第25.2.2节“查找属性”中定义。

17.3.4代码模板(Code Templates)

以下各节提供了SLP的一些代码模板。

17.3.4.1发现SLP服务(Discovering the SLP Service)

通过SLP,发现查找服务对用户而言是透明的; 正在运行的SLP守护进程负责查找服务代理或目录代理(取决于关于守护进程的配置)。

实际上,一条线足以找到查找服务,如图所示代码示例17-1:
CODE EXAMPLE 17-1 Discovering the SLP Service

import com.sun.slp.ServiceLocationManager;
import com.sun.slp.ServiceLocationException;
import com.sun.slp.Advertiser;
import com.sun.slp.Locator;
...
try {
    // Getting the Advertiser (for registration purposes)
    Advertiser slpAdvertiser = ServiceLocationManager.getAdvertiser(Locale.US);
    // Getting the Locator (for lookup purposes)
    Locator slpLocator = ServiceLocationManager.getLocator(Locale.US);
} catch(ServiceLocationException e) {...}
17.3.4.2使用SLP注册JMX服务URL(Registering a JMX Service URL With SLP)

类广告商用于执行SLP注册,如图所示代码示例17-2:
CODE EXAMPLE 17-2 Registering a Service URL With SLP

import com.sun.slp.ServiceURL;
import com.sun.slp.ServiceLocationAttribute;
...
try {
    // Create a new JMXMPConnectorServer, let the system allocate a
    // a port number.
    //
    JMXServiceURL jmxUrl = new JMXServiceURL("service:jmx:jmxmp://myhost:0");
    final JMXConnectorServer cserver = new JMXMPConnectorServer(jmxUrl,null);
    // Get the Connector Server address
    final JMXServiceURL srvAddr = cserver.getAddres();
    // Note: It is recommended that the JMX Agents make use of the leasing
    // feature of SLP, and periodically renew their lease.
    final ServiceURL serviceURL =
    new ServiceURL(srvAddr.toString(), ServiceURL.LIFETIME_DEFAULT);
    final Vector attributes = new Vector();
    final Vector attrValues = new Vector();
    // Using the default SLP scope
    attrValues.add("DEFAULT");
    final ServiceLocationAttribute attr1 =
    new ServiceLocationAttribute("SCOPE", attrValues);
    attributes.add(attr1);
    // AgentName attribute
    attrValues.removeAllElements();
    attrValues.add(new String("my-jmx-agent"));
    final ServiceLocationAttribute attr2 =
    new ServiceLocationAttribute("AgentName", attrValues);
    attributes.add(attr2);
    ...
    // Registration
    slpAdvertiser.register(serviceURL, attributes);
} catch(ServiceLocationException e) {...}
17.3.4.3使用SLP查找JMX服务URL (Looking up a JMX Service URL With SLP)

类Locator用于执行SLP查找,如图所示代码示例17-3:
CODE EXAMPLE 17-3 Looking up a JMX Service URL With SLP

import com.sun.slp.ServiceType;
import com.sun.slp.ServiceLocationEnumeration;
...
try {
    // lookup in default SCOPE.
    final Vector scopes = new Vector();
    scopes.add("DEFAULT");
    // Set the LDAPv3 query string
    // Here we look for a specific agent called "my-jmx-agent",
    // but we could have asked for any agent by using a wildcard:
    // final String query = "(&(AgentName=*))";
    //
    final String query = "(&(AgentName=my-jmx-agent))";
    // lookup
    final ServiceLocationEnumeration result =
    slpLocator.findServices(new ServiceType("service:jmx"), scopes, query);
    // Extract the list of returned ServiceURL
    while(result.hasMoreElements()) {
    final ServiceURL surl = (ServiceURL) result.next();
    // Get the attributes
    final ServiceLocationEnumeration slpAttributes =
    slpLocator.findAttributes(surl, scopes, new Vector());
    while(slpAttributes.hasMoreElements()) {
    final ServiceLocationAttribute slpAttribute =
    (ServiceLocationAttribute) slpAttributes.nextElement();
    ...
    }
    // Open a connection
    final JMXServiceURL jmxUrl = new JMXServiceURL(surl.toString());
    final JMXConnector client = JMXConnectorFactory.connect(jmxUrl);
    ...
    }
} catch(ServiceLocationException e) {...}

17.4使用Jini网络技术(Using the Jini Network Technology)

Jini网络技术[Jini]是一个开放的软件架构,可以实现开发人员可以创建高度适应变化的网络中心服务。

Jini规范提供标准的查找服务。 正在运行的Jini查找服务可以通过简单的API调用来发现。 远程服务(设备,软件,应用程序等),希望在Jini查找服务中注册提供了一个可序列化的Java对象。 当由远程客户端查找时,该Java对象的副本返回。 通常,这个对象充当远程服务的代理。

此外,Jini网络技术提供各种API和机制从远程HTTP服务器下载代码(必要的是获得所需的类实例化代理对象),Jini规范支持代码的安全性基于RMI安全管理器进行下载。

17.4.1 Jini网络技术实现(Jini Networking Technology Implementation)

Jini网络技术是基于Java的软件实现的可以在Sun社区源代码许可证v3.0(与Jini合作)下载技术特定附件v1.0)。 请参阅http://wwws.sun.com/software/communitysource/ JINI/ download.html

17.4.2服务注册(Service Registration)

Jini规范基于服务注册。服务通过注册一个可序列化的Java对象,它可以是一个存根,一个代理或一个简单的类提供有关该服务的信息。通常,注册的服务是一个存根提供与底层服务的直接链接。因此,虽然这是可能的要使用JMXServiceURL作为服务,此标准指定使用JMX远程API连接器存根,实现JMXConnector接口,如服务。这与Jini规范的哲学是一致的,其中的对象从Jini查找服务中检索的通常是实现接口的代理的服务抬头。

Jini查找服务,它使用Java RMI编组和动态类加载语义,将使用RMI注释自动从中下载服务器端需要反序列化客户端服务对象所需的所有类。这使得服务器可以注册任何私有实现类,并且为客户端使用该类(通过其通用JMXConnector接口)任何关于服务器实现的先验知识。但是,这需要一个来自服务器端的一定数量的配置。这个标准完全为其描述的协议指定JMX Remote API连接器存根,以便从JMX Remote API实现中序列化此类的实例服务器端可以在同一个类的实例中使用在客户端执行,而不必下载任何新的类。因此,使用标准时,服务器端不需要特殊配置连接器。然而,供应商和非标准连接器的用户应该如果他们想使他们的标准不符合,执行所需的配置步骤连接器可用于通用JMX API客户端。

17.4.3使用JMX远程API连接器存根(Using JMX Remote API Connector Stubs)

注册JMX Remote API连接器存根时,服务器应用程序将会或者调用JMXConnectorFactory.newConnector方法来获得一个未连接的存根,或者调用toJMXConnector方法
它想要注册的JMXConnectorServer。 toJMXConnector方法返回可直接注册为可提供服务的可串行连接器存根通过该连接器。

当客户端从Jini查找服务中查找注册的连接器时,返回的连接器存根尚未连接到其对应的服务器。 客户端应用程序需要调用该对象上的JMXConnector.connect()方法在使用之前。

在服务器端调用JMXConnector.connect()显示在代码示例17-4:
CODE EXAMPLE 17-4 Calling JMXConnector.connect() on the Server Side

// get the connector stub:
JMXConnector c = server.toJMXConnector(null);
// register c as the Jini Service.
...

在客户端调用JMXConnector.connect(),如图所示代码示例17-5:
CODE EXAMPLE 17-5 Calling JMXConnector.connect() on the Client Side

// Obtain the service from Jini
Object service = ...
JMXConnector c = (JMXConnector) service;
// Build the env Map, add security parameters,
Map env = new HashMap();
env.put(...)
// Connect with the server
c.connect(env);

17.4.4 Jini查找服务属性(Jini Lookup Service Attributes)

与SLP类似,Jini查找服务支持额外查找的规范属性,称为条目。 这些属性的Java类必须实现net.jini.core.entry.Entry接口。 由Jini定义的名称条目规范被解释为第17.2.2节定义的代理名称“查找属性”(第259页)。随着本规范的完成,其他参赛作品正在通过Jini社区决策流程进行标准化(JDP)。 有关当前信息,请参阅JMX技术主页:
http://java.sun.com/products/JavaManagement/

17.4.5代码模板(Code Templates)

以下部分提供了Jini查找服务的一些代码模板:

17.4.5.1发现Jini查找服务(Discovering the Jini Lookup Service)

Jini查询服务由net.jini.core.lookup.ServiceRegistrar类。 有两种方法发现Jini查找服务。 第一个也是最简单的方法就是假设你知道查找服务的地址,如代码示例17-6所示:
CODE EXAMPLE 17-6 Discovering the Jini Lookup Service Using an Address

import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.discovery.LookupLocator;
...
LookupLocator lookup = new LookupLocator("jini://my_host");
ServiceRegistrar registrar = lookup.getRegistrar();

第二种解决方案使用广播机制来检索查找服务运行在可访问的网络上,如代码示例17-7所示:
CODE EXAMPLE 17-7 Discovering the Jini Lookup Service Using a Broadcast Mechanism

import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
import net.jini.core.lookup.ServiceRegistrar;
...
LookupDiscovery lookupDiscovery = null;
try {
    lookupDiscovery = new LookupDiscovery(null);
} catch (IOException e) {...}
    lookupDiscovery.addDiscoveryListener(new LookupDiscoveryListener());

private class LookupDiscoveryListener implements DiscoveryListener {
    public LookupDiscoveryListener() {
    }
    public void discovered(DiscoveryEvent evnt) {
        ServiceRegistrar[] regs = evnt.getRegistrars();
        for(int i = 0; i < regs.length; i++) {
        String[] regGroups = regs[i].getGroups();
        // Must verify here that the ServiceRegistrar
        // contains the groups I want to register in...
    }
// It is generally better here to launch another Thread to use
// the discovered ServiceRegistrar; this avoids blocking the
// discovery process.
}
public void discarded(DiscoveryEvent evnt) {}
}
17.4.5.2注册JMX远程API连接器StubWithJini查询服务(Registering a JMX Remote API Connector Stub With the Jini Lookup Service)

使用Jini查找服务注册JMX远程API连接器存根如代码示例17-8所示:
CODE EXAMPLE 17-8 Registering a JMX Remote API Connector Stub With the Jini Lookup Service

import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceRegistration;
import net.jini.core.entry.Entry;
import net.jini.core.lease.Lease;
import java.rmi.RemoteException;
...
// Get the Jini ServiceRegistrar with one of the above methods
ServiceRegistrar registrar = ...;
// Get a connector stub for the server you want to export
//
JMXConnector proxy = jmxConnectorServer.toJMXConnector(null);
// Prepare Service’s attributes entry
Entry[] serviceAttrs = new Entry[] {
new net.jini.lookup.entry.Name("MyAgentName");
// Add here the lookup attributes you want to specify.
};
// Create a ServiceItem from the service instance
ServiceItem srvcItem = new ServiceItem(null, proxy, serviceAttrs);
// Register the Service with the Lookup Service
try {
ServiceRegistration srvcRegistration =
registrar.register(srvcItem, Lease.ANY);
System.out.println("Registered ServiceID: " +
srvcRegistration.getServiceID().toString());
} catch(RemoteException e) {...}
17.4.5.3从Jini查找中查找JMX连接器存根服务(Looking up a JMX Connector Stub From the Jini Lookup Service)

在Jini查找服务中查找JMX连接器存根显示在代码示例17-9:
CODE EXAMPLE 17-9 Looking up a JMX Connector Stub From the Jini Lookup Service

import net.jini.core.lookup.ServiceTemplate;
import net.jini.core.lookup.ServiceMatches;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.entry.Entry;
...
// Get the Jini ServiceRegistrar with one of the above methods
ServiceRegistrar registrar = ...;
// Prepare Service’s attributes entry to be matched
Entry[] serviceAttrs = new Entry[] {
// Retrieve all services for which a Name entry was registered,
// whatever the name is (null = wildcard).
new net.jini.lookup.entry.Name(null)
// Add here any other matching attribute.
};
// Look for a specific JMXMP Connector (you may also pass
// JMXConnector.class if you wish to get all types of JMXConnector)
//
ServiceTemplate template = new ServiceTemplate(null,new Class[] {JMXMPConnector.class}, serviceAttrs);
ServiceMatches matches = null;
try {
matches = registrar.lookup(template, Integer.MAX_VALUE);
} catch (RemoteException e) {...}
// Retrieve the JMX Connector and initiate a connection
for(int i = 0; i < matches.totalMatches; i++) {
if(matches.items[i].service != null) {
// Get the JMXConnector
JMXConnector c = (JMXConnector)(matches.items[i].service);
// Prepare env (security parameters etc...)
Map env = new HashMap();
env.put(...);
// Initiate the connection
c.connect(env);
// Get the remote MBeanServer handle
MBeanServerConnection server = c.getMBeanServerConnection();
...
}
}

17.5使用Java命名和目录接口(LDAP后端)(Using the Java Naming and Directory Interface (LDAP Backend))

Java命名和目录接口[JNDI]是Java的标准扩展平台,为Java技术支持的应用程序提供统一的接口
企业中的多种命名和目录服务。 特别是它提供了一个意味着通过轻量级目录访问访问X.500目录服务协议(LDAP)。 该标准定义了如何使用LDAP服务器进行存储有关JMX API代理的信息以及JMX Remote API客户端如何查找这些信息连接到代理。

可以获得使用JNDI API和LDAP后端的很好理解通过遵循[JNDI教程中的LDAP线程]。

17.5.1用于注册JMX连接器的LDAP模式(LDAP Schema for Registration of JMX Connectors)

输入LDAP目录树中的节点。一个节点可以有几个对象类。JMX连接器应该在类jmxConnector的节点中注册。该jmxConnector类包含两个属性,它们是JMX服务的URL相应的连接器(jmxServiceURL)以及JMX API代理的名称导出此连接器(jmxAgentName)。如果存在,JMX服务URL可能不存在代理不接受连接。 jmxConnector类也包含在内可选属性,如jmxAgentHost和jmxProtocolType。代理人名称使客户端应用程序能够连接到它所知道的代理程序名称。与jmxAgentHost和jmxProtocolType一起,它也可以实现可以执行过滤的查询,例如,“查找所有的JMXMP连接器 JMX API代理“或”查找<节点上运行的所有代理的所有连接器“。代码示例17-10是应该的模式定义(如[RFC 2252]中所述)用于注册JMX Remote API连接器:

CODE EXAMPLE 17-10 LDAP Schema for Registration of JMX Remote API Connectors

-- jmxServiceURL attribute is an IA5 String
( 1.3.6.1.4.1.42.2.27.11.1.1 NAME ’jmxServiceURL’
DESC ’String representation of a JMX Service URL’
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
-- jmxAgentName attribute is an IA5 String
( 1.3.6.1.4.1.42.2.27.11.1.2 NAME ’jmxAgentName’
DESC ’Name of the JMX Agent’
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
-- jmxProtocolType attribute is an IA5 String
( 1.3.6.1.4.1.42.2.27.11.1.3 NAME ’jmxProtocolType’
DESC ’Protocol used by the registered connector’
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
-- jmxAgentHost attribute is an IA5 String
( 1.3.6.1.4.1.42.2.27.11.1.4 NAME ’jmxAgentHost’
DESC ’Names or IP Addresses of the host on which the
agent is running. When multiple values are
given, they should be aliases to the same host.’
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
-- jmxProperty attribute is an IA5 String
( 1.3.6.1.4.1.42.2.27.11.1.5 NAME ’jmxProperty’
DESC ’Java-like property characterizing the registered object.
The form of each value should be: "<property-name>=<value>".
For instance: "com.sun.jmx.remote.tcp.timeout=200"’
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
-- jmxExpirationDate attribute is a Generalized Time
-- see [RFC 2252] - or X.208 for a description of
-- Generalized Time
( 1.3.6.1.4.1.42.2.27.11.1.6 NAME ’jmxExpirationDate’
DESC ’Date at which the JMX Service URL will
be considered obsolete and can be removed
from the directory tree’
SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
-- from RFC-2256 --
( 2.5.4.13 NAME ’description’
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
-- jmxConnector class - represents a JMX Connector.
-- must contain the JMX Service URL
-- and the JMX Agent Name
( 1.3.6.1.4.1.42.2.27.11.2.1 NAME ’jmxConnector’
DESC ’A class representing a JMX Connector, and
containing a JMX Service URL.
The jmxServiceURL is not present if the server
is not accepting connections’
AUXILIARY
MUST ( jmxAgentName )
MAY ( jmxServiceURL $ jmxAgentHost $ jmxProtocolType $
jmxProperty $ jmxExpirationDate $ description ) )

jmxConnector类是一个AUXILIARY类,这意味着它的属性可以添加到目录树中的任何节点 - 即不加任何节点限制目录树的结构。

要在目录树中创建一个节点,还需要一个STRUCTURAL类。 这个规范对结构类没有任何限制包含JMX远程API连接器。 你可以,例如,重用在[RFC]中定义的Java架构[JNDI - Java架构]中的javaContainer类2713],即创建一个对象类为javaContainer的节点(STRUCTURAL)和jmxConnector(AUXILIARY)。 包含该节点的节点jmxConnector也可以有任何附加的辅助类。

17.5.2映射到Java对象(Mapping to Java Objects)

该规范仅要求JMX服务URL存储在LDAP中。JMXAPI代理还可以存储序列化的JMX Remote API连接器存根,但是这不是本规范所要求的。 客户端应该只依赖JMX服务URL。 JNDI API使客户端可以使用StateFactories和ObjectFactories [JNDI - Java Objects]重新创建一个JMXConnectorURL执行查找()时,即使没有Java对象绑定到包含DirContext。 或者,客户端可以直接检索jmxServiceURL属性从中获取JMXConnectorJMXConnectorFactory。 JNDI API查找()是否返回一个JMXConnector或DirContext取决于配置设置客户端(InitialContext),并保持在该客户端本地。

17.5.3 JMX远程API注册树的结构(Structure of the JMX Remote API Registration Tree)

目录的实际结构因组织而异。 每组织或企业有其自己的目录树结构,政策等等,以便JMX API代理能够与任何预先存在的集成目录结构,这个规范并没有强加一个固定的目录树结构用于注册代理和JMX远程API连接器服务器。连接器必须简单地位于类jmxConnector的节点中。 这使它成为可能让组织在LDAP中建立自己的注册代理结构服务器。 例如,如果组织具有包含节点的现有目录对于其网络中的每个主机,它可以决定注册每个代理下面的节点它正在运行的主机。

17.5.4租赁(Leasing)

JNDI / LDAP不提供任何内置租赁服务。 如果代理商倒闭了,它的服务URL可能永远保留在目录服务器中。该可以使用jmxConnector辅助类中的jmxExpirationDate属性避免发生这种情况,如代码示例17-11所示:

CODE EXAMPLE 17-11 Leasing using the jmxExpirationDate Attribute

-- jmxExpirationDate attribute is a Generalized Time
-- see [RFC 2252] - or X.208 for a description of
-- Generalized Time
( 1.3.6.1.4.1.42.2.27.11.1.6 NAME ’jmxExpirationDate’
DESC ’Date at which the JMX Service URL will
be considered obsolete and may be removed
from the directory tree’
SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )

JMX API代理将不得不更新jmxExpirationDate属性定期。 然后,目录管理员可能会编写一个守护进程删除jmxConnector节点(或更一般的jmxServiceURL属性),jmxExpirationDate已过时。

17.5.5代码模板(Code Templates)

17.5.5.1发现LDAP服务器(Discovering the LDAP Server)

JNDI / LDAP不提供用于发现LDAP服务器的任何标准方法。假设本地主机上的标准端口(389)通常不是入口点一个选项,因为LDAP服务器通常是集中式的,而不是一个服务器每台主机。 JNDI API指定了一种发现LDAP服务器的方法,通过DNS [JNDI - LDAP服务器发现],但这是操作系统因为LDAP服务器并不总是这样,所以并不总是可行的在DNS中注册。 因此这个规范没有解决发现问题LDAP服务器。

JNDI API教程给出了一个如何配置InitialContext的例子与一个LDAP URL列表[JNDI - 多个URL]。

17.5.5.2在LDAP服务器中注册JMXServiceURL (Registering a JMXServiceURL in the LDAP server)

本规范不在目录树上强制注册任何结构JMX服务URL。 假定JMX API代理知道在哪里注册它连接器,无论是从配置,或从一些内置的逻辑适应的它正在运行的环境。 这个规范定义了数据的形式即在目录中注册(如何而不是在哪里),以便任何JMX远程API客户端可以以通用的方式查找它。 参见代码示例17-12。
CODE EXAMPLE 17-12 Registering a JMXServiceURL in the LDAP server

import javax.naming.InitialContext;
import javax.naming.directory.DirContext;
import javax.naming.directory.Attribute;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttributes;
...
// Create initial context
Hashtable env = new Hashtable(11);
env.put(InitialContext.PROVIDER_URL, ldapServerUrls);
env.put(...);
InitialContext root = new InitialContext(env);
// Assuming that the Directory Administrator has created a
// context for this agent, get the DN of that context
// from configuration (e.g. Java property)
// String myOwnLdapDN =
// System.getProperty("com.sun.jmx.myapplication.dn");
String myOwnLdapDN = ....
DirContext myContext = (DirContext)root.lookup(myOwnLdapDN);
// Create connector server
JMXServiceURL jmxUrl = new
JMXServiceURL("service:jmx:jmxmp://localhost:9999");
JMXConnectorServer connectorServer =
JMXConnectorServerFactory.newJMXConnectorServer(jmxUrl, null, null);
// Prepare attributes for register connector server
Attributes attrs = new BasicAttributes();
// Prepare objectClass attribute: we’re going to create
// a javaContainer (STRUCTURAL) containing a
// jmxConnector (AUXILIARY).
Attribute objclass = new BasicAttribute("objectClass");
objclass.add("top");
objclass.add("javaContainer");
objclass.add("jmxConnector");
attrs.put(objclass);
// Add jmxServiceURL of the connector.
attrs.put("jmxServiceURL",jmxUrl.toString());
// Add jmxAgentName
attrs.put("jmxAgentName","MyAgentName");
// Add optional attributes, if needed
attrs.put("jmxProtocolType","jmxmp");
attrs.put("jmxAgentHost",InetAddress.getLocalHost().getHostName());
// Now create the sub context in which to register the URL
// of the JMXMP connector.
// (we assume that the subcontext does not exist yet -
// ideally the agent should contain some more complex logic:
// if the context already exists, simply modify its attributes,
// otherwise, create it with its attributes).
myContext.createSubcontext("cn=service:jmx:rmi", attrs);
17.5.5.3从LDAP服务器查找JMX服务URL(Looking up a JMX Service URL From the LDAP Server)

CODE EXAMPLE 17-13 Looking up a JMX Service URL From the LDAP Server

import javax.naming.InitialContext;
import javax.naming.NamingEnumeration;
import javax.naming.directory.DirContext;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchResult;
import javax.naming.directory.SearchControls;
...
// Create initial context
Hashtable env = new Hashtable();
env.put(InitialContext.PROVIDER_URL, ldapServerUrls);
env.put(...);
InitialContext root = new InitialContext(env);
// Prepare search filter
String filter = "(&(objectClass=jmxConnector) (jmxServiceURL=*))";
// Prepare the search controls
SearchControls ctrls = new SearchControls();
// Want to get all jmxConnector objects, wherever they’ve been
// registered.
ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// Want to get only the jmxServiceURL (comment this line and
// all attributes will be returned).
ctrls.setReturningAttributes(new String[] { "jmxServiceURL" });
// Search...
final NamingEnumeration results = root.search("", filter, ctrls);
// Get the URL...
for (;results.hasMore();) {
final SearchResult res = (SearchResult) results.nextElement();
final Attributes attrs = res.getAttributes();
final Attribute attr = attrs.get("jmxServiceURL");
final String urlStr = (String)attr.get();
// Make a connector...
final JMXServiceURL url = new JMXServiceURL(urlStr);
final JMXConnector conn =
JMXConnectorFactory.newConnector(url,null);
// Start using the connector...
conn.connect(null);
...
}

17.6向标准机构注册(RegistrationWith Standards Bodies)

在完成本规范的同时,还有以下注册由标准组织制定:
对于SLP,正在注册jmx服务类型和关联的服务模板与IANA
对于LDAP,第17.5.1节“LDAP”中定义的查找属性的OID在Sun的定义中,定义了JMX连接器的注册模式OID命名空间
对于Jini网络技术,正在查找属性的条目通过Jini社区决策流程(JDP)进行定义

18.环境参数汇总

A.服务模板277
A.1服务的服务模板:jmx抽象服务类型277
A.2服务的服务模板:jmx:jmxmp具体服务类型279
A.3服务的服务模板:jmx:rmi具体服务类型280
A.4服务的服务模板:jmx:iiop具体服务类型282
B.非标准环境参数285

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值