6. 远程处理¶
6.1. CORBA在GDA中的应用¶
6.1.1. 介绍¶
本节介绍如何创建新对象并启用CORBA-也就是说,创建客户端与该对象远程交互所需的附加文件、接口和类。
6.1.2. 编写Java代码¶
6.1.2.1. 为对象创建Java接口¶
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
对应于一个CORBAlong
.输入参数必须以前缀
in
;例如:
.idl
files are in the project's source tree undersrc/idl.
double getDemandVoltage(in long electrodeNumber) raises (device::corba::CorbaDeviceException);
6.1.3.2. 编译IDL以创建CORBA类¶
要构建CORBA,请执行以下操作 .jar
对于项目,首先生成工作区(IDE或命令行),然后执行以下任一操作
使用Eclipse IDE
右击
releng.ant
在项目目录的根目录中选择
上
选项卡,选择corba-make-jar
和
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编译执行以下步骤
删除
src/corba/java
,bin/corba
编译
src/idl
到src/corba/java
编译
src/corba/java
到bin/corba
罐子
bin/corba
到jars/${{corba.jar.name}}
结果是一个新的 .jar
已命名 /plugin_name/jars/gda-corba.jar
(或类似情况)。您把这个托运一下 .jar
与Java接口、实现和IDL一起集成到版本控制中,但是 not 中的中间文件 src/corba/java
和 bin/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特定的异常(例如
DeviceException
到CorbaDeviceException
)见PhantomImpl
有关如何实现这些方法的示例,请参见。
6.1.3.3.2. 适配器类¶
对于幻影来说,这是 PhantomAdapter.
适配器类可以扩展其他适配器类,但始终需要实现您的Java接口(例如 Phantom
)
该类需要三个字段:
CORBA对象(例如
CorbaPhantom
“幻影”)。A
NetService
.对象的名称。
您需要一个带有3个参数的构造函数,它接受一个CORBA对象、该对象的名称和一个
NetService
.AdapterFactory
将使用此构造函数。您实现的每个方法都应该委托给CORBA对象;任何CORBA异常都必须转换为相应的非CORBA异常(例如
CorbaDeviceException
到DeviceException
)见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:
默认情况下 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封装 RmiServiceExporter , uk.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快捷方式自动设置 refreshStubOnConnectFailure
到 true
)
注意使用 RmiProxyFactoryBean
意思是说 每一个 调用服务接口中的方法将导致远程方法调用。例如,这不适合于实现 IObservable
为活动做准备。有关此问题的解决方案,请参阅下一节。
当前存在许多问题,阻碍将此机制替代CORBA用于对象(如可扫描对象):
CORBA IDL文件定义的“远程接口”以及适配器和实现类通常与“真实”对象实现的方法不同。使用导出的对象
RmiServiceExporter
,以及由自动生成的代理RmiProxyFactoryBean
,不会解释这些差异。CORBA适配器和实现类通常包括“真实”对象中不存在的附加逻辑。它们有时还执行类型转换。同样,使用标准RMI导出器/代理bean不会考虑这些差异。
CORBA适配器和实现类通常执行“真实”异常类型之间的转换(例如
DeviceException
)和CORBA特定的异常类型(如CorbaDeviceException
)。这意味着客户端需要准备好处理的异常通常相当有限。使用自动生成的RMI代理意味着可能需要修改客户端以处理其他异常类型。
但是,如果您正在开发新对象并希望远程调用方法,使用此机制可能就足够了。
6.2.1.2. 使用 GdaRmiServiceExporter
和 GdaRmiProxyFactoryBean
¶
春天的 RmiServiceExporter
和 RmiProxyFactoryBean
类在服务器端对象实现 IObservable
,原因有二:
虽然将使用RMI使对象可用,以便客户端可以调用其上的方法,但该对象生成的任何事件都不会传播到客户端。
在客户端上,将为以下对象执行远程方法调用 每一个 对象的服务接口中的方法-包括
IObservable
方法,这将不起作用。
此外,从GDA 9.0开始, RmiServiceExporter
将无法成功加载OSGi下的服务器bean类,因此根本不适合在服务器中使用。因此,从GDA 9.0开始, <gda:rmi-export />
XML标记独占使用 GdaRmiServiceExporter
.
若要导出生成事件的对象,请使用 GdaRmiServiceExporter
和 GdaRmiProxyFactoryBean
类应该用来代替 RmiServiceExporter
和 RmiProxyFactoryBean
分别。
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>