snowolf

这样的一种生活
posts - 23, comments - 5, trackbacks - 0, articles - 11

Live Communications Server Application API (1)

Posted on 2006-01-17 09:43 snowolf 阅读(417) 评论(0)  编辑  收藏 所属分类: 其他

Live Communications Server Application API
发布时间:2005 年 6 月

 
LCS API套件中包括:
Microsoft SIP Processing Language(MSPL)、Microsoft.Rtc.Sip命名空间、application manifests 和为LCS构建SIP应用程序的资料。
Note   The Live Communications Server Application API currently does not support SIP dialogs and UAC server behaviors. Future versions of the API will add support for these features.
1. Application manifests
The application manifest is an XML document that describes a Live Communications Server application to the server on which it is running. This document is presented to the server when the application registers with Live Communications Server through the Microsoft.Rtc.Sip.ServerAgent managed class.
1.1 Application manifests构成:
Application manifests由以下三部分组成:
1.1.1 <alias:applicationManifest>标签。
在这个标签内装入应用程序属性(Application attributes)和消息过滤脚本(MSPL),可以为"alias"定义一个别名,微软建议使用默认别名"ls"。
1.1.2 A set of application attributes that defines key properties of the application, such as the SIP message types to filter and whether it is script-only.
1.1.3 用MSPL语言写的消息过滤脚本。
在CDATA element里被附加上<alias:splScript>标签。
1.2 Application manifests部属:
这个Application manifests文件,一次建立后,可以和装配件(assembly)里的resource一样,植入应用程序内部,或者像一个外部文件一样存在。
1.2.1 植入assembly内部:
用Microsoft.Rtc.Sip命名空间的ApplicationManifest类,在新生成的 ApplicationManifest对象里用Compile方法对它进行编译。样里代码如下:
        ResourceManager myResourceManager = new ResourceManager(MyApplicationType);
        
string appManifestXml = myResourceManager.GetString("appManifest");
        ApplicationManifest myAppManifest 
= ApplicationManifest.CreateFromString(appManifestXml);
        
try {
            myAppManifest.Compile();
        }

        
catch (CompilerErrorException cee) {
            Console.WriteLine(
"Failed to compile.");
            
foreach (string message in cee.ErrorMessages) {
                Console.WriteLine(message);
            }

            
return
;
        }

               
               编译成功后,这个编译过的Application manifests部属到代理服务器上,
1.2.2 外部文件格式:
下面是一个仅有SIP INVITE and MESSAGE请求的基本的application manifest:
               <?xml version="1.0" ?>
               
<
lc:applicationManifest
             
appUri="http://www.adatum.com/myApplicationLocation"

             xmlns:lc
="http://schemas.microsoft.com/lc/2003/05">

                  
<lc:requestFilter methodNames="INVITE,MESSAGE"
 
                              strictRoute
="false"
 
                        registrarGenerated
="true"

                        domainSupported
="true">

                  
<lc:responseFilter reasonCodes="NONE" />

                  
<lc:proxyByDefault action="true" />

                
<lc:scriptOnly />

             
<lc:splScript><![CDATA[

                              if (sipRequest) {
                            if (sipRequest.Method == StandardMethod.Invite) {
                          Dispatch("NameOfInviteHandlerMethodInApplicationHere");
                            }
                                 else if (sipRequest.Method == StandardMethod.Message) {
                                    Dispatch("NameofMessageHandlerMethodInApplicationHere");
                            }
                              }
             
]]></lc:splScript>

               
</lc:applicationManifest>
1.3 Application Attributes
Application Attibutes在消息过滤脚本的前面,在<lc:applicationManifest>标签的后面,包含详细的描述LCS应用程序的数据。例如下面所示:
               <?xml version="1.0" ?>
               
<
lc:applicationManifest
                
lc:appUri="http://www.adatum.com/applicationName"

                xmlns:lc
=http://schemas.microsoft.com/lcs/2004/05>
                 
application attributes
               <lc:splScript
><![CDATA[

                 message filter script
               
]]></lc:splScript>

</lc:applicationManifest>

       
       其中,xmlns属性指定了用那个版本的LCS来运行应用程序,如果是LCS2003就采用下面的方式:
       xmlns:lc=http://schemas.microsoft.com/rtc/2003/05
共有下列应用程序属性标签可供Application manifests使用:
1.3.1
 <lc:proxyByDefault  action="true|false" />

说明:为应用程序指定默认代理行为。
如果是true,服务器自动代理任意消息,应用程序不依据句柄执行。如果是false,消息被抛出,应用程序结束,同时,一方将不会收到它。
1.3.2
<lc:scriptOnly />

说明:当部署时,指定应用程序不含托管代码的成分。
1.3.3
<lc:requestFilter methodNames="METHOD_NAMES|NONE|ALL" registrarGenerated="true|false"       strictRoute="true|false"  domainSupported="true|false" />

说明:指定应用程序通过服务器的那些请求。
有下列methodNames
REGISTER
SUBSCRIBE
NOTIFY
BENOTIFY
ACK
BYE
INVITE
OPTIONS
MESSAGE
SERVICE
INFO
REFER
OTHER
1.3.4
<lc:responseFilter reasonCodes="RESPONSE_CLASSES|ALL|NONE" />

说明:定义应用程序通过服务器响应那个类别。
类别如下:
1XX
2XX
3XX
4XX
5XX
6XX
1.3.5
 <lc:file path="filePath"  keyColumnName=[columName] delimitedBy="comma/tab/whitespace"
              <column name
="columnName"/>
 
<lc:column name="ColumnName"/>

</lc:file>

说明:Name属性指引用的text文件名称,必须是有效的MSPL。Path属性是这个文件的决对路径,如果是相对的,它必须是相对于运行RTCSRV.exe的路径。
1.3.6
<lc:column name="columnName"

用这个标签可以指定一个或多个列节点。

2. Microsoft SIP Processing Language
       MSPL是用来过滤或路由SIP消息的脚本语言,像大家知道的"message filters",这个脚本是植入到application manifests里的LCS应用程序中。
MSPL不支持:
Explicit (declarative) data types
Type casting
Pointers
Declarations (other than basic function declarations, as detailed below)
Arithmetic operations
Iteration statements
Preprocessor statements
Attributes
Static or global variables (state is not maintained across script invocations)
2.1 MSPL Script Syntax
2.1.1 Function Invocation
 一个函数在MSPL里是唯一的,在MSPL里函数用如下语法格式来定义:
function ParseDisplayName (displayName) 
               }

所有的函数定义必须在消息过滤脚本的开始处,在过滤脚本之前。一个函数能有零个或更多的参数。为了防止无意中的循环,递归函数的调用是不允许的。互相的递归调用calls-calls模式也是不允许的。例如,如果函数1调用函数2,然后调用函数3,函数3不能调用1或2的任意一个。返回值类型是implicitly defined的,更确切的说,这个类型是传递的返回声明的值决定的。参数也是implicitly defined的。函数不能返回collections,它支持的的返回类型有strings,bools,integers和floats.
下面的函数示例过滤了一个特殊的string,如果这个string被找到返回true,否则,返回false.
               function FilterString (content) 
               
{
                              filterWord 
="confidential"

                               
return ContainsString(content, filterWord, true
);
               }
2.1.2 The foreach Statement
       在SIP消息处理过程中foreach允许脚本编写者比较句柄collections,例如contact header collections和 endpoint collections。下面示例演示了foreach loop的语法构成。
       foreach (element in expression) statement
For example:
               
foreach (dbEndpoint in

               QueryEndpoints(
"someone@example.com"))  }
       这段代码将求出collections里的值,Strings和其他unary types 像single-item collections一样的处理。
2.1.3 The break Statement
Break 的用法和C#中switch/case里的用法相同
2.1.4 The null Keyword
Null关键字,脚本语法能添加一个null关键字,与empty string ("")不同,empty string是不能建立的。有下列函数将返回一个null来表明值没有找到或者错误。
" GetDisplayName
" GetHostName
" GetParameterValue
" GetScheme
" GetUri
" GetUriParameter
" GetUserName
" GetUserAtHost
" GetXMLAttr
" QueryHomeServer
另外,Message.Stamp 和 Message.StampPool也返回null.

要:本文档将介绍从最初建造一个application manifest到最终安装和管理SIP application的详细过程,并提供示例代码,以演示开发扩展LCS应用的具体方法。


Live Communications Server Application API应用
1 在新的服务器布局里开发应用程序
Live Communications Server 2005支持新的服务器角色,包括:Standard Edition, Enterprise Edition, Enterprise Edition Pool, Enterprise Edition Back End Storage, Archiving Service, Director, Proxy, and Access Proxy。第三方应用程序能配置到上述任意或全部服务器上,以满足想得到的功能性。理解每个新的服务器角色的功能是开发和设计应用程序的要点。
2 建立一个Application Manifest
以LCS默认核心消息过滤脚本为例(此脚本原始位置:..\Program Files\Microsoft LC 2005\Server\ routing.am):
这个MSPL脚本过滤引入的SIP消息,并且以端点ID(EDID)、可用性合计、活动性数值和AgeOfPresence数值为基础,尝试为每一个消息选择出最好的端点。端点没有注册存在或者可用性合计小于100的将不被考虑。
示例代码

<?xml version="1.0">
<lc:applicationManifest
 lc:appUri="http://www.my_domain_here.com/DefaultRoutingScript"
 xmlns:lc="http://schemas.microsoft.com/lc/2003/05">
<lc:requestFilter methodNames="INVITE,MESSAGE,INFO,REFER,ACK,BYE,OTHER" 
                        strictRoute="false" 
                        registrarGenerated="true"
                        domainSupported="true"/ >
<lc:responseFilter reasonCodes="NONE" />
<lc:proxyByDefault action="true" />
<lc:scriptOnly />
<lc:splScript><![CDATA[
    //Log函数向LCS指定Server Log写入调试Log,具体内容除消息内容之外的全部消息。 
    Log( "Debug", 1, "we have a request - ", sipRequest.Method );

//Ge tUri方法返回消息头中的“To”
toUri = GetUri( sipRequest.To );
//Concatenate方法将用GetUserName得到的Sip中的toUri与@与GetHostName中的HostName
//组合成toUserAtHost
    toUserAtHost = Concatenate( GetUserName( toUri ), "@", GetHostName( toUri ) );

// rameterValue方法返回消息头重指定的参数”EPID”的值。
    requestEPID = GetParameterValue( sipRequest.To, "EPID" );

 //定义变量并赋初值
bestEPID = "";
    bestAgeOfPresence = 0x7FFFFFFF;
    bestAvailability = 0;
    bestActivity = 0;
    bestContactInfo = "";
    Log( "Debug", 1, "EPID - ", requestEPID );
Log( "Debug", 1, "toUserAtHost - ", toUserAtHost );
//循环全部端点
    foreach (dbEndpoint in QueryEndpoints( toUserAtHost, true )) {
        Log( "Debug", 1, "    endpoint.EPID - ", dbEndpoint.EPID );
        Log( "Debug", 1, "    endpoint.HasPresence - ", dbEndpoint.HasPresence );
        Log( "Debug", 1, "    endpoint.Availability - ", dbEndpoint.Availability );
        Log( "Debug", 1, "    endpoint.Activity - ", dbEndpoint.Activity );
        Log( "Debug", 1, "    endpoint.AgeOfPresence - ", dbEndpoint.AgeOfPresence );
        Log( "Debug", 1, "    endpoint.ContactInfo - ", dbEndpoint.ContactInfo );

 // 第一步,用SupportsMethod函数确定SIP方法是否被应用程序支持。
        if (!SupportsMethod( sipRequest.Method, dbEndpoint.StandardMethods, dbEndpoint.ExtraMethods )) {
  //跳出这个端点,因为不能处理请求的方法。
            Log( "Debug", 1, "        * skipped because of method" );
            continue;
            }
        if (requestEPID != "") {
            if (requestEPID == dbEndpoint.EPID) {
       //这个请求已经把一个能处理这个方法的端点当作目标,所以用这个端点。
                Log( "Debug", 1, "        * matched EPID" );
 //给前面定义的变量赋值
                bestContactInfo = dbEndpoint.ContactInfo;
                break;
            }
            else {
 //请求已确定EPID,但不匹配,跳出这个端点,继续执行。
                Log( "Debug", 1, "        * skipped because of EPID" );
                continue;
            }
        }

        if (!dbEndpoint.HasPresence ||
            dbEndpoint.Availability < 100 ||
            dbEndpoint.ContactInfo == ""
           ) {
            //如果在ContactInfo字段中没有在线,或者状态为“offline”,或者没有路由信息,跳出这个端点
             Log( "Debug", 1, "        * skipped because of presence, activity or contactinfo" );
            continue;
        }

        if (dbEndpoint.Availability < bestAvailability) {
            //如果没有可用性条出这个端点
            Log( "Debug", 1, "        * skipped because of availability" );
            continue;
        }

        if (dbEndpoint.Availability == bestAvailability) {
            if (dbEndpoint.Activity < bestActivity) {
                //如果这个端点活跃性小于1,跳出这个端点
                Log( "Debug", 1, "        * skipped because of activity" );
                continue;
                }
……(略)

3 为Live Communications Server 创建一个SIP应用程序
3.1 Setting up a Server Agent
server agent是你的应用程序和LCS之间的公共点。它是用ServerAgent类定义的对象,由它通过LCS代表你的应用程序发送和接收消息。
配置一个 LCS Agent的第一步是建立一个application manifest并编译。这个application manifest将要运行在服务器上并且通过消息过滤脚本仅接收你的应用程序感兴趣的SIP消息。这个动作是通过执行你的应用程序的Main方法实现的。例如下面代码:
using System.Threading;
using System.Resources;
using Microsoft.Rtc.Sip;


// 在这个例子中获取包含你的application manifest的XML string,
// 这个XML string //像资源一样包含在应用程序装配件中,用 
// ResourceManager.GetString(resourceName)方法得到。

ResourceManager myResourceManager 
= new ResourceManager(myApplicationClass);

//” appManifest”是你的application manifest XML 文件的名字,像添加应用程序// 的资源一样的添加它。

string myAppManifestXmlString = myResourceManager.GetString("appManifest");

ApplicationManifest myAppManifest 
= new ApplicationManifest(myAppManifestXmlString);
 
try {
//编译
 myAppManifest.Compile();
}

//异常处理
catch (CompilerErrorException cee) {
 Console.WriteLine(
"The following errors occurred during compilation:");
 
foreach (string errorMessage in cee.ErrorMessages) {
    Console.WriteLine(
"--- {0}"
, errorMessage);
 }

}

 

在实例化ServerAgent的时候提供ApplicationManifest对象,ServerAgent的构造函数会编译这个Application Manifest.例如下面代码。

try {
 ServerAgent myAppServerAgent 
= new
 ServerAgent(myAppManifest);
}
 
catch (NullReferenceException nre) 
{
 Console.WriteLine(
"The application manifest is uncompiled and ServerAgent cannot be instantiated."
);
}
 
catch (ServerNotFoundException snfe) 
{
 Console.WriteLine(
"The Live Communications Server is not available."
);
}

 

应用程序必须从消息过滤中分配句柄信息,同时,还必须建立一个类和这个句柄的方法对应。例如下面代码,如果你有一个call在你的消息过滤器里。
Dispatch("OnInviteReceived");
在你的定义的类上必须有一个相应的OnInviteReceived方法。


3.2 Processing Server Events
当Dispatch被脚本呼叫过,一个服务器事件便被触发。
3.3 Handling Messages from the Live Communications Server
当LCS收到消息,application manifest必须有特定的转发给相应得应用程序。
例如在application manifest中有下列代码:

if (sipRequest.Method == "MESSAGE")
 Dispatch(
"OnMessageReceived"
);
 Respond(
200
);
}


上述代码功能是, 当LCS分配SIP消息类型为“MESSAGE”的消息给“OnMessageReceived”你的应用程序的事件句柄,定义事件句柄的代码示例如下:

public void OnMessageReceived(object sender, RequestReceivedEventArgs requestEventArgs) {

 
// Obtain the Request object to process from RequestReceivedEventArgs.

 Request request = requestEventArgs.Request;
 
 
// Obtain the new server transaction for this request.

 ServerTransaction myServerTransaction = requestEventArgs.ServerTransaction;

 
//
 Processing logic here.
 
//
 

 
// Create a branch on the server transaction to forward the request.

 try
 
{
    
// Attempt to send the request.

    requestEventArgs.ServerTransaction.CreateBranch();
    requestEventArgs.SendRequest(request);
 }

 
catch (Exception e)
 
{
    Console.WriteLine(
"Unexpected exception: {0}\n{1}"

                            e.Message,
                            e.StackTrace);
 }
 
}


参考文档《Microsoft Office Live Communications Server 2005》 

只有注册用户登录后才能发表评论。


网站导航: