当前位置:Gxlcms > mysql > 通过JXTA进行无线通信,第2部分:实现JXTA

通过JXTA进行无线通信,第2部分:实现JXTA

时间:2021-07-01 10:21:17 帮助过:39人阅读

在本系列的第一篇文章中,介绍了支持 Java 2 Platform, Micro Edition(J2ME)的设备为什么不能直接承载进行企业通信的客户端应用程序。第一篇文章还讨论了如何用 JXTA 技术在企业通信解决方案中集成瘦移动客户机(thin mobile client),并展示了如何在 J2M

在本系列的第一篇文章中,介绍了支持 Java 2 Platform, Micro Edition(J2ME)的设备为什么不能直接承载进行企业通信的客户端应用程序。第一篇文章还讨论了如何用 JXTA 技术在企业通信解决方案中集成瘦移动客户机(thin mobile client),并展示了如何在 J2ME 设备中使用 JXTA 协议。

本系列的第二篇文章将展示如何用 JXTA(JXTA-for-JMS,或简称 JXTA4JMS)实现 J2ME 客户机与 JMS 应用程序之间的连接。该 JXTA4JMS 实现包含两个组件:一个组件运行在台式计算机上(桌面端 JXTA4JMS),另一个组件在基于 J2ME 的移动设备上运行(移动端 JXTA4JMS)。

本文首先将用几个实例展示 J2ME 设备或者 JMS 客户机如何使用 JXTA4JMS。然后介绍 JXTA4JMS 架构,并说明 JXTA4JMS 实现中的类的部署情况。最后,本文将展示实际的实现,并提供一个可以工作的应用程序,该程序在 Java Message Service(JMS)应用程序中集成了瘦 J2ME 客户机。

考虑一家用 JMS 完成其企业范围通信需求的公司。这家公司安装了 JMS 提供者(一种消息中间件),使用 JMS 网络的雇员就是通信的客户机(消息的生产者或消费者)。公司希望通信客户机彼此保持连接。当所有用户可以访问他们的 PC 时,JMS 网络就可以满足这项要求。

当用户不能访问他们的 PC 时(例如,当他们不在办公室时),就需要使用 JXTA4JMS。假设 JMS 用户 Alice 离开了办公室。她启用了桌面上和手机上的 JXTA4JMS。桌面端的 JXTA4JMS 开始监听收到的 JMS 消息。每当收到 Alice 的 JMS 消息,它就将这个消息转发给 Alice 的手机。与此类似,当 Alice 要向同事发送消息时,她使用移动端 JXTA4JMS 发送这条 JMS 消息。消息接收者像接收普通 JMS 消息那样接收这条消息。JXTA4JMS 帮助 Alice 保持连接,并用支持 J2ME 的手机继续通过 JMS 网络收发消息。

注意 JXTA4JMS 的一项重要功能:它不干扰其他 JMS 用户的工作。如果 Alice 不想再使用 JXTA4JMS,只要禁用它就行了。JXTA4JMS 对于其他 JMS 用户来说是透明的,他们不受 JXTA4JMS 的影响,因此不会知道 Alice 是启用还是禁用了 JXTA4JMS。

如您所见,JXTA4JMS 可以处理 JMS 客户机和移动客户之间的双向通信。让我们分别考虑这两种用例,以便清楚地阐明 JXTA4JMS 的功能。


回页首

从 JMS 客户机到 J2ME 客户机的通信

假定 Bob 要向 Alice 发送 JMS 消息。Bob 用他的桌面计算机发送这个消息。Bob 不知道 Alice 这个时候不在她的办公室,也不知道她启用了桌面计算机和 J2ME 手机上的 JXTA4JMS。

在 Alice 启用桌面计算机和手机中的 JXTA4JMS 之后,这两端都做好了进行消息交换的准备:

  1. 桌面端 JXTA4JMS 开始监听 JMS 提供者上 Alice 队列中的 JMS 消息。

  2. 移动端 JXTA4JMS 创建一个输入 JXTA 管道,并开始监听传入的消息。桌面端 JXTA4JMS 搜索这个管道,将它当成一个输出管道,并用它向移动电话发送消息。因此,这个管道被用来从桌面向移动电话发送消息。注意,JXTA 管道是有两个端口(入口和出口)的虚拟通信信道。创建这个管道的一方位于入口,搜索它的一方位于出口。

  3. 需要另一个 JXTA 管道从手机向桌面发送消息。桌面端 JXTA4JMS 创建了这个管道,并开始监听管道中来自移动端 JXTA4JMS 的消息。移动端 JXTA4JMS 搜索这个管道,并用它向桌面发送 JMS 消息。

现在,有两个 JXTA 管道:一个用于从桌面端发送到移动端的消息,另一个用于从移动端发送到桌面端的消息。

现在看看当 Bob(JMS 用户)向 Alice(JXTA4JMS 用户)发送消息时会发生什么。图 1 以图形方式显示了一系列事件。


图 1. 从 JMS 客户机向 J2ME 客户机发送消息

  1. Bob 向 JMS 提供者的 Alice 的队列发送一条 JMS 消息。

  2. Alice 的 PC 上的 JXTA4JMS 负责持续监听 Alice 队列中的消息,因此 JXTA4JMS 接收到了来自 Bob 的 JMS 消息。

  3. JXTA4JMS 现在要将这条 JMS 消息转换成可以通过 JXTA 网络传送的 JXTA 格式。Alice 的移动客户机用 JXME 与 JXTA 网络通信。回想一下我们在本系列第一篇文章中讨论过的内容,JXME 客户机不直接与 JXTA peer 通信,它们通过中继器与 JXTA 网络通信。不过,这个过程对于 JXTA peer 来说是透明的,它们永远也不知道某一条消息是通过中继器转发的,还是直接到达最终接收者的。这就是为什么 JXTA4JMS 实现生成 JXTA 消息(而不是 JXME 消息,在第一篇文章中介绍了它的结构)的原因。

  4. JXTA4JMS 通过移动端 JXTA4JMS 创建的 JXTA 管道向 JXRA 中继器发送消息。由 JXTA 网络负责通过输出管道和通过中继器正确传递消息。

  5. 在将消息发送给移动客户机之前,JXTA 中继器自动将消息从 JXTA 格式转换为 JXME 格式。

  6. Alice 手机中的移动端 JXTA4JMS 持续地监听同一管道中的消息。所以它会接收到 Bob 的消息。

  7. JXTA4JMS 会在 Alice 的手机屏幕上显示这条消息。

从 J2ME 客户机到 JMS 客户机的通信

现在,看看 JXTA4JMS 是如何帮助 Alice 向 Bob 发送回复消息的。图 2 以图形方式展示了这个场景。


图 2. 从 J2ME 客户机向 JMS 客户机发送消息

图 2 中的这个场景与图 1 中的类似:

  1. Alice 写下回复给 Bob 的消息,并让手机中运行的移动端 JXTA4JMS 将这条消息发送给 Bob。

  2. JXTA4JMS 将 Alice 的消息包装为第一篇文章中讨论过的 JXME 消息格式。这条 JXME 消息还包装了 JMS 消息接收者的名字(在这里是 Bob)。

  3. JXTA4JMS 将消息发送给 JXTA 中继器。注意,这条消息是通过 Alice 的桌面端 JXTA4JMS 创建的 JXTA 管道发送的。

  4. JXTA 中继器将 JXME 格式转换为 JXTA 格式。

  5. JXTA 中继器通过 JXTA 网络将 JXTA 消息发到 Alice 的桌面,同时,在 Alice 的桌面端,JXTA4JMS 已经启用。

  6. 桌面端 JXTA4JMS 接收到来自中继器的消息。注意,消息现在是 JXTA 格式的。

  7. 桌面端 JXTA4JMS 将消息转换为 JMS 格式。它提取出接收 Alice 所发出消息的 JMS 客户用户名(在这里是 Bob),而且还将搜索 JMS 提供者上的 Bob 队列。

  8. 桌面端 JXTA4JMS 将消息发送到 JMS 提供者上的 Bob 队列。

  9. Bob 的客户端 JMS 应用程序监听 Bob 的队列,并接收到来自 Alice 的消息。


回页首

JXTA-for-JMS 架构

让我们分析一下 JXTA4JMS 实现的架构。正如在 JXTA4JMS 使用模型的讨论中已经看到的,JXTA4JMS 实现由桌面实现和移动端实现组成。现在来分析一下这两个实现。

桌面端实现由多个层组成,如图 3 所示。分层的架构可以确保 JXTA4JMS 有更好的重用性。您可以根据自已的需求很容易地修改 JXTA4JMS 的任何层,并在其他层保持不变的情况下使用它。


图 3. 桌面端 JXTA4JMS 层

下面将介绍 JXTA4JMS 的每一层:

  • 监听器层监听来自 JMS 网络的 JMS 消息,以及来自 J2ME 设备的 JXTA 消息。监听器层还利用 JMS 提供者建立 JMS 连接,并在 JXTA 网络上创建管道,以便与 J2ME 移动设备进行通信。这一层只包含一个名为Listener 的类。

  • 路由器层拥有将 JMS 消息路由到 JMS 设备(通过 JXTA 网络)的方法,以及将 J2ME 消息路由到 JMS 客户机的方法。这一层由一个名为 Router 的类组成。Listener 监听收到的消息,并用 Router 类正确路由消息。

  • 格式转换层将 JMS 消息和 JXTA 格式相互转换。这一层由两个类组成:JXTAToJMSMsgConverter JMSToJXTAMsgConverter。在将消息路由到目的地之前,Router 类用格式转换类来转换这些消息。

  • 网络层处理使用 JMS 和 JXTA 网络时的低级细节。这一层由类 JXTASearchJMSSearch 组成。JXTASearch 类搜索由 J2ME 设备创建、并通过 JXTA 网络发布的 JXTA 管道。可以用这个管道向 J2ME 设备发送消息。JMSSearch 类搜索 JMS 用户(J2ME 客户消息的接收者)的 JMS 队列。ListenerRouter 类使用 JXTASearchJMSSearch 这两个类。

JXTA4JMS 的移动端实现负责处理无线通信(从桌面端 JXTA4JMS 实现消息的接收和发送)。下面介绍的 4 个类可以在分层架构中工作,以便执行消息传递任务。

  • JXMEMIDlet 类是我编写的一个 MIDlet 示例应用程序,用于展示如何使用一个有简单用户界面的 JXTA4JMS 通信客户机。

  • JXTA4JMSMessagingClient 类是 J2ME 端实现中最重要的类。JXTA4JMSMessagingClient 类提供所有低级功能,比如连接到中继器,以及交换和处理消息。这使高层的应用程序可以只关注用户界面内容。

  • JXTA4JMSMessage 类生成 JXTA4JMS 消息,并对其进行处理。JXTA4JMS 消息是为这种应用程序定制的 JXME 消息。JXTA4JMSMessagingClient 使用这个类生成和处理 JXTA4JMS 消息。

  • DataStore 类是低级工具类,它存储用于接收来自 JMS 端实现的消息的管道标识符。DataStore 类处理使用 J2ME record store 的所有低级细节。JXTA4JMSMessagingClient 类使用这个工具类存储和接收管道标识符。


回页首

配置 JXTA4JMS

在实现 JXTA4JMS 之前,应当首先考虑如何在 JXTA 网络中配置 JXTA4JMS。这有助于了解 JXTA4JMS 如何使用 JXTA 集合点和中继器。

JXTA4JMS 需要运行三个 JXTA 实例。第一个实例表示 JXTA 网络上的 JMS 用户(例如 Alice)。这个 JXTA 实例是 JXTA4JMS 实例的一部分。

第二个实例是一个集合点。集合点是所有 JXTA peer 聚会的地方。集合点将缓存 JXTA 广告,使 JXTA peer 可以发现网络上的其他 peer。我将在后面介绍集合点。

第三个实例是一个 JXTA 中继器,移动 JXTA4JMS 用它在 JXTA 网络上进行通信。

这三个 JXTA 实例的配置要求各有不同,我会在稍后说明。在真实的应用程序中,这三个实例运行在通过某种网络技术(如 Internet)彼此连接的不同计算机上。

为了观察可能没有连接到任何网络的单机中运行的 JXTA4JMS,可以让这三个实例运行在同一台计算机上。这个 JXTA4JMS 实现并不关心不同的实例是运行在同一计算机中,还是运行在不同的计算机中。您还可以配置同一个 JXTA 实例,让它起到两个或者三个实例的作用。

我将本文的源代码打包在 wi-jxta2source.zip 文件中,在那里还可以找到一个 readme 文件,该文件解释了我是如何测试 JXTA4JMS 的。

在第一次运行 JXTA 实例时,会看到 JXTA 配置窗口。对每一个实例,只需要填写一次配置窗口中的字段即可。配置窗口由 basic、advanced、rendezvous/relays 和 security 选项卡组成。对于这三个 JXTA 实例,basic 和 security 选项卡中的项是相同的。图 4 显示了 basic 选项卡。


图 4. JXTA 配置窗口中的 basic 选项卡

basic 选项卡有一个用于输入 peer 名的文本框。例如,Alice 在配置在 JXTA 网络上表示她的 JXTA 实例时,在这个文本框中填入字符串“Alice”。您配置集合点和 relay peer 时,可以使用任何名字。

basic 选项卡还包含一个名为“Use proxy server”的选择框。只有当您位于防火墙后时,才需要选中这个选择框。

security 选项卡包含输入 JXTA 实例的登录信息的字段。图 5 显示了包含 secure username 和 password 字段的 security 选项卡。


图 5. JXTA 配置窗口里的 security 选项卡

secure username 与 basic 选项卡中的 peer name 字段不同。peer name 字段指定 JXTA 网络中 peer 的名字,而 secure username 字段表示特定 JXTA 实例的登录名。每次启动 JXTA 实例时,都需要 secure username 和 password。

为了简便起见,可以使用同一个名字(如 Alice)作为 peer name 和 secure username 字段的值。

现在让我们看看如何配置 advanced 选项卡。advanced 选项卡有两部分,一部分用于 TCP,一部分用于 HTTP 设置,如图 6 所示。TCP 和 HTTP 设置包括这个 JXTA4JMS 实例监听的 IP 地址和端口号。默认情况下,JXTA 的配置是对 HTTP 使用端口 9700,对 TCP 使用端口 9701。


图 6. JXTA 配置窗口中的 advanced 选项卡

如果三个 JXTA 实例使用不同的计算机,那么只需要指定每台计算机的 IP 地址即可。如果在同一计算机中运行多个 JXTA 实现,那么需要对每个 JXTA 实例使用不同的 TCP 和 HTTP 端口号。

例如,当我在一台计算机中试验这个应用程序时,我将 Alice 的 JXTA4JMS 实例的 HTTP 和 TCP 端口号分别配置为 9700 和 9701。我将第二个 JXTA 实例配置为既是集合点,又是 relay peer。第二个实例分别监听 HTTP 和 TCP 的端口号 9702 和 9703。

在配置集合点和 relay peer 时,必须在 advanced 选项卡中再做一件或者几件事:单击 HTTP settings 部分中的 Enable Incoming Connections 选择框。这使其他 peer 可以与集合点和 relay peer 建立 HTTP 连接。

如图 7 所示的 rendezvous/relays 选项卡包含两部分,一部分用于集合点,一部分用于 relay peer。


图 7. JXTA 配置窗口中的 rendezvous/relays 选项卡

在配置 relay peer 时,必须选择 rendezvous/relays 选项卡中的 Act as a JxtaProxyAct as a Relay 选择框。在配置集合点时,必须选择Act as a Rendezvous 选择框。

还需要为其他两个 JXTA 实例( JXTA4JMS 实例和中继器实例)提供集合点实例的 IP 地址和端口号。为此,必须在 Available TCP rendezvousAvailable HTTP rendezvous 字段中填入集合点的 IP 地址和端口号。

现在对集合点做一简单说明。真实的 JXTA 网络包含许多集合点。如果 Bob 和 Alice 通过 JXTA 网络进行通信,那么他们都至少要知道一个集合点。他们知道的不一定是同一个集合点。

如果 Bob 知道集合点 R1,而 Alice 知道集合点 R2,并且集合点 R1 和 R2 知道一个共同的集合点 R3,那么 Bob 和 Alice 就可以通过 JXTA 网络进行无缝通信。JXTA 网络自动管理 peer 的发现,Bob 和 Alice 不用关心这些细节。这是 JXTA 技术的一个重要特性。

现在让我们讨论桌面端 JXTA4JMS 实现的细节,然后再讨论移动端的 JXTA4JMS 实现。


回页首

实现桌面端 JXTA4JMS

我已经讨论了 JXTA4JMS 架构中不同层的功能,因此我现在将展示这些层的实现。因为是上层使用低层,所以我对实现的讨论是从下向上进行的。

JXTASearch 类

JXTASearch 类搜索 JXTA 网络中的特定管道。JXTASearch 类以同步方式搜索管道,这意味着对JXTASearch 类的调用在找到管道之后才会返回。要搜索的管道是由移动端创建、发布和监听的管道。这个管道将用于向 J2ME 设备发送消息。

JXTASearch 类包含一个构造函数和两个方法:getPipeAdvertisement() getOutputPipe()。此外,JXTASearch 类还实现了一个名为 OutputPipeListener 的接口。根据 JXTA 实现,任何想接收管道来创建通知的类都要实现OutputPipeListener 接口。OutputPipeListener 接口有一个名为outputPipeEvent() 的方法,它接收来自底层 JXTA 实现的管道解析通知(resolving notifiation)。很快您就会看到JXTASearch 类是如何实现outputPipeEvent() 方法的。

JXTASearch 构造函数

JXTASearch 构造函数(清单 1)采用了PeerGroup 类的一个实例。PeerGroup 类是与 JXTA peer 组交互的一个方便方式。它包含执行那些您可能想在 peer 组中执行的各种任务的方法。我会在后面描述如何使用这个类的两个方法。现在,只需要注意JXTASearch 构造函数只是将 PeerGroup 对象存储在类级的变量(名为 peerGroup),以便以后getPipeAdvertisement()getOutputPipe() 方法使用这些变量。


清单 1.JXTASearch 构造函数

 public JXTASearch ( PeerGroup peerGroup ){
        this.peerGroup = peerGroup;
    }

 



getPipeAdvertisement() 方法

清单 2 中显示的getPipeAdvertisement() 方法以管道名为参数,并搜索对应这个管道的广告。Listener 类在调用getPipeAdvertisement() 方法时指定管道的名称。

getPipeAdvertisement() 方法以 PipeAdvertisement 对象的形式返回管道的广告。


清单 2. getPipeAdvertisement() 方法

public PipeAdvertisement getPipeAdvertisement ( String targetPipeName ) {
        Enumeration enum = null;
        PipeAdvertisement pipeAdvert = null;
       
        DiscoveryService discoveryService = peerGroup.getDiscoveryService ();
        try
        {
            while( true ){
                discoveryService.getRemoteAdvertisements (
                                                     null, 
                                                     DiscoveryService.ADV, 
                                                     "Name", 
                                                     targetPipeName, 
                                                     5
                                                     );
                enum = discoveryService.getLocalAdvertisements (
                                                           DiscoveryService.ADV, 
                                                           "Name", 
                                                           targetPipeName
                                                           );
                
                if ( enum != null)
                {
                    while (enum.hasMoreElements()) 
                        pipeAdvert = ( PipeAdvertisement ) enum.nextElement ();
                }//if(enum != null)
                
                if ( pipeAdvert != null ) 
                    break;
        
            }//while( true )
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
        
        return pipeAdvert;
        
    }//getPipeAdvertisement() 



getPipeAdvertisement() 方法中的第一项工作是调用存储在构造函数中的 PeerGroup 对象的getDiscoveryService() 方法。这个getDiscoveryService() 方法调用将返回DiscoveryService 对象。

DiscoveryService 类表示一种称为发现服务的 JXTA 服务。JXTA peer 利用发现服务搜索 JXTA 网络上的不同资源。我使用 JXTA 发现服务搜索指定 peer 组中的特定管道。

如果想使用 JXTA 发现服务,那么需要编写 XML 搜索查询,并将查询发送给前面配置 JXTA4JMS 中讨论过的集合点。而且,必须处理搜索查询找到的消息。

不过,JXTA 实现可以使您不用做这项工作。DiscoveryService 类很好地包装了发现服务的功能。只需调用 DiscoveryService 类的几个方法作出响应即可,不用担心 JXTA 所要求和使用的 XML 语法。JXTA 实现负责这些底层细节。

获得 DiscoveryService 对象后,可以用它的方法执行管道搜索操作。DiscoveryService 类有一个名为getRemoteAdvertisements() 的方法,它向 JXTA 网络发送搜索查询。getRemoteAdvertisements() 方法有 5 个参数:

  • 第一个参数是一个标识符。如果知道要搜索的管道的标识符,那么可以将这个标识符作为第一个参数提供给 getRemoteAdvertisements() 方法,以加快远程搜索。不过,在这里,我们不知道要搜索的管道的标识符。因此将 null 作为第一个参数传递给getRemoteAdvertisements() 方法调用。

  • 第二个参数指定要搜索的资源类型(例如,广告、peer 或者 peer 组等)。我们搜索的是管道广告,因此只需要使用 DiscoveryService 类的一个名为ADV 的静态字段。如果搜索 peer 或者 peer 组,那么还要分别指定PEER 或者 PEERGROUP 静态字段。

  • 第三个参数是要搜索的属性的名字。要搜索的是具有特定名字的管道广告。因此,要搜索带有 “Name” 属性的特定值的管道。这类似于在第一篇文章的“JXME 编程”一节中讨论的search() 方法的第二个参数。因此,我们将字符串“Name”作为第三个参数传递给getRemoteAdvertisements() 方法调用。

  • 第四个参数是要搜索的管道名(或者可以说 “Name” 属性的值)。只需将管道的名字作为值传递即可。

  • getRemoteAdvertisements() 方法的最后一个参数是要接收的搜索结果的最大数量。您不知道有多少 peer 会响应这个搜索查询,以及每一个 peer 会返回多少个响应。因此,要对搜索操作进行限制。将“5”作为最后一个参数的值传递,这表示从每一个 peer 接收最多 5 个结果。

getRemoteAdvertisements() 方法不返回任何东西。它将搜索结果存储在由 JXTA 实现维护的本地缓冲中。因此,在调用getRemoteAdvertisements() 方法后,必须调用另一个getLocalAdvertisements() 方法以从本地缓冲区中提取搜索结果。

getLocalAdvertisements() 方法有三个参数:资源类型、属性名和属性值。这三个参数与 getRemoteAdvertisements() 方法的第二、三、四个参数是相同的。

getLocalAdvertisements() 方法返回一个 Enumeration 对象。Enumeration 对象包含以几个Advertisement 对象的形式出现的搜索结果。

getLocalAdvertisement() 方法返回的 Enumeration 对象会有不同类型的 Advertisement 对象。因为搜索的是管道广告,可以预计,如果 Enumeration 对象不是 null,那么它将包括一个或者多个PipeAdvertisement 对象。PipeAdvertisement 对象表示管道广告。

让我们遍历整个 Enumeration 并取出最后的广告,因为最后一个也是最新的。

getOutputPipe() 方法

清单 3 中所示的 getOutputPipe() 方法用一个 PipeAdvertisement 对象作为参数。通常,这个参数与在getPipeAdvertisement() 方法中搜索的PipeAdvertisement 对象是相同的。

getOutputPipe() 方法返回一个 OutputPipe 对象。这个 OutputPipe 对象表示管道广告对应的 JXTA 管道。


清单 3. getOutputPipe() 方法

    public OutputPipe getOutputPipe (PipeAdvertisement pipeAdvert){
        try
        {
            PipeService pipeSvc = peerGroup.getPipeService ();
            pipeSvc.createOutputPipe ( pipeAdvert, this );
            while ( true )
            {
                if ( outputPipe != null )
                    break;
            }//while (true)
         
         return outputPipe;        
        
        }//try
        catch ( Exception e ){
             e.printStackTrace();
        }//catch
         return null;
    }//getOutputPipe()

正如需要使用 JXTA 发现服务来搜索管道广告一样,这里需要使用 JXTA 管道服务创建一个输出管道。JXTA 实现提供了一个名为 PipeService 的类,它包装了 JXTA 管道服务的功能。用PeerGroup 类的getPipeService() 方法可以得到 PipeService 对象。

PipeService 类包含 createInputPipe()createOutputPipe() 方法,可以用它们来创建进行消息交换的输入和输出管道。PipeService 类的createOutputPipe() 方法有两个参数:PipeAdvertisementOutputPipeListener 对象。它用这个广告创建一个 JXTA 管道,并返回一个OutputPipe 对象。

JXTA 管道创建过程是在 JXTA 网络上完成的。我不会讨论这些细节,不过 createOutputPipe() 方法不会等待管道创建好后才返回,所以它是异步返回的。

JXTA 网络随后以管道解析(resolving)事件的形式确认管道的创建。根据 JXTA 实现,只有实现 OutputPipeListener 接口的类才可以接收管道解析事件通知。OutputPipeListener 接口只包含一个名为outputPipeEvent() 的方法。JXTASearch 类实现了outputPipeEvent() 方法,因此它可以接收通知。

通知到达时,outputPipeEvent() 方法就获得了控制权。outputPipeEvent() 实现很简单,清单 4 显示它只有一行。outputPipeEvent() 方法在名为outputPipe 的类级对象中存储新创建的管道。


清单 4. outputPipeEvent() 方法
    public void outputPipeEvent ( OutputPipeEvent e ) {
        outputPipe = e.getOutputPipe ();
    }//outputPipeEvent()

为了简化 JXTASearch 类的使用,我将 getOutputPipe() 方法封闭在一个无限 while 循环中,只有通知到达时,循环才会中断。这个无限 while 循环不断检查outputPipeEvent() 方法是否设置了outputPipe 类级对象。当 getOutputPipe() 方法发现所需要的OutputPipe 对象时,就会返回这个对象。

JXTAToJMSMsgConverter 类

JXTAToJMSMsgConverter 类使用 JXTA 消息,并将它转换为相应的 JMS 消息。JXTAToJMSMsgConverter 类包含 4 个方法(一个构造函数和三个 getter 方法),这些方法的具体说明如下:

JXTAToJMSMsgConverter 构造函数

清单 5 显示了 JXTAToJMSMsgConverter 构造函数。


清单 5. JXTAToJMSMsgConverter 构造函数
public JXTAToJMSMsgConverter (
                              QueueConnectionFactory qConnFactory, 
                              net.jxta.endpoint.Message jxtaMsg
                            ) throws JMSException {
          QueueConnection queueConnection = null;
  
          try
          {
              //Creating a new JMS text message.
              //Step 1:
              queueConnection = qConnFactory.createQueueConnection ();
              //Step 2:              
              queueSession = 
                     QueueConnection.createQueueSession (
                                               false, 
                                               Session.AUTO_ACKNOWLEDGE );
              //Step 3:
              jmsMessage = queueSession.createTextMessage ();
          }
          catch ( Exception e ) {
              e.printStackTrace ();
          }
        MessageElement jmsRecipientElement, msgElement;
        jmsRecipientElement = jxtaMsg.getMessageElement ("JMSRecipient");
        msgElement = jxtaMsg.getMessageElement ("Message");
        jmsRecipient = jmsRecipientElement.toString();
        jmsMessage.setText (msgElement.toString());
        
    }//JXTAToJMSMsgConverter


这个构造函数有两个参数,一个名为 qConnFactoryQueueConnectionFactory 对象,以及一个名为jxtaMsgnet.jxta.endpoint.Message 对象。我将进一步分析这些对象。

Listener 类实例化了一个 QueueConnectionFactory 对象,并将这个对象作为 JXTAToJMSMsgConverter 构造函数的第一个参数进行传递。QueueConnectionFactory 是一个连接工厂和一个 JMS 管理的对象。根据 JMS 架构,需要一个QueueSession 对象来创建新的 JMS 消息。同时还需要一个队列连接工厂(一个QueueConnectionFactory 对象)来创建QueueSession 对象。在文章的后面部分,可以看到如何用连接工厂创建 JMS 消息。

net.jxta.endpoint.Message 对象(JXTAToJMSMsgConverter 构造函数的第二个参数)是将转换为 JMS 格式的 JXTA 消息。看一看JXTAToJMSMsgConverter 构造函数是如何工作的。

必须先创建一个新的 JMS 消息对象,这分为三步。首先,调用 QueueConnectionFactory 对象的 createQueueConnection() 方法。这会提供一个QueueConnection 对象。然后调用QueueConnection 对象的 createQueueSession() 方法,它会给出一个QueueSession 对象。在第三步,调用QueueSession 对象的 createTextMessage() 方法,它返回一个javax.jms.TextMessage 对象。这个javax.jms.TextMessage 对象名为 jmsMessage。最后要用来自 JXTA 消息的数据填充这个 JMS 消息对象。注意清单 5 “创建一个新 JMS 文本消息”中的这三步。

在这里要注意的是,QueueSession 对象有创建不同类型的 JMS 消息的方法。例如,可以用 QueueSession 类的createByteMessage() 方法创建二进制形式的消息。不过,我选择使用createTextMessage() 方法,因为我想在本系列文章中展示文本消息的交换。

现在看一下如何从收到的 JXTA 消息中提取数据,并用这些数据填充 JMS 消息。

收到的 JXTA 消息包含两部分:消息接收者的名字和消息本身。JXTA 消息的每一部分在 JXTA 消息中都是一个元素。消息接收者的名字包装在名为 “JMSRecipient” 的元素中。消息包装在名为 “Message” 的元素中。

可以调用 net.jxta.endpoint.Message 类的 getMessageElement() 方法,并同时传递元素的名字。getMessageElement() 方法返回一个MessageElement 对象,它表示 JXTA 消息的一个元素。

我将在后面介绍 JXTA 消息中的每个元素的结构。现在,只需注意清单 5 中两个MessageElement 对象即可,这两个对象分别名为msgElement(为包装消息的对象)和 jmsRecipientElement(为包装接收者名字的对象)。

现在可以调用每一个 MessageElement 对象的 toString() 方法。这个方法以字符串的形式返回MessageElement 的内容。

然后可以调用 jmsMessage 对象(即前面创建的 javax.jms.TextMessage 对象)的setText() 方法,同时传递 JXTA 消息内容。这会在jmsMessage 对象中设置 JXTA 消息内容。jmsMessage 对象现在就准备好了。

getQueueSession() 方法

getQueueSession() 方法(如清单 6 所示)返回在构造函数中创建的 QueueSession 对象。可以看到Router 类是如何调用getQueueSession() 方法来提取 QueueSession 对象的。Router 类用这个QueueSession 对象将文本消息发送给 JMS 接收者。


清单 6. getQueueSession() 方法

    public QueueSession getQueueSession() {
        return queueSession;
    }//getQueueSession()


getMessage() 方法

getMessage() 方法(如清单 7 所示)返回在构造函数中创建并填充的 javax.jms.TextMessage 对象,该对象名为jmsMessage


清单 7. getMessage() 方法
    public javax.jms.TextMessage getMessage() {
        return jmsMessage;
    }//getMessage()



getJMSRecepient() 方法

清单 8 中所见的 getJMSRecipient() 方法返回接收者的名字,这个名字可以从前面构造函数中传入的 JXTA 消息中提取。

还要注意的是,在清单 8 中,已经将字符中 “jms/” 作为前缀串添加到 JMS 接收者的名字上。然后用接收者的名字作为 JMS 队列名向队列发送消息。“jms/” 字符串遵守了 Java 规范,所以 JMS 队列的名称是以“jms/” 字符串开头的。


清单 8. getJMSRecipient() 方法
    public String getJMSRecipient() {
        return "jms/" + jmsRecipient;
    }//getJMSRecipient()

JMSToJXTAMsgConverter 类

JMSToJXTAMsgConverter 是消息格式转换器类,它采用了一个 JMS 消息,并生成一个 JXTA 消息。JMSToJXTAMsgConverter 类包含下面介绍的这些方法。

JMSToJXTAMsgConverter 构造函数

现在看看清单 9,它展示了 JMSToJXTAMsgConverter 构造函数。


清单 9. JMSToJXTAMsgConverter 构造函数

    public JMSToJXTAMsgConverter ( String sender, 
                                   TextMessage jmsMsg
                                 ) throws JMSException {
        StringMessageElement smeSender  = null;
        StringMessageElement smeMessage = null;
        jxtaMessage = new net.jxta.endpoint.Message ();
        smeSender  = new StringMessageElement ("JMSSender", sender, null);
        smeMessage = new StringMessageElement ("Message", jmsMsg.getText(), null);
        jxtaMessage.addMessageElement (smeSender);
        jxtaMessage.addMessageElement (smeMessage);
    }//JMSToJXTAMsgConverter constructor


JMSToJXTAMsgConverter 构造函数几乎执行这个类所要求的所有处理。它有两个参数,第一个参数是发送 JMS 消息的 JMS 客户机的名称。在构造函数JMSToJXTAMsgConverter 中生成的 JXTA 消息里加入发送者的名字。这使 J2ME 接收者知道是哪一个 JMS 客户机发送的消息。第二个参数是以TextMessage 对象形式出现的 JMS 消息。

JXTA 消息是根据收到的 JMS 消息生成的。JXTA 消息由几个消息元素组成。因此,我首先分析 JXTA 消息中每个元素的内部细节。

JXTA 消息元素的结构类似于(第一篇文章的“中继器和 JXME 客户机”一节中介绍的)JXME 消息元素的结构。

JXTA 消息元素包含以下 4 个字段:

  • 消息元素的名字。 在这里,在一个名为 “Message” 的元素中包装 JMS 消息。与此类似,将 JMS 发送者的名字包装到一个名为“JMSSender” 的元素中。注意,元素的名称是可选的,因此可以忽略它。不过,为每个元素命名会使处理变得直观而且更容易实现。

  • 可选的消息元素 MIME 类型。 如果没有指定这个字段,那它的值就假定为 “Application/Octet-Stream”。在这里,所生成的消息的 MIME 类型是“text/plain”

  • 要发送的数据。 消息元素,如包含 JMS 消息内容的消息元素,包含 JMS 发送者名字的 sender 元素等。

  • 可选的签名。 这个字段包含消息元素的加密签名。目前,JXME 还不支持在 J2ME 一端添加签名。因此,在生成 JXTA 消息时没有必要添加签名。

您已经见过了 JXTA 消息元素的结构。JXTA 实现有一个名为 MessageElement 的类,它处理所有类型的 JXTA 消息元素。此外,还有一些从MessageElement 类派生的子类。每一个子类处理一种特定类型的消息元素。例如,StringMessageElement 类处理携带文本字符串的消息元素(这种消息的 MIME 类型是“text/plain”)。

StringMessageElement 类生成 JXTA 消息的每个消息元素。将 JMS 消息发送者的名字包装到名为 smeSenderStringMessageElement 对象中。与此类似,将 JMS 消息内容包装到名为 smeMessage 的另一个 StringMessageElement 对象中。

要用 StringMessageElement 类生成一个消息,必须先调用 StringMessageElement 类的构造函数实例化这条消息。这个构造函数有三个参数。第一个参数指定消息元素的名称,第二个参数指定元素数据的内容,第三个参数指定加密签名。我们没有使用签名,因此传递 null 作为第三个参数的值。

分析一下清单 9 中的JMSToJXTAMsgConverter 构造函数。先实例化名为smeSendersmeMessage 的两个StringMessageElement 对象。在创建smeMessage 对象时,需要使用传入的 JMS 消息的内容。可以用TextMessage 对象的 getText() 方法提取 JMS 消息内容,然后将内容传递给 StringMessageConstructor,以便生成smeMessage 对象。

现在,需要将这两个 StringMessageElement 对象包装到一个 JXTA 消息中。为此,必须先实例化一个名为 jxtaMessagenet.jxta.endpoint.Message 对象,该对象表示了一个完整的 JXTA 消息。然后,需要两次调用它的addMessageElement() 方法,对每个StringMessageElement 对象调用一次。

getMessage() 方法

用清单 10 所示的 getMessage() 方法提取 JXTA 消息。这个方法只返回在构造函数中准备的 jxtaMessage 对象。


清单 10. getMessage() 方法
    public Message getMessage () {
        return jxtaMessage;
    }//getMessage()

JMSSearch 类

JMSSearch 类在 JMS 网络中进行搜索,就像 JXTASearch 在 JXTA 网络中搜索一样。不过,在 JMS 网络中搜索是相当简单的,因为可以使用 Java Naming and Directory Interface(JNDI)搜索不同的 JMS 资源。您可以用 JNDI 搜索像消息对象(队列和连接工厂)、数据库和 mail 会话这样的资源。

这里用 JNDI 搜索队列。看一下清单 11 中的 JMSSearch 类。


清单 11. JMSSearch 类
public class JMSSearch
{
    public JMSSearch (){
    }
    public QueueSender getQueueSender ( String queueName, 
                                       QueueSession queueSession ) {
        InitialContext jndiContxt = null;
        Queue outgoingQueue= null;
        QueueSender qSender = null;
 
        try
        {
            jndiContxt = new InitialContext ();
            outgoingQueue= (Queue) jndiContxt.lookup ( queueName );
            qSender = queueSession.createSender (outgoingQueue);
        }//try
        catch ( Exception e ) {
            e.printStackTrace ();
        }//catch
        
        return qSender;
        
    }//getQueueSender()
}//JMSSearch


JMSSearch 类只包含一个方法 getQueueSender()。这个方法有两个参数:要搜索的队列名和一个QueueSession 对象。这与前面对JXTAToJMSMsgConvertergetQueueSession() 方法进行的讨论中介绍的QueueSession 对象是一样的。我会解释将QueueSession 对象作为参数传递的目的。

getQueueSender() 方法为这个特定的队列名返回 QueueSender 对象。

正如在清单 11 中看到的,getQueueSender() 方法的实现很简单。首先,它实例化一个 InitialContext 对象。这是 JNDI 的要求。InitialContext 对象包含进行搜索操作的方法。

然后,只需调用 InitialContext 类的 lookup() 方法即可。在 JNDI 术语中,搜索被称为查找(lookup)。lookup() 方法以队列名作为参数,返回一个表示所搜索队列的Queue 对象。Queue 对象被命名为outgoingQueue,因为这个 Queue 对象表示消息接收者的 JMS 队列。在后面要用这个队列发送输出消息。例如,假设 Alice 正在路上,带着她的移动端 JXTA4JMS,并向 Bob 发送了一条消息。她的桌面端 JXTA4JMS 收到通过 JXTA 网络传来的这条消息,然后在 JMS 网络中搜索 Bob 的队列,将 Bob 的队列作为outgoingQueue 对象处理,同时将 Alice 的消息发送给(Bob 的)outgoing 队列。

现在您认识到了在队列名前面加上前缀 “jms/” 的重要性了吧(回想一下在 JXTAToJMSMsgConverter 类的getJMSRecepient() 方法前添加的“jms/” 前缀)。JNDI 查询不仅搜索 JMS 资源(其他服务端模块用它搜索非 JMS 资源,如数据库),所以用某种标识符标识出属于特定组的资源(例如,所有属于 JMS 网络的资源)很重要。如果不在队列名前加上字符串前缀,那么可能会将队列名与一些其他非 JMS 网络资源相混淆(如数据库表)。

尽管搜索了所需要的 Queue 对象,但是这里还有一步操作要做。要用搜索到的 Queue 对象创建一个 QueueSender 对象。然后,Router 类就用这个 QueueSender 对象发送 Queue 中的消息。

要创建 QueueSender 对象,还需要一个 QueueSession 对象。这个 QueueSession 对象应当与在JXTAToJMSMsgConverter 类中生成 JMS 消息时使用的那个对象相同。Router 类同时使用JXTAToJMSMsgConverterJMSSearch 类,所以在下面讨论 Router 时,我将说明它如何确保将正确的QueueSession 对象传递给getQueueSender() 方法。

getQueueSender() 方法中做的最后一件事是调用 QueueSession 对象的 createSender() 方法。这个方法用我们刚刚创建的 Queue 对象作为参数,并返回一个 QueueSender 对象。QueueSender 现在就完成了它该做的操作。如果通过这个 sender 对象发送 JMS 消息,那么消息会自动达到正确的接收者。现在,看一下Router 类如何使用刚才创建的所有底层支持来完成路由。

Router 类

Router 类是 JXTA4JMS 通信的通信枢纽。Router 类使用一些低级的层(包括格式转换和网络),并在 JMS 网络与 JXTA 网络之间来回发送消息。Router 类的作用是双重的:

  • 它将从 JMS 网络收到的消息发送给移动客户机。
  • 同时将从 JXTA 网络接收到的消息发送给 JMS 客户机。

因此,它包含两个方法:sendMessageToMobile()sendMessageToJMS()Listener 类在收到来自 JMS 客户机的消息时调用sendMessageToMobile() 方法。sendMessageToMobile() 方法通过 JXTA 网络向 J2ME 客户机发送消息。与此类似,Listener 类在收到来自移动客户机的消息时使用sendMessageToJMS() 方法。

清单 12 显示了 Router 构造函数的实现。


清单 12. Router 构造函数
    public Router ( String peerName, PeerGroup peerGroup ) {
        this.peerName = peerName;
        this.peerGroup = peerGroup;
    }//Router

Router 构造函数的实现很简单。它只采用了两个参数(peer 的名字和它的 peer 组),并存储这些参数,以便 Router 类的两个方法使用它们。

sendMessageToMobile() 方法

清单 13 展示了 sendMessageToMobile() 方法的实现。


清单 13. sendMessageToMobile() 方法
    public void sendMessageToMobile( 
                                   String sender, 
                                   OutputPipe outputPipe, 
                                   TextMessage jmsMessage ) {
        try
        {
            JMSToJXTAMsgConverter jxtaConverter = 
                              new JMSToJXTAMsgConverter ( sender, jmsMessage );
            net.jxta.endpoint.Message jxtaMessage = jxtaConverter.getMessage ();
            outputPipe.send ( jxtaMessage );
        }//try
        catch ( Exception e ) {
            e.printStackTrace ();
        }//catch
    }//sendMessageToMobile()


sendMessageToMobile() 方法有三个参数:

  • JMS 发送者的名字,我们将发送者的 JMS 消息发送给移动客户机。

  • 一个表示 JXTA 管道的 OutputPipe 对象。(移动客户机监听这个管道的另一端,因此通过这个管道发送消息。)在后面讨论Listener 类时,我将说明创建这个OutputPipe 对象的机制。

  • 要发送给移动客户机的 JMS 消息。

很容易猜到,您必须做的就是将 JMS 消息从 JMS 格式转换为 JXTA 格式,然后通过输出管道发送这个消息。

因此,正如在清单 13 的sendMessageToMobile() 方法中看到的,首先实例化一个JMSToJXTAMsgConverter 对象,并用它获得传入的 JMS 消息的 JXTA 表达。然后,只需调用OutputPipe 对象的send() 方法,同时传递 JMS 消息即可。OutputPipe 对象处理通过 JXTA 网络发送消息的低级过程。

sendMessageToJMS() 方法

现在看一下清单 14,它展示了 sendMessageToJMS() 方法的实现。


清单 14. sendMessageToJMS() 方法
    public void sendMessageToJMS( QueueConnectionFactory qConnFactory, 
                                  net.jxta.endpoint.Message jxtaMessage )
    {
        try {
            JXTAToJMSMsgConverter jmsConverter = new JXTAToJMSMsgConverter (
                                                       qConnFactory,
                                                       jxtaMessage
                                                     );
            String jmsRecipient = jmsConverter.getJMSRecipient();
            TextMessage jmsMessage = jmsConverter.getMessage();
            QueueSession queueSession = jmsConverter.getQueueSession();
            
            JMSSearch jmsSearch = new JMSSearch ();
            QueueSender qSender = jmsSearch.getQueueSender ( jmsRecipient,
                                                             queueSession );
            
            qSender.send ( jmsMessage);
         
        }//try
        catch ( Exception e ){
            e.printStackTrace ();
        }//catch
        
    }//sendMessageToJMS



sendMessageToJMS() 方法采用了两个参数:QueueConnectionFactory 对象和将发送给 JMS 接收者的 JXTA 消息。回想一下,JXTAToJMSMessageConverter 构造函数也要求使用同样的两个对象。因此,sendMessageToJMS() 方法要实例化一个JXTAToJMSMsgConverter 对象,然后调用JXTAToJMSMsgConverter 类的 getJMSRecepient()getMessage() 方法,以便分别提取 JMS 消息的发送者的名字,以及所收到的 JXTA 消息的 JMS 格式。

sendMessageToJMS() 方法还调用了 JXTAToJMSMsgConverter 类的 getQueueSession() 方法。如前所述,这个方法将返回 QueueSession 对象。

然后 sendMessageToJMS() 方法实例化了一个 JMSSearch 对象,并调用该对象的 getQueueSender() 方法。getQueueSender() 方法用 JMS 消息接收者的名字(一个队列名)和QueueSession 对象作为参数。它返回一个QueueSender 对象(已在讨论 JMSSearch 类时介绍)。

最后,调用 QueueSender 对象的 send() 方法。send() 方法采用转换后的 JMS 消息,并将这条消息发送给 JMS 接收者。

Listener 类

Listener 类被设计成一个连续监听模块。需要将这个类设计为在启动后同时监听 JMS 和 JXTA 网络。当它收到 JMS 消息后,立即将这个消息通过 JXTA 网络转发给 J2ME 设备。而当它从 JXTA 网络收到 J2ME 设备发来的消息后,它会立即将这个消息转发给相关的 JMS 客户机。

Listener 类实现了一个 run() 方法,该方法在单独的线程中执行。这个线程不间断地监听来自 JMS 和 JXTA 网络的消息。我将展示如何在run() 方法中实现 JMS 和 JXTA 消息的处理逻辑。Listener 类包含一些专用辅助方法。这些方法有助于创建输入和输出 JXTA 管道。

createPipeAdvertisement() 方法

清单 15 中展示的 createPipeAdvertisement() 方法是一个专用辅助方法,它将创建一个管道广告,并以 PipeAdvertisement 对象的形式返回这个广告。


清单 15. createPipeAdvertisement() 方法
    private PipeAdvertisement createPipeAdvertisement (String peerName) {
        PipeAdvertisement pipeAd = null;
        try 
        {
            String fileName = peerName+".xml";
            File file = new File ( fileName );
            if ( file.exists() )
            {
                FileInputStream is = new FileInputStream (file);
                if ( is.available() > 0 )
                {
                    pipeAd =
                       (PipeAdvertisement) AdvertisementFactory.newAdvertisement(
                                           new MimeMediaType( "text/xml" ),
                                           is 
                                         );
                                         
                }//if ( is.available() > 0)
            }
            else
            {
                pipeAd = 
                     (PipeAdvertisement) AdvertisementFactory.newAdvertisement (
                                          pipeAd.getAdvertisementType ()
                                        );
                pipeAd.setName (peerName);
                pipeAd.setType ( "JxtaUnicast" );
                pipeAd.setPipeID( (ID)net.jxta.id.IDFactory.newPipeID(
                                               netPeerGroup.getPeerGroupID()
                                              )
                                            );
                                              
                FileOutputStream os = new FileOutputStream ( fileName );
                os.write ( pipeAd.toString().getBytes() );
                os.close();
            }//end of else        
            return pipeAd;
        
        }//try
        catch (Exception ex) {
            ex.printStackTrace ();
        }//catch
        return null;
    }//createPipeAdvertisement()



创建管道广告就是一个生成 XML 的任务。需要根据由 JXTA 协议定义的广告格式生成正确的 XML 代码。观察清单 16,它展示了管道广告的 XML 结构。


清单 16. 一个 XML 格式的管道广告示例



    
       urn:jxta:uuid-59616261646162614E50.....91E04
    
    
       JxtaUnicast
    
    
       AliceJMS
    


JXTA 提供了方便您生成 JXTA 广告的类。createPipeAdvertisement() 方法首先检查管道广告是否(由于以前的管道创建操作)已经存在于一个文件中。如果存在这样的文件,那么只需打开该文件并在文件输入流中读取其内容即可。然后,用一个名为AdvertisementFactory 的 JXTA 类从输入流创建一个广告。

AdvertisementFactory 类有一个名为 newAdvertisement() 的静态方法,它有两个参数。第一个参数指定所创建广告的 MIME 类型。在这里,要创建一个 XML 广告,所以 MIME 类型应当是“text/xml”。第二个参数指定从文件中创建的输入流。

newAdvertisement() 方法返回一个 Advertisement 对象,在将它返回给调用应用程序之前,可以将它强制转换为PipeAdvertisement

现在,看一看没有管道广告时如何做。当然,必须从头开始生成管道广告。为此,要重载 AdvertisementFactory 类的 newAdvertisement() 方法。这个方法只采用了一个参数,它指定要创建的广告的类型。newAdvertisement() 方法返回Advertisement 形式的管道广告。将Advertisement 对象强制转换为 PipeAdvertisement 对象。

现在设置刚创建的新管道广告的三个参数。这三个参数是要宣传的管道的名字、管道的类型(在这里是 JXTAUnicast)和管道标识符。我们已经知道前两个参数,但是需要一个名为IDFactory 的标识符工厂来创建新的标识符。这个工厂帮助创建惟一的标识符。

完成了新的广告后,将它保存到磁盘文件中并返回 PipeAdvertisement 对象。

publishPipeAdvertisement() 方法

清单 17 所示的 publishPipeAdvertisement() 方法是一个专用辅助方法,它采用了一个 PipeAdvertisement 对象,并通过 JXTA 网络发布它。


清单 17. publishPipeAdvertisement() 方法
   private boolean publishPipeAdvertisement (PipeAdvertisement pipeAd)
    {
        DiscoveryService discSvc = netPeerGroup.getDiscoveryService ();
        discSvc.remotePublish ( pipeAd, 
                                DiscoveryService.ADV, 
                                DiscoveryService.NO_EXPIRATION
                              );
        return true;
    }//publishPipeAdvertisement()

 



已经在一个 peer 组发布了管道广告。而且,所有广告都有一个失效时间。因此,这个广告会在特定的时间段内出现在特定的 peer 组中。

JXTA 提供了一种称为管道服务的服务,可以用它来发布管道广告。JXTA 实现在一个名为 PipeService 的类中包装了管道服务的功能。可以通过调用PeerGroup 对象的getPipeService() 方法实例化 PipeService 类。

只需调用 PipeService 类的 remotePublish() 方法就行了。remotePublish() 方法在 JXTA 网络上发布广告。它有三个参数(要发布的PipeAdvertisement 对象、广告的类型和广告的失效时间)。

createInputPipe() 方法

清单 18 所展示的 createInputPipe() 方法采用了一个 peer 名,并创建一个输入管道来监听收到的消息。


清单 18. createInputPipe() 方法
    private void createInputPipe (String peerName) {
        PipeAdvertisement pipeAd = createPipeAdvertisement (peerName);
        
        if (pipeAd!= null) {
            boolean published = publishPipeAdvertisement ( pipeAd );
            
            if (published) {
                PipeService pipeSvc = netPeerGroup.getPipeService ();
        
                try {
                    inputPipe = pipeSvc.createInputPipe ( pipeAd );
                    
                }//try
                catch (IOException io) {
                    io.printStackTrace ();
                }//catch
                
            }//if(published)
            
        }//if (pipeAd!= null)
    }//createInputPipe()

 



createInputPipe() 方法分三步创建一个输入管道:

  1. 它调用 createPipeAdvertisement() 方法,同时传递 peer 标识符。createPipeAdvertisement() 方法返回一个已经讨论过的PipeAdvertisement 对象。

  2. 然后它调用 publishPipeAdvertisement() 方法,同时传递 PipeAdvertisement 对象。publishPipeAdvertisement() 方法在 JXTA 网络上发布广告。

  3. 最后,它在发布的 PipeAdvertisement 对象上创建一个 InputPipe 对象。为此,只要调用PipeService 类的createInputPipe() 方法,就会返回一个 PipeService 对象。

createInputPipe() 设置新创建的 InputPipe 对象为一个类级的对象。在后面用这个 JXTAInputPipe 对象监听收到的 JXTA 消息。

searchAndCreateOutputPipe() 方法

已经介绍了如何创建一个输入 JXTA 管道。现在,看一看如何创建一个输出 JXTA 管道。

清单 19 中所见的 searchAndCreateOutputPipe() 方法用一个 peer 名作为参数,并为这个 peer 创建一个输出管道。


清单 19. searchAndCreateOutputPipe() 方法
    private void searchAndCreateOutputPipe(String peerName) {
        try {
            JXTASearch jxtaSearch = new JXTASearch (netPeerGroup);
            PipeAdvertisement pipeAdvert = jxtaSearch.getPipeAdvertisement (peerName);
            if (pipeAdvert != null) 
                outputPipe = jxtaSearch.getOutputPipe(pipeAdvert);
        }//try
        catch ( Exception ex ) {
            ex.printStackTrace ();
        }//catch
    }//searchAndCreateOutputPipe()

创建输出管道与创建输入管道的过程类似。创建输入管道时,首先创建一个 PipeAdvertisement

人气教程排行