6. 远程处理

6.1. CORBA在GDA中的应用

6.1.1. 介绍

本节介绍如何创建新对象并启用CORBA-也就是说,创建客户端与该对象远程交互所需的附加文件、接口和类。

6.1.2. 编写Java代码

6.1.2.1. 为对象创建Java接口

例如, gda.device.detector.Phantom.

6.1.2.2. 编写接口的实现

例如 gda.device.detector.phantom.PhantomV73.

请注意,如果接口是 some.package.Xxx ,实现应该在 some.package.xxx.SomeClass .

在幻影的情况下, PhantomV73 类适合设备/可扫描/检测器层次结构,但实现 IPhantomV73Controller 接口用于实际与硬件(或硬件的模拟)交互。

6.1.3. 创建特定于CORBA的文件

6.1.3.1. 创建与Java接口匹配的IDL

例如, phantom.idl .

以下是几点:

  • 请注意,虽然Java接口被称为 Phantom 中,CORBA接口被称为 CorbaPhantom .

  • Java和CORBA类型是不同的;例如,Java int 对应于一个CORBA long .

  • 输入参数必须以前缀 in ;例如:

  • .idl files are in the project's source tree under src/idl.

double getDemandVoltage(in long electrodeNumber) raises (device::corba::CorbaDeviceException);

6.1.3.2. 编译IDL以创建CORBA类

要构建CORBA,请执行以下操作 .jar 对于项目,首先生成工作区(IDE或命令行),然后执行以下任一操作

使用Eclipse IDE

  • 右击 releng.ant 在项目目录的根目录中

  • 选择 Run As ‣ Ant Build...

  • Targets 选项卡,选择 corba-make-jarRun

OR

使用命令行和 dawn.py (需要Ant版本1.8+)::

if [[ "$(uname -n)" == *diamond.ac.uk ]]; then
    module load ant
fi
ant -version

dawn.py --workspace=/path/to/workspace/ --include=name.of.project corba-make-jar

OR

使用命令行和本机 ant (需要Ant版本1.8+)::

if [[ "$(uname -n)" == *diamond.ac.uk ]]; then
    module load ant
fi
ant -version

cd /path/to/workspace_git/plugin_name/
ant -logger org.apache.tools.ant.NoBannerLogger -buildfile releng.ant corba-make-jar

CORBA编译执行以下步骤

  1. 删除 src/corba/javabin/corba

  2. 编译 src/idlsrc/corba/java

  3. 编译 src/corba/javabin/corba

  4. 罐子 bin/corbajars/${{corba.jar.name}}

结果是一个新的 .jar 已命名 /plugin_name/jars/gda-corba.jar (或类似情况)。您把这个托运一下 .jar 与Java接口、实现和IDL一起集成到版本控制中,但是 not 中的中间文件 src/corba/javabin/corba (这些应通过以下方式排除 .gitignore

新的 .jar 将包含对象的新类。对于Phantom,这些类包括:

  • CorbaPhantomOperations -包含幻影特定操作的接口(例如 setupForCollection

  • CorbaPhantom -表示CORBA版本的接口 Phantom ;扩展 CorbaPhantomOperations 外加其他一些CORBA接口

  • _CorbaPhantomStub -实施 CorbaPhantom 并发出CORBA远程请求

  • CorbaPhantomHelper -使用的各种实用程序方法 CorbaPhantom 物体

6.1.3.3. 编写CORBA实现/适配器类

这些类必须位于正确的包中,这样才能找到它们。

  • 设备的接口将位于 some.package.Xxx .

  • 这个 ImplFactory 需要命名实现类 some.package.xxx.corba.impl.XxxImpl .

  • 这个 AdapterFactory 要求将适配器类命名为 some.package.xxx.corba.impl.XxxAdapter .

6.1.3.3.1. 实现类

对于幻影来说,这是 PhantomImpl.

实现类必须扩展CORBA对象的POA类(对于Phantom,这称为 CorbaPhantomPOA

  • 该类需要两个字段:

    • 真实的物体-a Phantom 实例,在幻影的情况下。

    • A POA 字段。

  • 您需要一个带有两个参数的构造函数,该构造函数接受“real”对象和 POA . ImplFactory 将使用此构造函数。

  • 您实现的每个方法都应该委托给“真实”对象;任何异常都必须转换为CORBA特定的异常(例如 DeviceExceptionCorbaDeviceException )见 PhantomImpl 有关如何实现这些方法的示例,请参见。

6.1.3.3.2. 适配器类

对于幻影来说,这是 PhantomAdapter.

适配器类可以扩展其他适配器类,但始终需要实现您的Java接口(例如 Phantom

  • 该类需要三个字段:

    • CORBA对象(例如 CorbaPhantom “幻影”)。

    • A NetService .

    • 对象的名称。

  • 您需要一个带有3个参数的构造函数,它接受一个CORBA对象、该对象的名称和一个 NetService . AdapterFactory 将使用此构造函数。

  • 您实现的每个方法都应该委托给CORBA对象;任何CORBA异常都必须转换为相应的非CORBA异常(例如 CorbaDeviceExceptionDeviceException )见 PhantomAdapter 有关如何实现这些方法的示例,请参见。

6.1.4. 远程调用的工作方式

一旦完成了CORBA工作,就可以按如下方式使用该对象:

MyObject myObject = Finder.find("My_Object_Name");
myObject.myMethod("foobar");

处理这件事的方式如下:

  • myObject.myMethod("foobar") 调用适配器中的相应方法。

  • 适配器调用CORBA存根。

  • CORBA存根通过网络进行远程调用。

  • 在服务器端,CORBA调用实现类中对应的方法。

  • 实现类调用“真实”对象。

6.1.5. 正在验证CORBA JAR

CORBAJAR中编译的代码可能与用于生成代码的IDL文件不同步。CORBAJAR可以使用构建JAR的任何方法进行验证,但是要替换 corba-make-jar 具有 corba-validate-jar .

这将重新编译IDL文件以生成Java源代码,然后编译Java源代码以生成类文件。将新编译的类文件与CORBAJAR中的类文件进行比较。任何不匹配的情况都会报告。

6.1.6. 查看名称服务器中绑定的对象

启动GDA对象服务器后,可以使用JacORB的NameManager查看名称服务器中绑定的对象。

NameManager可以使用 gda 使用以下命令的启动器:

gda namemanager

这将启动NameManager:

_images/namemanager.png

默认情况下 gda Launcher将尝试自动确定JacORB配置目录的位置,该目录包含 etc/jacorb.properties 指定名称服务器位置的文件。(此目录通常为 $GDA_CONFIG/properties 。)如果由于任何原因无法自动确定目录,或者如果您要使用特定的JacORB配置文件,则可以手动指定JacORB目录:

gda namemanager --jacorb=/path/to/jacorb/dir

由于错误,随JacORB提供的标准NameManager不会显示所有绑定对象。这个 org.jacorb.naming.namemanager 插件包含针对此错误的修补程序。如果您的GDA安装中存在此插件,它将自动包含在类路径中。必须编译插件才能使修补程序生效。

6.2. CORBA的替代方案

6.2.1. 使用RMI

6.2.1.1. 使用标准RMI导出器/代理

对于新写入的对象,可以使用RMI使这些对象在网络上可用。

Spring的GDA封装 RmiServiceExporteruk.ac.gda.remoting.server.GdaRmiServiceExporter 应在服务器端使用,以使对象远程可用。必须告知要导出的对象、用于导出对象的名称以及 服务接口 -定义客户端应该可用的方法的接口(不要尝试使用纯Spring RmiServiceExporter 否则将出现类加载错误)。例如:

<!-- the object that is to be made remotely available -->
<bean id="prosilica_server" class="...">
    ...
</bean>

<!-- See below for a more concise way of doing this -->
<bean class="uk.ac.gda.remoting.server.GdaRmiServiceExporter">
    <property name="serviceName" value="gda/ProsilicaServer" />
    <property name="service" ref="prosilica_server" />
    <property name="serviceInterface" value="gda.images.camera.prosilica.server.ProsilicaImageServer" />
</bean>

可以使用以下XML快捷方式代替上面显示的完整bean定义,前提是 service 参数是对现有Spring bean的引用:

<gda:rmi-export
  service="prosilica_server"
  serviceName="gda/ProsilicaServer"
  serviceInterface="gda.images.camera.prosilica.server.ProsilicaImageServer"
  events="false" />

在客户端,Spring的 RmiProxyFactoryBean 可用于在服务器上生成对象的代理。它将创建一个实现服务接口的代理对象;每个方法都调用远程对象。例如:

<!-- See below for a more concise way of doing this -->
<bean id="prosilica_server" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
    <property name="serviceUrl" value="rmi://otherserver/gda/ProsilicaServer" />
    <property name="serviceInterface" value="gda.images.camera.prosilica.server.ProsilicaImageServer" />
    <property name="refreshStubOnConnectFailure" value="true" />
</bean>

这个 refreshStubOnConnectFailure 属性会导致客户端重新连接到服务器,例如,如果服务器重新启动。这允许服务器热重新启动,而无需重新启动客户端。

可以使用以下XML快捷方式代替上面所示的完整Bean定义:

<gda:rmi-import
  id="prosilica_server"
  serviceUrl="rmi://otherserver/gda/ProsilicaServer"
  serviceInterface="gda.images.camera.prosilica.server.ProsilicaImageServer"
  events="false" />

(此XML快捷方式自动设置 refreshStubOnConnectFailuretrue

注意使用 RmiProxyFactoryBean 意思是说 每一个 调用服务接口中的方法将导致远程方法调用。例如,这不适合于实现 IObservable 为活动做准备。有关此问题的解决方案,请参阅下一节。

当前存在许多问题,阻碍将此机制替代CORBA用于对象(如可扫描对象):

  • CORBA IDL文件定义的“远程接口”以及适配器和实现类通常与“真实”对象实现的方法不同。使用导出的对象 RmiServiceExporter ,以及由自动生成的代理 RmiProxyFactoryBean ,不会解释这些差异。

  • CORBA适配器和实现类通常包括“真实”对象中不存在的附加逻辑。它们有时还执行类型转换。同样,使用标准RMI导出器/代理bean不会考虑这些差异。

  • CORBA适配器和实现类通常执行“真实”异常类型之间的转换(例如 DeviceException )和CORBA特定的异常类型(如 CorbaDeviceException )。这意味着客户端需要准备好处理的异常通常相当有限。使用自动生成的RMI代理意味着可能需要修改客户端以处理其他异常类型。

但是,如果您正在开发新对象并希望远程调用方法,使用此机制可能就足够了。

6.2.1.2. 使用 GdaRmiServiceExporterGdaRmiProxyFactoryBean

春天的 RmiServiceExporterRmiProxyFactoryBean 类在服务器端对象实现 IObservable ,原因有二:

  • 虽然将使用RMI使对象可用,以便客户端可以调用其上的方法,但该对象生成的任何事件都不会传播到客户端。

  • 在客户端上,将为以下对象执行远程方法调用 每一个 对象的服务接口中的方法-包括 IObservable 方法,这将不起作用。

此外,从GDA 9.0开始, RmiServiceExporter 将无法成功加载OSGi下的服务器bean类,因此根本不适合在服务器中使用。因此,从GDA 9.0开始, <gda:rmi-export /> XML标记独占使用 GdaRmiServiceExporter .

若要导出生成事件的对象,请使用 GdaRmiServiceExporterGdaRmiProxyFactoryBean 类应该用来代替 RmiServiceExporterRmiProxyFactoryBean 分别。

GdaRmiServiceExporter 使用RMI导出对象的方式与 RmiServiceExporter ,只是对象生成的任何事件都将通过CORBA传播到客户端。

GdaRmiProxyFactoryBean 创建一个代理,该代理处理 IObservable 方法在本地调用。代理自动连接到CORBA事件分派系统,并注册以接收与服务器端对象相关的事件。它还维护一个客户端观察者列表。当客户端代理接收到事件时,它们将被分派给观察者。

如果您使用 <gda:rmi-export /><gda:rmi-import /> 上面描述的XML快捷方式(而不是包括完整的bean定义),然后删除 events="false" 属性(默认为 true )。因此,服务器端XML将为:

<gda:rmi-export
  service="prosilica_server"
  serviceName="gda/ProsilicaServer"
  serviceInterface="gda.images.camera.prosilica.server.ProsilicaImageServer" />

客户端XML将是:

<gda:rmi-import
  id="prosilica_server"
  serviceUrl="rmi://otherserver/gda/ProsilicaServer"
  serviceInterface="gda.images.camera.prosilica.server.ProsilicaImageServer" />

必须注意确保服务器端和客户端对象的名称匹配,否则将不会收到事件。在服务器上, GdaRmiServiceExporter 将安排使用基础对象的名称发送事件。因此,对于这个:

<bean id="A" class="...">
    ...
</bean>

<bean class="uk.ac.gda.remoting.server.GdaRmiServiceExporter">
    <property name="serviceName" value="..." />
    <property name="service" ref="A" />
    <property name="serviceInterface" value="..." />
</bean>

事件将与对象名称一起发送 A 。在客户端上, GdaRmiProxyFactoryBean 还必须调用 A ,否则它将不会从名为 A .

<bean id="A" class="uk.ac.gda.remoting.client.GdaRmiProxyFactoryBean">
    ...
</bean>