SLEE
SLEE 是一个容器标准,目标是提供移动技术与企业技术之间的整合。该标准类似于 EJB,不过是用于各种面向事件的应用程序,例如 IP 电话服务、分布式交互模拟/监控/控制等。Mobicents 是第一个而且是目前唯一开源并被 JAIN SLEE 1.0 认定的产品,它从交换协议构造上基于 JAIN-SIP。
前言
Mobicents 是一个专业开源的 VoIP 中间件平台。Mobicents 是第一个而且是目前唯一开源并被 JAIN SLEE 1.0 认定的产品,它从交换协议构造上基于 JAIN-SIP。
JAIN SLEE 是一个以事件为驱动的中间件,采用了各个服务单元(Sbb)消息机制,减少了在事物处理上的等待延迟,其工作方式是从外部协议资源扫描事件状态,然后将这些事件递交到各个处理单元去,可以以它为核心设计成网关和网守,软交换上层的应用服务器,媒体服务器等多种设备,同时适配多种交换协议。
以下分成 10 个部分来对 Mbicents-SLEE 进行详细介绍:
1. 资源适配器(Resource Adaptor)体系;
2. JAIN-SIP;
3. 事件和事件类型(Events),事件引导(Routing);
4. 行为实体(Activity)和行为实体上下文(Activity Context);
5. SBB部件和SBB实体;
6. 数据供应(Provisioned Data);
7. SLEE的一些工具(facility);
8. 服务和部署(Service and Deployment);
9. 应用范例(SIP代理服务器,信令网关);
10. 使用elipslee来进行SBB的开发;
下面展示了Mobicents Docs中的一个体系分布图:
图1 . SLEE的体系分布图
1> SBB 服务管理单元(Sbb Service Management):这个部分面向上层的应用,也就是 Service Block Building 的构造和部署的主要部分,其中包含了对象持久性(Persistence),类似 EJB 的CMP 一样,对数据对象的持久性(包括生存期和数据库连接等等)由 SLEE 容器自动完成的,SBB 分成 Sbb 实体和 Sbb 对象两个组成。对整个 Sbb 的服务管理单元来说,包含有 Sbb 工厂,持久性管理,Sbb 对象池管理,服务部署者,这些对于实际的使用者而言是不可见的。用户的应用部署文件是 sbb.jar,而用户服务描述是 service.xml。
2> Sbb 运行环境(Sbb Runtime):Sbb 运行环境就是Sbb的执行体,核心是事件导向单元--Event Router,(获得事件并且分配导入到指定的 Sbb 中去),SLEE 端点管理(连接资源适配器产生事件送达端点),通过上下文(Context)方式来实现各个实体之间的联系(参看图7),和 Sbb服务管理单元之间的接口是 ActivityContext(以下为活跃实体或者行为实体上下文),用于表示独立的事件接口;和资源适配器之间的接口是 Activity,也就是行为实体,具体事件的封装,例如 SIP 的注册事件(SIP Register),这个事件会引发 Sbb 的相关注册服务(例如 RegisteraSbb);另一个接口是 SLEE Activity,这个是 SLEE 内部的行为实体,例如一些内部的工具产生的行为实体,例如定时器事件(Timer Event)和用于调试的 Trace 事件。
3> SLEE 的一些工具:和 J2EE 中的工具一样,提供了一些工具来使用,工具在 SLEE 中的定义是一些标准的功能组件,他们的提供了一些预定义的接口为应用提供服务,其中包括了Adress,Profile,Alarm,Naming。
4> 资源适配器(RA),用于具体的协议在SLEE上的封装,例如(SCAP,SS7,SIP,MGCP,H.323)当然也可以是自己的私有协议,封装的方式可以参看Mobicents官方网站的一些介绍,协议和SLEE Run-time的接口就是具体的行为实体,也就是事件的封装, 用户定义的资源适配器部署文件是RA.jar在本文中SIP资源适配器的部署文件是sip-local-ra.jar和sip-type-ra.jar。
一. 资源适配器(Resource Adaptor)体系
1.Mobicents资源适配器的概述以及定义和应用模式:
备注:Mobicents 中的资源适配器提供了非常方便的接口以便实现各种协议资源在 SLEE 上的组装,目前在 1.0a 和 1.0b中都只完成了 SIP 协议和 TCAP 协议,事实上后者的意义在于将 SIP 模拟作为 NSP 层而采用的应用层协议。
资源适配器主要是指的网络设备和协议栈本身,和状态机的有连接协议,例如我们在下面将详细介绍的JAIN-SIP 以及其他支持JAVA语言的呼叫代理协议, JCC API,Parlay/OSA 等。
资源适配器分成三个范畴,
a. 资源适配器类型(Resource adaptor type);
b. 资源适配器对象;
c. 资源适配器实体(entity);
2. 协议栈的资源在 SLEE 上的绑定:
在 Mobicents1.0a 上绑定了两个资源实体就是 JAIN-SIP 的资源实体(SIpRA),和 JAIN SLEE TCK,下面我们根据它的第三方范例 (Third-Party) 注册机(Registar)来简单介绍它的资源绑定模式:
首先是协议栈描述,就是 SIP 在 Sbb 服务上的描述,在 sbb-jar.xml 文件中会体现,这个文件是 SBB 的部署文件,可以理解为 J2EE 中的 ejb-jar.xml 文件。
如Registrar中的:
<resource-adaptor-type-binding>
<resource-adaptor-type-ref>
<resource-adaptor-type-name>jain-sip</resource-adaptor-type-name>
//JAIN SIP的资源适配器类型
<resource-adaptor-type-vendor>javax.sip</resource-adaptor-type-vendor>
<resource-adaptor-type-version>1.1</resource-adaptor-type-version>
</resource-adaptor-type-ref>
在JNDI中的实体上下文工厂的绑定,SBB的开发者通过lookup来调用
<activity-context-interface-factory-name>
slee/resources/jainsip/1.1/acifactory
</activity-context-interface-factory-name>
定义了SBB所依靠的一个定义资源适配器对象名称在JNDI上绑定的类型
<resource-adaptor-entity-binding>
<resource-adaptor-object-name>
slee/resources/jainsip/1.1/provider
</resource-adaptor-object-name>
定义了resource-adaptor-object-name中表示的资源实体对象SIPRA
<resource-adaptor-entity-link>
SIPRA
</resource-adaptor-entity-link>
</resource-adaptor-entity-binding>
</resource-adaptor-type-binding>
其中:
资源适配器对象名称就是<resource-adaptor-object-name>slee/resources/jainsip/1.1/provider</resource-adaptor-object-name>
资源适配器实体的名称<resource-adaptor-entity-link>SIPRA </resource-adaptor-entity-link>,实体本身使用专门的工具DeploySipRA.sh将会对SIPRA进行创建和部署,脚本中如下描述:
call %JBOSS_HOME%inSleeCommandInterface.bat -createRaEntity "ResourceAdaptorID[jainsip#NIST#1.1]" SIPRA
类型名称是:<resource-adaptor-type-name>jain-sip</resource-adaptor-type-name>
3.协议栈工厂的调用 :
在Sbb中调用lookup() 方法时都会执行一次新查找获得资源实体工厂,这里会返回工厂实例SipFactoryProvider,<resource-adaptor-type-name>包含了SBB需要对资源实体访问时候需要访问的资源实体工厂名称,例如:
fp = (SipFactoryProvider) sbbEnv.lookup("slee/resources/jainsip/1.1/provider");
SIP协议工厂,用于生产SIP的消息体(Message),SIP的头(Header),SIP的JAIN-SIP的ServerTransaction/ClientTransaction(交易的状态机,和每次会话--Cseq对应),SIP消息发送发送者和侦听事件的侦听点。
4.行为实体上下文工厂的调用:
<activity-context-interface-factory-name>,这里是行为实体上下文工厂接口在JNDI上的绑定,目的是为了Sbb产生行为实体接口的,SBB通过lookup()方式访问行为实体上下文接口的环境入口,而行为实体上下文接口的工厂的接口(比较拗口,可以理解为一个对象工厂的接口就好了)是在<activity-context-interface-factory-interface-name>中体现出来,在对SBB的服务进行初始化时候,工厂和JNDI上绑定的名字相对应(sbb-jar.xml中),如resource-adaptor-type-jar.xml中:
<activity-context-interface-factory-interface-name>
org.mobicents.slee.resource.sip.SipActivityContextInterfaceFactory
</activity-context-interface-factory-interface-name>
查找一个行为实体上下文接口工厂:
acif = (SipActivityContextInterfaceFactory)
myEnv.lookup("slee/resources/jainsip/1.1/acifactory")
SBB需要对资源实体活动体上下文接口工厂进行访问获得行为实体的接口,例如发送一个SIP的消息的有状态消息(INVITE消息)那么sbb就需要获得一个行为实体上下文接口,与创建的Sbb和这个行为实体接口绑定,维持这个有状态会话。
关于行为实体的概念,是SLEE中的重要的概念,对于它的理解可以看作对应于一个SIP的会话状态机,一般用SIP消息中的Branch ID作为一个"种子"来生成一个行为实体的状态机,根据这个会话将不同的事件导向到对应的SBB中,在后续将详细介绍,的上下文(ActivityContextInterface)是可以在Sbb间共享的,通过共享可以获得消息的参数,如via,to,from,contact,CallID,Cseq;在SLEE中规定定义一些Set/Get方法来对这些参数进行访问,参看注册机中的RegistrarActivityContextInterface中的Set/Get接口方法。
二.JAIN-SIP的简单介绍
1.什么是JAIN-SIP:
综合网络的Java 应用编程接口(JAIN,Java API for Integrated Networks)是一组基于Java技术的API,这些接口将业务可移植性、网络聚合以及安全的网络接入引进电话网和数据网络,使得在Java平台上快速开发下一代电信产品和服务成为可能,在Mobicents中以Jain-sip作为Resource Adaptor。
在JAIN SIP API体系结构中,为所有SIP消息的报头定义了类,并借助提供者/监听者接口,将用于处理报文的JavaBeans体系结构的接口定义为事件。
Jain-SIP是一个标准的JAVA的SIP协议栈的interface,它分成以下几个部分:
a. 标准的协议栈的接口;
b. 标准的SIP消息接口;
c. 标准的SIP消息状态机和事件的触发。
2. 从JAIN-SIP到SLEE中的事件传导机制:
以下仍然以Registar为例子简要介绍SLEE中对SIP消息(或者是事件)的管理以及处理句柄的调用。见图2,分成事件和协议两个部分:
事件部分:也就是RFC3261中的事件信息, REGISTER,INVITE等,EventRouter将事件导向到对应的的SBB中的事件处理回调中去(onRegistar),processinitialevents被EventRouter调用,目的是找到处理对应事件的SBB,RegisterSbb是处理对象的SBB。
协议部分:SIP Stack也就是协议栈的部分, SipResourceAdaptor是Resource Adaptor的SIP实例化,Event表示了当前的SIP会话的行为实体,从这里可以获得Jain-SIP的服务端会话交易用于处理侦听消息的处理句柄(在下面会有介绍),Event Type是JAIN-SIP消息类型,EventObject是传递到SBB实体时候对JAIN-SIP消息进行的封装。
图2.一个注册机的事件交易过程
3.JAIN-SIP中的重要类介绍:
1> Siplistener
概述:
这个类代表SIP协议的负责侦听的应用程序端,这个接口定义了一个侦听端,接收处理从SipProvider提交的SIP事件消息,是一个抽象的侦听线程。
结构:
每个SIPStack或每个IP地址对应一个的Siplistener,而Siplistener与SipProvider的关系是一对多的关系。
2> SipFactory
SIPFactory是一个单类,用单一方式得到这个SipStack应用的执行权。SIPFactory单一的实例能用得到实例的方法(或者Singleton)而被得到。通过在SIPFactory里调用合适构造方法,一个代表被叫端的对象能从SIPFactory得到。创造一个被叫的SipStack,这样应用将可以调用SipStack中的方法。
3> ServerTransaction
概述:
ServerTransaction可以通常被理解为一个会话状态机,SipProvider用来处理接收SIP事件和消息序列,将接收到消息发送到SipListener(侦听方法)的processRequest或者processResponse回调,通过用户代理服务器发送回应信息给应用程序。这个类让应用程序能发送一个回应对应SipListener收到的请求。
方法:
ServerTransaction中的方法:
sendResponse
应用希望发送一个响应时候,它创立一个来自MessageFactory创造的回应,然后回应传送到ServerTransaction的sendResponse方法。
4> ClientTransaction
概述:
ClientTransaction应用可以理解为发送INVITE信息到UAS。ClientTransaction也用从SipListener接收到的回应进行匹配。和ServiceTransaction相对应。
方法:
ClientTransaction的方法:
Request createAck()
通过ClientTransaction创造一个关于当前的请求的正确应答。
void sendRequest()用于发送创建的请求消息。
这个类的调用意味着上层的应用是UAC。
图3. JAIN SIP 结构和状态机体系
图4 JAIN-SIP,Resource Adaptor以及SbbEntity之间调用关系
回页首
三. 事件和事件类型(Events),事件引导(Routing):
1. 事件和事件类型:
在SLEE中和资源适配器中,事件代表外部到达的消息,向外部发送的消息,某些状态的迁移(例如定时器的时间到),其他的SBB使用Activity Context共享状态。这就减少了SBB实体间直接对事件的调用,其他的SBB实体可以对事件达到同时处理,这个是和EJB最大的不同的地方。
2. 事件源:
a 外部的协议栈资源(SS7 stacks, SIP stacks,记费系统 ...,Profile触发)
b SLEE的服务容器内的部件(定时器、服务触发、数据源触发)
c 自定义的触发源(自定义事件)
3.SLEE中的事件描述:
在有多个资源的时候,这些资源所触发的事件都是放在event-jar.xml和slee-event-jar.xml两个文件中的, 而SBB所感兴趣的事件,则放在sbb-jar.xml中定义了,以及还包括了一些Sbb需要知道的消息细节,例如消息方向,触发顺序和初始化参数:
<event event-direction="Receive" initial-event="True">
<event-name>RegisterEvent</event-name>
<event-type-ref>
<event-type-name>javax.sip.message.Request.REGISTER</event-type-name>
<event-type-vendor>javax.sip</event-type-vendor>
<event-type-version>1.1</event-type-version>
</event-type-ref>
<initial-event-select variable="ActivityContext" />
</event>
initial-event表示初始事件,也就是事件会触发实例化一个根Sbb,Root Sbb,就是说这个消息可能是一个消息状态机中的第一个消息。Root Sbb定义在后续章节会更加详细的看到。
event-direction表示事件是发出(Fire)还是接收(Receive)来触发SBB.
event-type-ref这里表示的相关事件的类型单元,其中event-type-name为事件的名字。
4.SLEE中的事件处理过程:
在SBB中的事件处理句柄是在事件用"On"+ event-name表示,例如在注册机例子中的RegiterEvent的处理句柄为OnRegisterEvent。
带入参数:
"on"+<event name>(<event class> event,<SBB Activity Context Interface interface> activity)
行为实体<SBB Activity Context Interface interface> activity和<event class> event:
SBB对象作为一个事件消费者通过行为实体了解事件,activity是带入处理句柄的一个参数,是代表行为实体的接口activity interface,关于行为实体的一些来历在前面做了介绍。从行为实体接口可以获得从对端发送的消息;从一个Mobicents中的Activity可以获得一个行为实体的调用--ServerTransaction,
另外一个带入的参数是这个SBB实体感兴趣的消息,这个消息是被SLEE自动导入到SBB中去的,这个消息事件是JAIN-SIP中javax.sip.RequestEvent包的消息类,从这个类中可以得到SIP消息的一些具体信息。
图5 Sbb和活跃实体以及上下文之间的关系图
回页首
四.行为实体(Activity)和行为实体上下文(Activity Context)
1.行为(Activity):
在SLEE中对行为如下定义:一个相关事件流的抽象,也是一个根据状态变化而发出事件的有限状态机
2. 实体上下文(Activity Context)
SLEE中定义一个ActivityContext是资源实体在SLEE中的表现,SBB实体从ActivityContext中获得事件,可以把它理解为一个事件总线,SBB要获得事件需要绑定到事件总线上去,SBB实体根据它绑定的不同的实体上下文获得事件触发相应的动作,并且做出相应的响应动作;而实体上下文接口(ActivityContextInterface)就是也就是SBB对实体访问的接口,在使用过程当中Sbb一般会根据定义自己的状态在ActivityContextInterface上增加一个封装。
3.范例:
下面我们看以下SIP Registrar中实体和实体上下文的例子:
1> 对于一个JAIN-SIP的协议栈实体来说,实体对象是ServerTransaction和ClientTransaction(参看图5),这两个交易是针对特定呼叫的,每个呼叫指定一个特定对象。
一个新的SIP呼叫--Register消息进入SLEE,这个呼叫被某个Sbb所感兴趣,
2> SIP呼叫实体传递一个SIP的消息(REGISTER)通过ActivityContext传递到SBB实体中去,由onRegisterEvent(RequestEvent event, ActivityContextInterface ac)来进行处理,
3> 从ActivityContextInterface获得实体ServerTransaction:
ServerTransaction serverTransactionId = (ServerTransaction)ac.getActivity();
4> 从SBB中根据这个状态机创建一个新的SIP呼叫,并且发送这个请求:
this.clientTransaction = sipProvider. getNewClientTransaction(request);
clientTransaction.sendRequest();
5> 为了接收这个呼叫中一些消息的内容,需要绑定当前的SBB实体到ActivityContext接口上,为了将当前SBB行为实体上下文和SIP的行为实体上下文接口结合起来,SLEE规范中提供了一个asSbbActivityContextInterface方法将SIP(或者其他)的行为实体的接口作为参数带入到SBB的行为实体上下文中,才可以作为提供了一个SBB可以访问的共享行为实体上下文:
ActivityContextInterface aci=SipActivityContextInterfaceFactory.getActivityContextInterface()
RegistrarActivityContextInterface regACI = asSbbActivityContextInterface(aci)
//以下是对SIP消息头的设定,将该特定的SIP消息和ActivityContextInterface绑定起来,
便于Sbb实体进//行跟踪,例如针对该消息的超时事件,这个在注册机例子中有详细的使用过程,
regACI.setSipAddress(sipAddress);
regACI.setSipContactAddress(sipContactAddress);
// callId and cseq used to identify a particular registration
regACI.setCallId(callId);
regACI.setCSeq(cseq);
regACI.attach(getSbbLocalObject());
五.SBB部件和SBB实体
1. SBB实体定义:
1> SBB定义为服务组件,封装了服务逻辑和相关的触发状态。
2> SBB组件需要包含:
a> Sbb组件产生或者是接收的事件。
b> 单个实例化的状态:每个实例化域的状态都被收录到SLEE集中管理的CMP容器
c> 事件处理句柄: Sbb组件中包含的,每个它所接收到的事件类型的一个事件处理方法。
d> 本地接口:Sbb中定义一个Sbb本地接口,它定义有可能同步被调用的Sbb组件(是在同一个SBB Tree上生成的Sbb才可调用的接口,下面也将详细介绍)的操作。
e> 子关系成分:SBB成分可能包括零个或者多个子SBB组件
f> 可共享性:SBB组件通过Activity Context的方式与其他SBB组件共享状态,SBB组件定义。
g> 一个JAVA接口(Activity Context Interface),这个接口定义为了得到和设置这些特性所进行的安全访问操作。
例子:
在Mobicents中有一个简单的SIP代理网关范例JainSipProxySbb可以参看,和前面所说的RegistrarSbb做为它的子Sbb,可以通过他们来了解整个Sbb构造。
2. 根SBB和子SBB
a.SBB 实体(Sbb Entity)
SBB实体是SBB组成的一个实例。SBB实体是一个代表着实例持久性状态的实体。
b.SBB子实体和根SBB实体的定义
在运行时间内,SBB实体可能创造多个SBB子实体。由于SBB实体可能被创造,所以它是一个单一的父目录。以图6为例SBB实体子目录是一个被管理的非循环的树形表。
图6详细说明了父和子Sbb之间的关系,它也显示了SLEE是所有根SBB实体合理的父亲。
在SLEE中定义,在父的SBB访问子SBB采用"获得子SBB关系对象"的方法,父的SBB实体使用getSBBLocalObject从SbbContext对象中,获得一个SBB的本地对象,父SBB实体也将这个本地的SBB对象传递到的子SBB中去,例如某些情况下,在子SBB的本地接口中定义一个方法给父SBB用来传递消息到子SBB中去。
下面我们用一个简单的子SBB的例子来说明这个问题,请参看ProxyBaseProxySbb.java 在这里父 SBB 是 BaseProxySbb 中处理各种 SIP 呼叫的消息,其中对 Register 事件的消息是由子 SBB Registera 来进行处理这个 SIP 消息的,换句话来说,有一个子的 SBB 应用嵌入在父 SBB BaseProxySbb 处理中。
1> 父 Sbb 获得子 Sbb 的关系:
public void onRegisterEvent(RequestEvent event, ActivityContextInterface ac) {
getDefaultSbbUsageParameterSet().incrementNumberOfRegister(1);
trace(Level.INFO, "Received REGISTER request, class="+event.getClass());
try {
// is local domain?
//在父SBB中了使用了一个方法来创建子的SBB关系,然后并且通过这个子关系创建子SBB的本地对象,这个
//本地对象提供了子SBB可以调用的方法,而且这个子SBB在sbb-jar.xml
中需要对这个获得子SBB的方法进行
//定义
ChildRelation relation = getRegistrarSbbChild();
… …
2> 子Sbb在sbb-jar.xml中的定义
子SBB在sbb-jar.xml中需要对这个获得子SBB的方法进行定义:
<sbb-classes>
<sbb-abstract-class>
<sbb-abstract-class-name>
com.opencloud.slee.services.sip.proxy.JainSipProxySbb
</sbb-abstract-class-name>
父SBB使用这个方法来创建子SBB实体,
<get-child-relation-method>
定义了子SBB,这里表示最后调用的子SBB为RegisteraSbb
<sbb-alias-ref>RegistrarSbb</sbb-alias-ref>
定义了获得子关系的方法名字,调用这个方法可以得到SBB子关系,通过调用子关系可以创建这个子SBB的本地对象
<get-child-relation-method-name>getRegistrarSbbChild</get-child-relation-method-name>
<default-priority>0</default-priority>
</get-child-relation-method>
…
3> 创建一个子 Sbb 的调用过程:
SBB 的开发者的 SBB 调用方法一般如下,例如对当前 Reigster 事件的 ChildRelation 创建(creat)方法的调用,得到一个新的 SBB 实体,在调用过程中 Registera 的 sbbCreate 将被调用,这里和一个常见的 SBB 实体创建过程基本相同,返回的是一个以 SbbLocalObject 接口的 SBB 实体:
Public class RegistrarChildRelation implements ChildRelation {
Public sbblocalobject creat() … {
SbblocalObject local=RegisteraSbbLocalObject();
Return local ;}
}
4> 一个应用中的创建并且使用子 SBB 的方法:
调用子关系的创建方法,得到子 SBB 的实体
… …
SbbLocalObject child = relation.create();
//将当前的子SBB绑定到当前的行为实体上下文上去;
// attach child to this activity
ac.attach(child);
//把父实体从当前的上下文上分离出去;
// detach myself
ac.detach(getSbbLocalObject());
// 这样后续的消息处理都被重定向到RegisterSbb上去了。
…. …
} catch (Exception e) {
trace(Level.WARNING, "Exception during onRegisterEvent", e);
}
}
从上述例子也可以看到事件优先权的问题,在事件传送过程中,父SBB实体总是在它的子SBB实体前接收同样的事件。SBB实体的事件传递优先权决定了同级别SBB实体接收事件的顺序,另外在SLEE的各个实体关系中,SBB实体的事件传送优先权可能被改变。
3. 事件传送优先权
对于实体序列来说。父SBB实体总是在它的子SBB实体前接收同样的事件。对于优先级的定义大家可以参看规范中的8.5.7部分,要改变优先级的时候会在SbbLocalObject接口的setSbbPriority方法。
4. SBB 实体之间的并行删除:
SBB实体可以删除,通过在SbbLocalObject 接口或ChildRelationObject接口调用删除的方法。当执行删除时候,SLEE将会删除SBB的实体和所有SBB父实体后代。
5. SBB 对象
SBB对象是SBB所扩展的抽象类SLEE之一个实例,和SBB实体所不同的是SBB的实体是逻辑的实体,而SBB对象则是一个JAVA的对象,可以被实际调用的。
6. SBB 本地接口和 SBB 本地对象
每个SBB有一个SBB本地接口。SBB的SBB本地接口是一个指定的SBB本地界面,它是由SBB开发者根据SbbLocalObject接口来进行扩展的。
SBB对象通过SbbLocalObject同步地调用目标SBB实体的SBB对象,因此可以理解SbbLocalObject就是一个Sbb对象的实例,这样使用者可以通过GetSbbLocalObect方法获得相关的目标对象,并且得到它的调用方法。本质上,SBB本地对象是一个应用SBB本地对象接口和代表目标SBB实体的执行对象。当SBB对象在SbbLocalObject上调用SBB开发者定义的方法时,在Sbb抽象类中的相关方法将被Sbb对象进行调用。另外,Sbb本地对象可以在远程过程调用的时候作为一个客户存根对象。本质上来说Sbb的本地接口从提供了对SbbLocalObject的调用能力,通过SbbLocalObject访问到相对应的SBB实体,这个很有点和EJB的本地接口类似。
7.SBB 的生命对象周期
当 SLEE 采用 newIstance 创建一个 SBB,这个时候 sbb 对象生命周期就开始了,这个时候缓冲Sbb对象,调用setsbbContext将sbbContext(也就是Sbb的上下文)对象传入Sbb对象中,此时SIP消息工厂对象或者是地址工厂,以及SIP栈的提供者(SipProvidor)这些的对象的创建都在setSbbContext回调中进行。
当完成了实例化的过程以后,sbb对象将进入对象池,每个Sbb组件都有自己的对象池,例如RegistraSbb对象会进入自己的对象池中,但这个时候Sbb对象还不和任何Sbb实体相关联。
当一个Sbb接收到一个事件并且开始进程处理过程,而某个业务逻辑调用主动调用这个服务的时候,首先会创建Sbb的实体(sbbEntity)例如在序列图上看到的EnventRouteImpl这个SLEE这个内部的类就是执行这样的动作,在EnventRouteImpl中,首先实体暂时不和任何对象相互关联的,几个方法可以调用让Sbb对象将从对象池进入Ready状态,SbbCreate和SbbActivate,前者是在使用ChildRelation对象中的create的方法或者是事件产生的时候才被调用,后者是一个sbb对象需要调用一个现有的Sbb实体上的某个方法的时候才这样做,这种情况往往是一个没有Sbb对象对应一个Sbb实体而进入准备状态的时候。
对于一个处于Ready状态的Sbb对象,它已经和具体的某个Sbb实体相关联了,这个时候Sbb对象可以和Sbb实体之间同步暂态和稳态的数据了,例如一个数据库之间的同步。需要调用 SbbLaod 方法和SbbStore 方法。SbbLaod方法用数据库中的数据刷新变量的值,SbbStore方法把变量的值写入到数据库中,和EJB的规范一样,Sbb的开发者,也就是客户端是不能直接调用SbbLoad 方法和SbbStore 方法。
涉及到具体的业务处理的方法,SLEE要在业务处理方法调用之前调用 SbbLoad 方法刷新数据,业务处理方法执行之后,SLEE 容器又立即调用 SbbStore 方法把数据存储到数据库中。,开发人员在业务处理方法中不必刷新和存储实例变量的值,如果有兴趣浏览SLEE的源代码,可以看到,在一个事件到来的阶段,一旦一个Sbb实体和某个Sbb对象关联以后,将调用SbbLoad,而调用删除或者是Roll Back方法的时候,将调用SbbStore。
删除对象的方法EJB的生命周期非常相似,这里就不介绍了。
8.SLEE 如何删除 SBB 实体子目录
SLEE用附属点(attachment count)的机制来删除SBB实体树,它不再附属于任何一个行为实体,它将不再接收事件,也就被删除了,这个机制也就是JAVA垃圾回收机制的一种扩展。
9. Sbb 的持久稳定状态:
Sbb采用CMP域(CMP field)定义了它的稳定状态,CMP将持久保存的管理交给SLEE,SLEE规范中定义的CMP的体系是基本上基于Java EJB2.0规范中,它们之间的不同可以参看规范的6.5章节。
10.服务的定义(service.xml):
在SLEE里被展开的服务展开描述定义一个展开/管理的配置文件。可以参看Proxy中的service.xml
图7. 本图表示了一个外部资源如何将一个消息转交给Sbb处理的过程,从图中可以看到SLEE的端点获得了事件以后传递到核心事件处理系统(Event Routing)中,然后转交给事件处理的回调过程。
六.数据供应(Provisioned Data)和Profile(数据简表)
图8。数据供应的基本组成,从图上看SBB从Profile Table中得到各个Profile接口,而Profile从Profile Mbean服务器获得具体的数据内容,从Profile Management Client的角度上来看,来看是可读写的,而从Sbb获得配置的角度上看,是只读的。
1.数据供应(profile):
在SLEE中供应数据表现为阐述数据模型的profile,数据模型将profile集群到profile表中(profile table),Profile中的接口给则定义了应用程序调用的方式,一个Profile的规则包括了接口,类,和部署描述单元,用户采用这个规则访问profile,获得数据信息。
2.关于Profile规格的一些元素和各自的关系:
1> 从Sbb视角上看范例和执行流程;
我们可以下载OpenCloud的Slee的参考设计<Call Forwarding Call Blocking>,描述SBB对profile的调用,在这个Profile中包含了地址简表和资源简表两个对资源描述:
a. 在创建SBB的过程中。sbbCreate中首先通过调用JNDI 的 lookup() 方法执行查找,获得简表接口的实例--简表的工具Profilefacility;
b. 利用Profilefacility简表工具中的getProfileByIndexedAttribute方法,这个方法是Profile工具中定义的,只要应用了Profilefacility接口就可以使用到这个方法获得相应的简表。
带入的参数中第一个String profileTableName是一个Profile的表名,参数的第二个String attributeName是表中profile,例如在一个表中有address,mail,name这三个Profile,第三个参数就是attributeValue,也就是指具体的特性值,例如某个连接呼叫对应的目的地址值。返回的就是PorfileID也就是每个Porfile的鉴别符。
c. 根据profileid调用getProfile从profile表中获得profile,我们在上面介绍过一个Profile通过两个接口继承,这两个接口是AddressProfileCMP,和ResourceInfoProfileCMP,SLEE是从Resource Info Profile表的各个接口中获得资源适配器实体的相关信息,地址简表在本例中的继承扩展了另外的一些接口,例如用于对呼叫前转(Call Forwarding)的地址进行设定或者提取(使用setForwardingAddress和getForwardingAddress的两个方法来管理)
d. 获得了profile后就可以调用profile接口中定义好的方法来访问Profile资源了,例如设定或者获得呼叫前转地址。
2> 从管理视角上看Profile的管理过程:
ProfileManagement;一个Profile的管理,对于一个管理客户端来说,会有一个管理客户端(Profile Management Client)的接口,这个接口可以被网络管理代理所调用。通过这个接口可以设定或者是获得Profile的特性,SLEE对Profile管理对象进行实例化的时候,Profile管理对象将会缓冲,而且成为一个实时的运行对象,通过Porfile CMP接口调用持久化域中的设定和获得的方法(Set/Get)来执行对Profile的访问,这个和Sbb开发者的使用是一致的。
3> ProfileMbean接口:
Profile规范中规定了采用Profile中的ProfileManagementbean类来提供管理者对Profile的访问,更确切的说,外部的管理者调用ProfileMbean对象,ProfileMbean对象调用Profile缓冲的ProfileManagement对象,另外要注意的一点是Profile MBean类可以应用在任何JMX规范的MBean类型上,但是必须要在Mbean服务器上已经注册的接口才可以(也就是用CreateProfile创建的Mbean,这里可以把Profile Mbaen理解为JMX Mbean接口的应用)
ProfileMbean的接口可以参看规范156页的介绍;
七. 服务和部署(Service and Deployment)
总结上面所述我们总结一下基本的服务的部署模式:
和EJB类似,SBB的部署过程是基本相同的,只是服务性质服务描述上不一致,部署描述可以参看EJB的onMessage EJB的部署,部署者这个角色范例在SLEE的部署中同样有定义,开发者必须实现所有接口的抽象实例,一般这些实例包括:
a. 生命周期内的各个回调方法;
b. 事件(包含某些SLEE内部工具产生的事件,例如TIMER)处理句柄的方法;
c. 发送消息,和获得profile的一些抽象方法等等;
d. 获得子SBB的方法;
e. 访问Sbb中CMP域的一些方法;
f. 可共享的数据,例如行为实体等;
在部署服务期间所要定义的的XML文件和发布方法。
1> META-INF/sbb-jar.xml:sbb的jar文件,包含了: SBB描述符表示的是SBB的接口,类以及子类的关系,Activity Context的描述,
2> META-INF/event-jar.xml:Mobicents所感兴趣的消息,这里主要是SIP消息。 SBB开发者对事件的描述部分,我们在前面已经看到了很多介绍,在部署阶段,描述事件类型的Java类文件就是用该文件来进行。
3> META-INF/slee-profile-spec-jar.xml:用于对profile的类型进行定义。 在SLEE规范中定义,Profile的jar文件可以包装一个或者多个Profile规范。
4> service.xml:定义服务。服务描述文件是表示所部署的SBB服务的文件。
5> 部署文件: deployable-unit.xml:用于对jar执行文件进行部署,定义事件和定义服务,SBB,资源和profile将装入SLEE。
6> resource-adaptor-type-jar.xml:用于对资源适配器类型部署的描述。
7> 使用SleeCommandInterface来完成组件的部署任务:
对于部署和应用在Mobicents中,使用了SleeCommandInterface工具,JBOSS主要是使用JMX框架的,各个组件之间采用JMX进行通讯,各个服务组件(Mbean)都以插件的方式挂接在MBean服务器上,SleeCommandInterface工具中使用了jmx/rmi/RMIAdaptor的Mbean作为服务接口,在下面我们将以创建实体类的方法来介绍一下挂接资源适配器的过程,其他的服务部署和这个也是类似的。
本质上SLeeCommandInterface是一个用于对JAIN SLEE容器在命令行下的部署,启动服务,创建资源适配器的任务.通常而言,要启动SLEE容器,要部署和启动资源适配器,然后是部署和启动SLEE,关于"SLEE命令"大家可以参看源代码。
启动资源适配器是由DeploySipRA.sh来进行,而在范例中启动网关是由DeploySipProxy.sh来完成.
部署一个SIP资源适配器首先要部署(-deploy)两个JAR文件,一个是 sip-ra-type.jar另外一个是sip-local-ra.jar,在创建SIP资源适配器时候,在Mobicents的根目录下运行ant siptype和ant sipra就可以生成他们,前者包含了SIP事件和Activity的一些定义侧重于资源,的后者包含了协议本身的实现
。
下一步是-createRaEntity,也就是创建SIP的资源适配器, resource-adaptor-jar.xml中定义了资源配置器的jar文件,和resource-adaptor-type-jar.xml不同的地方在于,前者是直接面向特定某个资源配置器的类文件,例如这里指明了。
org.mobicents.slee.resource.sip.SipResourceAdaptor
告诉了SLEE资源配置器的调用入口的类,也就是SIP协议的封装,那么给谁调用呢,回到前面说的createRaEntity创建实体命令来,上面说了通过JMX的框架来完成调用创建的过程,首先在初始化SleeCommandInterface的过程当中,RemoteMbeanServer已经被定义为jmx/rmi/RMIAdaptor,而JNDI中的URL是SBB开发者定义的,在Mobicents中对资源适配器进行管理的是ResourceAdaptorMBean接口,而它的Mobicents中的实例化ResourceAdaptorMBeanImpl继承了JBOSS中的StandardMBean.这个接口提供了createResourceAdaptorEntity方法来创建资源适配器的实体,这个接口的实例化的过程带入的参数是当前在resource-adaptor-jar.xml中指定的SIP协议的封装,并且使用createResourceAdaptorEntity来创建实体SIPRA,实体在SLEE中是以ResourceAdaptorEntity类来表示的,其部署文件中会调用该类的方法来完成进一步的实施.
最后一步,就是要使用(激活)实体SIPRA了,使用命令-activateRaEntity来进行,和上面的创建实体的过程一样,同样是利用ResourceAdaptorMBean接口的来完成的,而方法则是使用了activateResourceAdaptorEntity方法来激活,经过一系列的方法的封装之后,最后会调用SipResourceAdaptor的start方法,创建侦听者,消息工厂,地址工厂的实例。
八.应用范例(SIP代理服务器,信令网关)
我们下面以SIP代理服务器(JainSipProxy)为例子来阐述一个基本SIP应用建立的过程:
事件接收:
1. 首先是SLEE接收到一个SIP消息.
2. 这个事件是一个初始化事件--intial-event
3.如果initial-event-select-variable中规定的事件是 ActivityContext,表示初始化事件带入的参数,
4. 当资源适配器发送一个消息到一个新的活跃实体上下文后,一个对应的服务将会初始化,Event Router可能也会创建一个新的子Sbb来进行处理。
5. 一个活跃实体上下文将会以一对一的方式对应一个事件,也就是Activity。
6. SIP的资源适配器将会把这个活跃实体定义为一个专门的SIP状态机(SIP Transaction)当中网关维持的SIP会话状态。
服务逻辑
JAIN SIP协议栈将会接收SIP消息,事件接收到以后,通过消息处理器发送到SLEE的事件引导部分EnventRouteImpl进行,这个时候,Sbb对象将要进入Ready状态了,调用SbbCreate回调将被调用。
对于初始化的事件来说,initial-event-select表示第一个事件触发时候将要加入的参数,我们来看这个参数在这里是Activity Context,也即是JAIN SIP事件发送到上面去的上下文,这个时候通过EnventRouteImpl选举到convergence names,你可以理解它为一个事件的回调句柄,也就是事件的消费者,在SBB事件处理回调会调用这个句柄,并且把ActivityContext作为参数带入。
例如这个时候事件INVITE使用onInviteEvent消息处理句柄,这个是有状态的服务,需要代理维持状态,例如INVITE消息以及后续的Trying,Ringing,OK等消息,无状态的如ACK消息,有状态的消息,意味着要创建一个状态机维持后续消息的状态,也就是ClientTransction和ServerTransction。这两个状态机都是维持会话的,而无状态呼叫,可以消息序列的最后一个消息。