cpl extensions - kthdevlic/cpl extensions.pdf · cpl extensions alisa devlic royal institute of...
TRANSCRIPT
CPL extensions
Alisa Devlic
Royal Institute of Technology (KTH)
Report for course 2G1325/2G5564
31 May 2006
2
Table of contents
1. Introduction ............................................................................................................................ 3 2. CPL......................................................................................................................................... 4 3. SIP Express Router (SER)...................................................................................................... 7
3.1. Application scenarios ...................................................................................................... 7 3.2. CPL extensions for Context ............................................................................................ 8
3.2.1. Context switch.......................................................................................................... 8 3.2.2. XML DTD for proposed CPL extensions ................................................................ 9 3.2.3. Examples of CPL scripts ........................................................................................ 10
4. Context-aware VoIP prototype............................................................................................. 13 4.1. Implementation.............................................................................................................. 14
4.1.1. Client application ................................................................................................... 14 4.1.2. Match component ................................................................................................... 15 4.1.3. Wrapper .................................................................................................................. 15 4.1.4. CPL-C module extensions...................................................................................... 26
5. Measurements evaluation..................................................................................................... 27 5.1. Measurement 1 .............................................................................................................. 27 5.2. Measurement 2 .............................................................................................................. 29 5.3. Measurement 3 .............................................................................................................. 33
5.3.1. Simulation model ................................................................................................... 33 5.3.2. Simulation results ................................................................................................... 34
Conclusion............................................................................................................................ 41 Future work .......................................................................................................................... 41
Appendix A .............................................................................................................................. 44 Match component source code ............................................................................................. 44
Appendix B .............................................................................................................................. 52 Wrapper source code ............................................................................................................ 52
Appendix C .............................................................................................................................. 56 CPL-C module extensions.................................................................................................... 56
Appendix D .............................................................................................................................. 60 Measurements program souce code ..................................................................................... 60
Appendix E............................................................................................................................... 75 SER measurements tables .................................................................................................... 75
Measurement1 .................................................................................................................. 75 Measurement 2 ................................................................................................................. 76
3
1. 1. 1. 1. IntroductionIntroductionIntroductionIntroduction
Communication has become an essential part of user's everyday life. Some users would like to be reachable on multiple devices at anytime, anyplace. As a consequence, there has been a need to know and exploit a user's availability for communication (so called presence
information), so that he/she can control incoming calls and make the decision to accept this call or not based on the user's current context. Context information can be any information that can characterize a user and his/her current situation, such as the location, task, activity, time of the day, etc. The appearance and acceptance of the Session Initiation Protocol (SIP) [1] as a signalling protocol for next generation networks has opened the door for multiple services, such as Voice Over IP (VoIP), chat, games, instant messaging, and other innovative communication services. Built on top of existing data communication networks, this has enabled easy integration of voice and data services. The purpose of this paper is to use this context information to enhance the power of existing SIP call control services, to enable the user to have greater control over their incoming/outgoing calls. Ideally, the behavior of these services should be described using a set of rules and once they are defined they should execute on behalf of the user, but dissapear from the user's perspective (i.e. the user should not have to be aware of their existence unless these rules make a mistake – thus there also needs to be a way for the user to indicate when the rules have not achieved the user's goal; however, this is outside the scope of this report) . For this purpose we used Call Processing Language (CPL) and extend its syntax to enable the use of context information. We have chosen a scalable and reliable open source SIP platform, called SIP Express Router (SER) [2] to upload and execute CPL scripts. It can act as SIP registrar, proxy, or redirect server. It also provides advanced SIP features such as messaging and presence, translation between SIP and SMS or Jabber, RADIUS authentication and authorization, etc. We modified the code of the existing cpl-c module responsible for uploading and executing CPL scripts. The paper describes the essential components and concepts needed for realization of the described solution, as well as the problems and issues we faced on the way to achieving this goal. Finally, we made some measurements to test the prototype's scalability, performance, delay, and cost of adding reasoning compared to just uploading and executing conventional CPL scripts. The paper is organized as follows: first, we give an introduction to CPL and SER to give the reader some background concerning the problem. Second, we outline application scenarios that illustrate the value in having context parameters in call processing logic.
4
2. 2. 2. 2. CPLCPLCPLCPL The Call Processing Language (CPL) is a language used to describe and control Internet telephony services. It is described in RFC 3880 [3]. It works on top of SIP or H.323. It can be implemented on either network servers or user agents; as both can usefully process and direct users' calls. CPL is an XML-based language. It is simple, extensible, and independent of operating system or signalling protocol. It is not Turing complete, meaning that it doesn't support recursions, variables, loops, or calls to external programs. Its purpose is to prevent users from doing anything more complex than describe Internet telephony services. CPL scripts are XML documents. The Document Type Definition (DTD) for CPL is specified in the "cpl.dtd" file available at [4]. A CPL script consists of ancilliary information about the script and specifies call processing actions. Ancilliary information is information which is neccessary for a server to correctly process a script, but which does not directly describe any operations or decisions. A call processing action is a structured tree that describes operations and decisions a telephony signalling server performs upon a call setup event. There are two types of call processing actions: top-level actions and subactions. Top-level actions are actions that are triggered by signalling events that arrive at the server. Two top-level actions are defined: "incoming", the action performed when a call arrives whose destination is the owner of the script, and "outgoing", the action performed when a call arrives whose originator is the owner of the script. Subactions are actions which can be called from other actions. Subactions may not be called recursively. The graphical representation of a CPL action is shown in figure 1. An action is described by a collection of nodes that describe operations that can be performed or decisions that can be made. A node can have several parameters, which specify the behavior of the node. Nodes usually have outputs, which depend on the result of a decision or action. Nodes are represented as boxes, and outputs as arrows. Nodes are arranged in a tree, starting at a root node. Outputs of nodes are connected to other nodes.
5
Figure 1: Graphical representation of CPL script
When the action of the top-level node is invoked, based on the result of that node a server follows one of the node's outputs, and the subsequent node it points to is invoked. This procedure is repeated until a node with no outputs is reached. The namespace of the CPL extensions (so far for presence and network operators) will be http://www.rfc-editor.org/rfc/rfcxxxx.txt. Thus, this namespace declaration will be used in subsequent CPL scripts throughout the document. The full syntax of a CPL node is given here: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE cpl PUBLIC '-//IETF//DTD RFCxxxx CPL 1.0//EN' 'cpl.dtd'> <cpl> <subaction id="voicemail"> <location url="sip:[email protected]"> <redirect /> </location> </subaction> <incoming> <address-switch field=“origin” subfield=“host”> <address subdomain-of=“example.com”> <location url=“sip:[email protected]”> <proxy timeout=“10”>
6
<busy><sub ref=“voicemail”/></busy> <noanswer><sub ref=“voicemail“/></noanswer> <failure><sub ref=“voicemail”/></failure> </proxy> </location> </address> </address-switch> <otherwise> <sub ref=“voicemail”/> </otherwise> </incoming> </cpl> Both nodes and outputs are represented by XML tags. Node parameters are represented as XML tag attributes. There are four types of nodes: switches, which represent choices a CPL script can make, location modifiers, which add or remove locations from the location set, signalling operations, which cause signalling events in the underlying protocol, and non-
signalling operations, which trigger behavior which does not effect the underlying protocol. As noted earlier, CPL scripts can reside on a SIP proxy server, an application server, or intelligent agent. In our case, we have uploaded CPL scripts to a SIP proxy server, SER (see figure 2). When a SIP INVITE arrives (initiating an incoming or outgoing call), SER will execute the appropriate part of the user's CPL script that refers to an incoming or outgoing call and manage the call routing logic (accept and route the call to the callee, reject the call, redirect it to some third party, forward it to the voicemail, etc.).
������
Figure 2: CPL script upload
CPL scripts can be uploaded using SIP's REGISTER method or with the aid of graphical programs, such as CPLEd [5].
7
3. 3. 3. 3. SIP Express Router SIP Express Router SIP Express Router SIP Express Router (SER)(SER)(SER)(SER) A CPL script is parsed after uploading to SER. It is stored in an external MySQL database and is loaded and executed upon receiving incoming/outgoing call requests delivered by SIP INVITE messages. CPL scripts then process these calls.
Figure 3: SIP Express Router
3.1. 3.1. 3.1. 3.1. Application scenariosApplication scenariosApplication scenariosApplication scenarios
We tried to identify the need for context parameters by specifying scenarios that would be applicable in the real life situations. We also wanted to illustrate call processing possibilities that an end user would use.
1. Alice works for a company "example.com". When she is in a meeting and is presenting new solution to the management staff, she wants to forward all incoming calls to her voicemail. On the other hand, if she is listening to someone's presentation she may want to receive calls that are labeled with an urgent priority on her mobile phone. Otherwise she wants to receive the calls at her office phone.
2. During the working hours, meaning from Monday till Friday, 9am to 5pm, Alice wants to receive calls to her office phone, except when she is at lunch, then she would like that her calls be forwarded to her mobile phone.
3. When she is on vacation, Alice wants to reject all incoming calls from the company. 4. When Alice is in her car, she would like for safety reasons to set the policy to disable
all outgoing calls from her mobile phone while driving. 5. When she is on the business trip in the France, she prefers to get e-mails instead of
receiving expensive roaming calls on her mobile phone. However, if the language settings are set to French and the call is coming from her customer's company "trade.com", these calls should be forwarded to her mobile phone.
8
From the scenarios above we can extract the following context parameters: the context owner (the person to whom the context parameters relate to; i.e. Alice), location (office, home, vacation, business trip, car), task (in a meeting, at lunch), and activity (presenting,listening, driving). Later in the text we will give examples of CPL scripts for each of these scenarios.
3.2. 3.2. 3.2. 3.2. CPL CPL CPL CPL extensions for Contextextensions for Contextextensions for Contextextensions for Context
CPL extensions for context were designed to describe call processing services related to context relevant to Internet Telephony. We have chosen to use the following context parameters: user's location (e.g. home, office, car, business trip, vacation), task (e.g. lunch, in a meeting, relaxing), and activity (e.g. discussing, presenting, listening). In these extensions, we define a context-switch to support the services whose decisions are based on the context information of an end user. In CPL, switches represent choices a CPL script can make based on either attributes of the original call request or other items independent of a call. All switches have a list of conditions that can match a variable. Each condition corresponds to a node output. The output points to the next node that should be executed if the condition is true. When the CPL script is executed, the conditions are checked in the order they are presented in the script. The output of the first matching node is taken. The information affecting the choice is carried in the SIP message.
3.2.1. 3.2.1. 3.2.1. 3.2.1. Context Context Context Context switchswitchswitchswitch
Adding a "context switch" allows an end user to make decisions based on the current context parameters of a context owner. The context owner can be the user himself/herself or the user can specify context for some other person. (However, we will not consider this later case further in this paper.) Values of context parameters are specified in the user's ontology document. The user's context determines which script will be uploaded to the SER. When the context-switch node is invoked, it will match the context parameters set by ontology with context values in the CPL script and return the decision of how to process an incoming/outgoing call (accept, reject, redirect, voicemail, etc.). Node "context-switch" has one parameter "owner", that identies a context owner with his/her URI. Node "context" is the output of the "context-switch" node. It specifies different context attributes, such as: "location", "task", and "activity" of a context owner. These attributes were identified after analyzing application scenarios for a typical business user. The "location" parameter indicates a current user's location. The parameter value can be: "home", "office", "car", "bussiness trip", or "vacation". The "task" parameter indicates the current user's task status, whether he is "in a meeting", at "lunch", "relaxing", "working", or "talking on the phone".
9
The "activity" parameter expands the "task"parameter by adding the user's current activity, such as: "discussing", "listening", and "presenting". Based on the current activity, user can personalize his/her call control even further, so as not to miss the important calls. Syntax of the node "context-switch" and the "context" node is shown below: Node: context-switch Outputs: context specific context parameters to match Parameters: owner URI of a context owner Output: context Parameters: location location of a context owner task task status activity activity status
3.2.2. 3.2.2. 3.2.2. 3.2.2. XML DTD for XML DTD for XML DTD for XML DTD for proposed proposed proposed proposed CPL extensionsCPL extensionsCPL extensionsCPL extensions
The definition of CPL extensions for context is specified in the file "context.dtd". The part of the file that contains CPL extensions is shown below: <?xml version="1.0" encoding="UTF-8"?> <!-- Switch nodes --> <!ENTITY % Switch 'address-switch|string-switch|language-switch| time-switch|priority-switch|context-switch' > <!-- Context-switch makes choices based on context information. --> <!ELEMENT context-switch ( context*, (not-present, context*)?, otherwise? ) > <!ATTLIST context-switch owner CDATA #REQUIRED > <!ELEMENT context (%Node;) > <!ATTLIST context location CDATA #IMPLIED task CDATA #IMPLIED activity CDATA #IMPLIED > <!-- at least one and at most three of those attributes must appear --> An example of CPL script based on this extended CPL is shown in the next figure. Jim's SIP proxy server will reject the incoming call if he is in the meeting room called Grimeton, in a meeting, and if he is presenting. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE cpl SYSTEM 'file:C:/Programs/CPLEd/context.dtd'>
10
<cpl> <incoming> <context-switch owner="jim"> <context location="grimeton" task="meeting" activity="presenting"> <reject status="reject" reason="InMajorMeeting_And_Presenting"/> </context> </context-switch> </incoming> </cpl>
3.2.3. 3.2.3. 3.2.3. 3.2.3. Examples of Examples of Examples of Examples of CPL scriptsCPL scriptsCPL scriptsCPL scripts
As current CPL defines five types of switches: address switch, string switch, time switch, priority switch, and language switch, and we have added a sixth one, context swithch, different screening services can be created based on any of the above switches or combinations of them. We will give some examples of CPL scripts that will be based on the erarlier scenarios and will use some of the defined switches. 1. scenario: Alice works for a company "example.com". When she is in a meeting and is presenting new solution to the management staff, she wants to forward the incoming calls to the voicemail. On the other hand, if she is listening someone's presentation she may want to receive calls that are labled with an urgent priority on her mobile phone. Otherwise she wants to receive the calls at her phone in the office. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE cpl SYSTEM "C:\Programs\CPLEd\CPLscripts\context.dtd"> <cpl> <subaction id="voicemail"> <location url="sip:[email protected]"/> </subaction> <incoming> <context-switch owner="alice"> <context task="in a meeting" activity="presenting"> <sub ref="voicemail"/> </context> <context task="in a meeting" activity="listening"> <priority-switch> <priority equal="urgent"> <location url="tel:+46735759556"> <redirect/> </location> </priority> <otherwise> <sub ref="voicemail"/> </otherwise> </priority-switch> </context> <otherwise> <location url="sip:[email protected]">
11
<proxy/> </location> </otherwise> </context-switch> </incoming> </cpl> 2. scenario: During the working hours, meaning from Monday till Friday, 9am to 5pm, Alice wants to receive the calls to her phone in the office, except when she is at lunch, then she would like that her calls be forwarded to her mobile phone (+46735759556). <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE cpl SYSTEM "C:\Programs\CPLEd\CPLscripts\context.dtd"> <cpl> <incoming> <time-switch>
<time dtstart="20000703T090000" duration="PT8H" freq="weekly" byday="MO, TU, WE, TH, FR">
<context-switch owner="alice"> <context task="lunch"> <location url="tel:+46735759556"> <redirect/> </location> </context> <otherwise> <location url="sip:[email protected]"> <proxy/> </location> </otherwise> </context-switch> </time> </time-switch> </incoming> </cpl> 3. scenario: When she is on vacation, Alice wants to reject all incoming calls from the company. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE cpl SYSTEM "C:\Programs\CPLEd\CPLscripts\context.dtd"> <cpl> <incoming> <context-switch owner="alice"> <context location="vacation"> <address-switch field="owner"> <address subdomain-of="example.com"> <location url="sip:[email protected]"> <reject status="reject"
reason="I am on vacation"/> </location>
12
</address> </address-switch> </context> </context-switch> </incoming> </cpl> 4. scenario: When Alice is in the car, she would like for security reasons to disable all outgoing calls from her phone while driving. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE cpl SYSTEM "C:\Programs\CPLEd\CPLscripts\context.dtd"> <cpl> <outgoing> <context-switch owner="alice"> <context location="car" activity="driving"> <location url="tel: +46735759556"> <reject status="reject" reason="I am in the car!"/> </location> </context> </context-switch> </outgoing> </cpl> 5. scenario: When she is on a business trip in France, she prefers to get e-mails instead of receiving expensive roaming calls. Otherwise, if the language settings are set to French and calls are coming from her customer's company "trade.com", calls should be forwarded to her mobile phone. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE cpl SYSTEM "C:\Programs\CPLEd\CPLscripts\context.dtd"> <cpl>
<incoming> <context-switch owner="alice"> <context location="business trip"> <language-switch> <language matches="fr"> <address-switch field="origin"> <address subdomain-of="trade.com"> <location url="tel:+46735759556"> <redirect/> </location> </address> </address-switch> </language> <otherwise>
<mail url="mailto:[email protected]?subject=Write%20me%20an%20email!%20I%20am%20on%20the%20business%20trip"/>
13
</otherwise> </language-switch> </context> </context-switch> </incoming> </cpl>
4. 4. 4. 4. ContextContextContextContext----aware VoIP prototypeaware VoIP prototypeaware VoIP prototypeaware VoIP prototype
We want to make call processing dependent on context parameters. Context parameters are described in a user's ontology document. After uploading this ontology, the matching component (see figure 4) parses it, extracts the user's context parameters, and matches their values with the corresponding values in available CPL scripts to determine which script describes rules for the user's current context. Before the script is uploaded to SER, CPL scripts are stored in a CPL repository, while ontologies reside in a context repository. Upon receiving a call or INVITE message from a SIP UA, SER loads and executes the user's CPL script. If this CPL script contains a context-switch, it will match values set in script rules with the context values, and if they match, then it will take the appropriate actions. The wrapper component is used to retrieve context values set when the ontology was uploaded.
Figure 4: Context-aware VoIP prototype
14
4.1. 4.1. 4.1. 4.1. ImplementationImplementationImplementationImplementation
For our prototype we needed to implement a client application, match component, wrapper, and extensions to the CPL-C module of SER.
4.1.1. 4.1.1. 4.1.1. 4.1.1. Client appClient appClient appClient applicationlicationlicationlication
A simple client aplication is used for uploading ontologies and CPL scripts. It was meant to be used from different machines and different locations, hence the preferable choice was to implement it as an applet (see the following figures).
Figure 5: Client application
The user can upload an ontology or a CPL script. This applet was built as a proof of concept only. The alternative solution is to have two clients (applets), one for uploading context(ontology) and another for uploading scripts.
Figure 6: File chooser dialog
15
The benefit of this choice is that it is accessible from any computer on the Internet. The drawback of this solution is that applet code needs to reside on the same machine as SER is running on, and because of security restrictions it can upload only a localy stored CPL script. The applet opens the file chooser dialog (see figure above) to browse for a file to open. Therefore it requires a setup of java policy and it can run only in an applet viewer, but not in a browser. A browser-based version could be implemented that uses a service Common Gateway Interface (CGI) script [8].
4.1.2. 4.1.2. 4.1.2. 4.1.2. Match componentMatch componentMatch componentMatch component
The match component is responsible for parsing the uploaded ontology to get context values, determine the appropriate CPL script, and upload that script via SIP (or HTTP(S)) protocol to SER. Both choices are available, but we mainly focused on SIP in this prototype. If the CPL script was selected by an applet, then the match component uploads it directly to SER. The match component requires a SIP domain name exported by SER, the username, and the password of the user the CPL script refers to (see figure 7). SER will, upon receiving the script, store it in the database under the supplied user's credentials.
Figure 7: Data window
A free, open source CPLEd project (http://developer.berlios.de/projects/cpled/) for uploading CPL scripts via SIP and HTTP(S) was extended by adding a CPLEd.owl package to support the functions needed by our match component. This program is written in Java and its source code is provided in the appendix. The applet class is CPLExtensionsDemo.java.
4.1.3. 4.1.3. 4.1.3. 4.1.3. WrapperWrapperWrapperWrapper
The wrapper was envisioned to pass context values between java applications (applet and match component) and SER which was written in C. The first and obvious decision was to implement it using JNI (Java Native Interface), because the same context values were needed both by the match component and SER. Our idea was to use the Invocation API, allowing access to the native functionality from within Java code. How? By implementing Java setters and getters of context values as native methods, and then by using JNI calls to call back into the JVM from the wrapper program when executing CPL context-dependent scripts. This assumes that JVM is created inside the wrapper and that the path to C implementations of native methods is known beforehand.
16
Step-by-step implementation We started the implementation by defining a C structure that describes a user's context and a function that returns a pointer to that structure. We chose the bottom-up approach to illustrate step by step what components are needed to realize such a solution.
typedef struct {
str cntx_name; /* user who set the context */
str cntx_owner; /* context owner'*/
str cntx_loc; /* owner's location */
str cntx_task; /* owner's task */
str cntx_activ; /* owner's activity */
} Context;
Context* getContext();
The C structure ser already exists and was extensively used in SER code:
struct _ser {
char *s; /* string */
int len; /* string's length */
}
When reading "Essential JNI" book from Rob Gordon, we found a powerful tool, called structConverter, designed to create a Java class from a submitted C/C++ struct together with JNI functions for transferring data between them. So we decided to try it out! The problem was to find a source for it, as it wasn't publicly available on the Internet. Finally we managed to find a web site that hosted a javadoc for this source code, and the web pages containing the code. The book only gave instructions on how to setup the tool.
Figure 8: structConverter
The first important step is to store the context C struct file, called <cntx.h>, under the folder /usr/include.
17
In order to run the structConverter tool it is neccessary to run the C/C++ preprocessor on the <cntx.h> file. As we are using the gcc C compiler, the option –E invokes the C preprocessor:
gcc –E /usr/include/cntx.h > cntx_e.h
The output of this file is used as an input to structConverter. The output it produces consists of two files for each converted C structure: a java source file and an adapter functions file. The java source file has a class definitions that correspond to C structure members data types. The adapter file contains C adapter functions for the java class, that will be used by native functions for transfering data between this C struct and a java object.
Before running the structConverter, it requires a configuration file <jclass.cfg> in the /usr/include directory that tells structConverter the names of C structures for which it should generate java classes and the adapter code. The configuration file can take a second parameter to identify a package in which it will put the java class to reside. By entering:
Context CPLEd.posix
structConverter will generate the package CPLEd.posix in front of the java source file. Running the structConverter on the preprocessed file with a –c option will generate C adapter code and java source files for each C struct named in configuration file. For a C++ code generation, use the –C option instead.
java structConverter.structConverter –c cntx_e.h
Note that this command has to be invoked from the /usr/include directory, because structConverter requires access to the jclass.cfg file, and the CLASSPATH has to point to the structConverter class. This command will create classes <Context.java> and <str.java> (because Context strucure uses/includes str structure) under the package CPLEd.posix. Both classes will have (public) setter and getter methods for each of the field contained in the correspondent C structure, where instance variables are private and have the same names as the field names. They also contain a declaration for the private static native method initFIDs and a static block which calls initFIDs. Below is the java source Context.java generated for the Context structure. package CPLEd.posix;
public class Context {
private str cntx_name;
public void setCntx_name(str lcl_arg0) {
cntx_name = lcl_arg0;
}
public str getCntx_name() {
return cntx_name;
}
private str cntx_owner;
public void setCntx_owner(str lcl_arg0) {
cntx_owner = lcl_arg0;
}
public str getCntx_owner() {
return cntx_owner;
}
18
private str cntx_loc;
public void setCntx_loc(str lcl_arg0) {
cntx_loc = lcl_arg0;
}
public str getCntx_loc() {
return cntx_loc;
}
private str cntx_task;
public void setCntx_task(str lcl_arg0) {
cntx_task = lcl_arg0;
}
public str getCntx_task() {
return cntx_task;
}
private str cntx_activ;
public void setCntx_activ(str lcl_arg0) {
cntx_activ = lcl_arg0;
}
public str getCntx_activ() {
return cntx_activ;
}
private native static void initFIDs();
static {
initFIDs();
}
The setters and getters defined within a Java class are meant for the java world's manipulation of objects of this class. It is clearly a mapping of a C structure into the java code. Now, for the C world, there is a static call to the native method initFIDs, which is reponsible for initializing the field IDs of the C structure for each of the Java member variable. But the field Ids are valid only during the lifetime of the class! The adapter source files, generated by structConverter, are written to files <jniContext.c> and <jnistr.c>. This file contains: the static jfieldID declarations, the native implementations of initFIDs method, the adapter code for each field member of the C structure (or equivalently member variable of the generated Java class), two aggregate adapters for a bulk transfer of data from C struct to java, and vice versa, and include directives for all relevant include files. Note also, if your java classes have package names in front of their class names, the C adapter classes names have to be renamed as: <CPLEd_posix_Context.c>, <CPLEd_posix_Context.h>, <CPLEd_posix_str.c>, and <CPLEd_posix_str.h>, because javah, the C header and stub file generator, requires a fully qualified class name and underscores ( _ ) are used as class delimiters. If the class passed to javah is inside the package, the package name is preprended to both the header name and the structure name. Otherwise, javah prepends <jni> to both files. Below is the generated <CPLEd_posix_Context.c> file:
static void initContextFieldIDs(JNIEnv* env, jclass clazz) {
Context_Cntx_name_FID = (*env)->GetFieldID(env, clazz, "cntx_name",
"LCPLEd/posix/str;");
Context_Cntx_owner_FID = (*env)->GetFieldID(env, clazz, "cntx_owner",
"LCPLEd/posix/str;");
Context_Cntx_loc_FID = (*env)->GetFieldID(env, clazz, "cntx_loc",
"LCPLEd/posix/str;");
19
Context_Cntx_task_FID = (*env)->GetFieldID(env, clazz, "cntx_task",
"LCPLEd/posix/str;");
Context_Cntx_activ_FID = (*env)->GetFieldID(env, clazz, "cntx_activ",
"LCPLEd/posix/str;");
}
void jni_SetCntx_name_in_Context(Context* __Context_, JNIEnv *env, jobject
thisContext) {
jobject lcl_jobj0;
lcl_jobj0 = (*env)->GetObjectField(env, thisContext,
Context_Cntx_name_FID);
jni_SetS_in_str(&(__Context_->cntx_name), env, lcl_jobj0);
jni_SetLen_in_str(&(__Context_->cntx_name), env, lcl_jobj0);
}
void jni_GetCntx_name_from_Context(Context* __Context_, JNIEnv *env,
jobject thisContext) {
jobject lcl_jobj0;
lcl_jobj0 = (*env)->GetObjectField(env, thisContext,
Context_Cntx_name_FID);
jni_GetS_from_str(&(__Context_->cntx_name), env, lcl_jobj0);
jni_GetLen_from_str(&(__Context_->cntx_name), env, lcl_jobj0);
}
void jni_SetCntx_owner_in_Context(Context* __Context_, JNIEnv *env, jobject
thisContext) {
jobject lcl_jobj0;
lcl_jobj0 = (*env)->GetObjectField(env, thisContext,
Context_Cntx_owner_FID);
jni_SetS_in_str(&(__Context_->cntx_owner), env, lcl_jobj0);
jni_SetLen_in_str(&(__Context_->cntx_owner), env, lcl_jobj0);
}
void jni_GetCntx_owner_from_Context(Context* __Context_, JNIEnv *env,
jobject thisContext) {
jobject lcl_jobj0;
lcl_jobj0 = (*env)->GetObjectField(env, thisContext,
Context_Cntx_owner_FID);
jni_GetS_from_str(&(__Context_->cntx_owner), env, lcl_jobj0);
jni_GetLen_from_str(&(__Context_->cntx_owner), env, lcl_jobj0);
}
void jni_SetCntx_loc_in_Context(Context* __Context_, JNIEnv *env, jobject
thisContext) {
jobject lcl_jobj0;
lcl_jobj0 = (*env)->GetObjectField(env, thisContext,
Context_Cntx_loc_FID);
jni_SetS_in_str(&(__Context_->cntx_loc), env, lcl_jobj0);
jni_SetLen_in_str(&(__Context_->cntx_loc), env, lcl_jobj0);
}
void jni_GetCntx_loc_from_Context(Context* __Context_, JNIEnv *env, jobject
thisContext) {
jobject lcl_jobj0;
lcl_jobj0 = (*env)->GetObjectField(env, thisContext,
Context_Cntx_loc_FID);
jni_GetS_from_str(&(__Context_->cntx_loc), env, lcl_jobj0);
jni_GetLen_from_str(&(__Context_->cntx_loc), env, lcl_jobj0);
}
20
void jni_SetCntx_task_in_Context(Context* __Context_, JNIEnv *env, jobject
thisContext) {
jobject lcl_jobj0;
lcl_jobj0 = (*env)->GetObjectField(env, thisContext,
Context_Cntx_task_FID);
jni_SetS_in_str(&(__Context_->cntx_task), env, lcl_jobj0);
jni_SetLen_in_str(&(__Context_->cntx_task), env, lcl_jobj0);
}
void jni_GetCntx_task_from_Context(Context* __Context_, JNIEnv *env,
jobject thisContext) {
jobject lcl_jobj0;
lcl_jobj0 = (*env)->GetObjectField(env, thisContext,
Context_Cntx_task_FID);
jni_GetS_from_str(&(__Context_->cntx_task), env, lcl_jobj0);
jni_GetLen_from_str(&(__Context_->cntx_task).s, env, lcl_jobj0);
}
void jni_SetCntx_activ_in_Context(Context* __Context_, JNIEnv *env, jobject
thisContext) {
jobject lcl_jobj0;
lcl_jobj0 = (*env)->GetObjectField(env, thisContext,
Context_Cntx_activ_FID);
jni_SetS_in_str(&(__Context_->cntx_activ), env, lcl_jobj0);
jni_SetLen_in_str(&(__Context_->cntx_activ), env, lcl_jobj0);
}
void jni_GetCntx_activ_from_Context(Context* __Context_, JNIEnv *env,
jobject thisContext) {
jobject lcl_jobj0;
lcl_jobj0 = (*env)->GetObjectField(env, thisContext,
Context_Cntx_activ_FID);
jni_GetS_from_str(&(__Context_->cntx_activ), env, lcl_jobj0);
jni_GetLen_from_str(&(__Context_->cntx_activ), env, lcl_jobj0);
}
void jni_SetAll_in_Context(Context* __Context_, JNIEnv* env, jobject
thisContext) {
jni_SetCntx_name_in_Context(__Context_, env, thisContext);
jni_SetCntx_owner_in_Context(__Context_, env, thisContext);
jni_SetCntx_loc_in_Context(__Context_, env, thisContext);
jni_SetCntx_task_in_Context(__Context_, env, thisContext);
jni_SetCntx_activ_in_Context(__Context_, env, thisContext);
}
void jni_GetAll_from_Context(Context* __Context_, JNIEnv* env, jobject
thisContext) {
jni_GetCntx_name_from_Context(__Context_, env, thisContext);
jni_GetCntx_owner_from_Context(__Context_, env, thisContext);
jni_GetCntx_loc_from_Context(__Context_, env, thisContext);
jni_GetCntx_task_from_Context(__Context_, env, thisContext);
jni_GetCntx_activ_from_Context(__Context_, env, thisContext);
}
JNIEXPORT void JNICALL Java_CPLEd_posix_Context_initFIDs(JNIEnv *env,
jclass clazz) {
initContextFieldIDs(env, clazz);
}
We also added the implementation of getContext method to this file (to allocate the memory for the Context structure):
21
Context* getContext() {
Context* context=(Context*)malloc(sizeof(Context));
return context;
}
The adapter setters functions follow the same pattern: the first argument is a pointer to the C structure, the second is the Java runtime environment pointer, and the third argument is a jobject reference to the corresponding Java object. The adapter getter functions copy the value from the Java object to the C structure. structConverter also generates aggregate setters and getters: jni_SetAll_in_Context and jni_GetAll_from_Context. To tie everything together, we need a new class, called dbContext.java. Its role is to tie the java code and the C adapter code together. It extends the Context class to make available java setters and getters to the java application. It also declares native methods that populate the member variables of the Context class from the C Context structure. Finally, it ensures that all object member variables of the Context class are initialized.
package CPLEd.posix;
public class dbContext extends Context
{
public native void jni_commit();
public native void jni_retrieve(String owner);
public void commit() {
setCntx_name(new str());
setCntx_owner(new str());
setCntx_loc(new str());
setCntx_task(new str());
setCntx_activ(new str());
jni_commit();
}
public void retrieve(String owner){
jni_retrieve(owner);
}
}
The two native methods, commit and retrieve, will have their implementation in the CPLEd_posix_dbContext.c file. They act as helpers in the construction of native methods.
#include "CPLEd_posix_Context.h"
#include "CPLEd_posix_dbContext.h"
#include "/usr/include/cntx.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_CPLEd_posix_dbContext_jni_1commit
(JNIEnv *env, jobject thisContext) {
Context* res = getContext();
}
JNIEXPORT void JNICALL Java_CPLEd_posix_dbContext_jni_1retrieve
(JNIEnv *env, jobject thisContext, jstring owner) {
Context *res=getContext();
jni_GetAll_from_Context(res, env, thisContext);
}
22
The commit is called before any setter method to initialize java memeber variables. The retrieve method can be called later to retrieve all field values from the Context structure, or the values can be obtained one at a time.
Figure 9: JNI setter and getters of context values
From the match component we have set context values using JNI calls (see the figure above) over RMI (Remote Method Invocation). The remote class is called <UserDataImpl.java> and it implements a remote interface <UserDataInterface.java>. The <RMIClient.java> is used to bind the UserDataImpl service with RMI Registry. In SER's CPL-C module we have also implemented a function <rmi_jni.c> which is called from <cpl_switches.h> when the (context) CPL script is executed. It finds over RMI the appropriate service stub, calls getter methods through the JNI, and copies these values to the newly instantiated Context structure. Below is the code of the given classes.
<UserDataInterface.java>
package CPLEd.owl;
import java.rmi.*;
import CPLEd.posix.*;
public interface UserDataInterface extends Remote{
public void setOwner(String owner) throws RemoteException;
public void setUserLocation(String userLocation) throws
RemoteException;
public void setUserTask(String userTask) throws RemoteException;
public void setUserActivity(String userActivity) throws
RemoteException;
public void setUserName(String userName) throws RemoteException;
public String getOwner() throws RemoteException;
public String getUserLocation() throws RemoteException;
public String getUserTask() throws RemoteException;
public String getUserActivity() throws RemoteException;
public String getUserName() throws RemoteException;
}
23
<UserDataImpl.java>
package CPLEd.owl;
import java.rmi.*;
import java.rmi.server.*;
import CPLEd.posix.*;
public class UserDataImpl extends UnicastRemoteObject implements
UserDataInterface {
static {
try {
System.loadLibrary("dbContext");
} catch (UnsatisfiedLinkError e) {
System.out.println(e);
}
}
public UserDataImpl() throws RemoteException{
res=new dbContext();
res.commit();
}
public void setUserName(String userName) {
res.setCntx_name(getStr(userName));
}
public String getUserName() {
return res.getCntx_name().getS();
}
public void setOwner(String owner) {
res.setCntx_owner(getStr(owner));
}
public String getOwner() {
return res.getCntx_owner().getS();
}
public void setUserLocation(String userLocation) {
res.setCntx_loc(getStr(userLocation));
}
public String getUserLocation() {
return res.getCntx_loc().getS();
}
public void setUserTask(String userTask) {
res.setCntx_task(getStr(userTask));
}
public String getUserTask() {
return res.getCntx_task().getS();
}
public void setUserActivity(String userActivity) {
res.setCntx_activ(getStr(userActivity));
}
public String getUserActivity() {
return res.getCntx_activ().getS();
}
}
24
<rmi_jni.c> #include <rmi_jni.h>
#include "/usr/include/cntx.h"
#define USER_CLASSPATH "/home/devlic/public_html/myclasses"
#define JAVA_POLICY "/home/devlic/.java.policy"
JNIEnv* create_vm() {
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
JavaVMOption options[2];
jint ret;
options[0].optionString="-Djava.class.path=" USER_CLASSPATH;
options[1].optionString="-Djava.security.policy=" JAVA_POLICY;
vm_args.version=JNI_VERSION_1_2;
vm_args.options=options;
vm_args.nOptions=2;
vm_args.ignoreUnrecognized=JNI_FALSE;
ret=JNI_CreateJavaVM(&jvm,(void**)&env, &vm_args);
if (ret < 0) {
fprintf(stderr, "Can't create JVM\n");
}
return env;
};
Context* invoke_class() {
JNIEnv* env=create_vm();
Context* context=NULL;
jclass cls=(*env)->FindClass(env, "CPLEd/owl/RMIClient");
if (cls == 0) {
LOG(L_ERR,"Can't find RMIClient class\n");
} else {
jmethodID constructorID=(*env)->GetMethodID(env, cls, "<init>",
"()V");
if (constructorID == 0) {
LOG(L_ERR,"Can't find constructor\n");
}
jobject instance=(*env)->NewObject(env,cls, constructorID);
jmethodID mid=(*env)->GetMethodID(env, cls, "lookup",
"()LCPLEd/owl/UserDataInterface;");
if (mid == 0) {
LOG(L_ERR,"Can't locate the lookup method.
Exiting...\n");
}
jobject userData=(*env)->CallObjectMethod(env, instance, mid);
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
exit(1);
}
jclass userDataCls=(*env)->GetObjectClass(env,userData);
jmethodID mid2=(*env)->GetMethodID(env, userDataCls, "getUserName",
"()Ljava/lang/String;");
25
if (mid2 == 0) {
LOG(L_ERR,"Can't locate the getUserName method. Exiting...\n");
}
jstring userName=(jstring)(*env)->CallObjectMethod(env, userData,
mid2);
const char *str=(*env)->GetStringUTFChars(env, userName, 0);
context=(Context*)malloc(sizeof(Context));
strcpy(context->cntx_name.s, str);
context->cntx_name.len=strlen(str);
(*env)->ReleaseStringUTFChars(env,userName,str);
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
exit(1);
}
jmethodID mid3=(*env)->GetMethodID(env, userDataCls, "getOwner",
"()Ljava/lang/String;");
if (mid3 == 0) {
LOG(L_ERR, "Can't locate the getOwner method. Exiting...\n");
}
jstring owner=(jstring)(*env)->CallObjectMethod(env, userData, mid3);
const char *str2=(*env)->GetStringUTFChars(env, owner, 0);
strcpy(context->cntx_owner.s, str2);
context->cntx_owner.len=strlen(str2);
(*env)->ReleaseStringUTFChars(env,owner,str2);
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
exit(1);
}
jmethodID mid4=(*env)->GetMethodID(env, userDataCls, "getUserLocation",
"()Ljava/lang/String;");
if (mid4 == 0) {
LOG(L_ERR, "Can't locate the getUserLocation method.
Exiting...\n");
}
jstring location=(jstring)(*env)->CallObjectMethod(env, userData,
mid4);
const char *str3=(*env)->GetStringUTFChars(env, location, 0);
strcpy(context->cntx_loc.s, str3);
context->cntx_loc.len=strlen(str3);
(*env)->ReleaseStringUTFChars(env,location,str3);
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
exit(1);
}
jmethodID mid5=(*env)->GetMethodID(env, userDataCls, "getUserTask",
"()Ljava/lang/String;");
if (mid5 == 0) {
fprintf(stderr, "Can't locate the getUserTask method. Exiting...\n");
}
jstring task=(jstring)(*env)->CallObjectMethod(env, userData, mid5);
const char *str4=(*env)->GetStringUTFChars(env, task, 0);
strcpy(context->cntx_task.s, str4);
context->cntx_task.len=strlen(str4);
26
(*env)->ReleaseStringUTFChars(env,task,str4);
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
exit(1);
}
jmethodID mid6=(*env)->GetMethodID(env, userDataCls, "getUserActivity",
"()Ljava/lang/String;");
if (mid6 == 0) {
LOG(L_ERR, "Can't locate the getUserActivity method. Exiting...\n");
}
jstring activity=(jstring)(*env)->CallObjectMethod(env, userData,
mid6);
const char *str5=(*env)->GetStringUTFChars(env, activity, 0);
strcpy(context->cntx_activ.s, str5);
context->cntx_activ.len=strlen(str5);
(*env)->ReleaseStringUTFChars(env,activity,str5);
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
exit(1);
}
}
return context;
};
After the implementation was done, we have concluded that it is not possible to test this prototype with more than one user, because all values that are set when the ontology was uploaded are buffered into the memory and last until the match application is open. When we execute the context-dependent CPL script, the wrapper program retrieves these values from the buffer and uses them further. Without using a database and further modification of the program it is not possible to support multiple users and their call processing. Our next thought was that if we are going to use the database, then why should we need JNI over RMI? We can easily store context parameters when the ontology is parsed, and retrieve them by the wrapper program when the script is executed. The important issue is that even the first solution was more powerful and took a long time (cca 2 months) to implement it, it didn't solve our problems. So we changed our approach. What did we change? There are no longer remote classes and interfaces, and instead of calling native methods in setters and getters of <UserDataImpl.java>, we have methods for storing context data to the database. The wrapper program is also modified, we removed the file <rmi_jni.c>, and instead we have added a <context_db.c> file with functions for retrieving context parameters from the database. The source code is in Appendix B.
4.1.4. 4.1.4. 4.1.4. 4.1.4. CPLCPLCPLCPL----C module extensionsC module extensionsC module extensionsC module extensions
Here we will describe what is needed to add a new switch to the SER. We will show it on the example of the context-switch. The source files of cpl-c module reside in the folder $(SER_DIR)/modules/cpl-c.
27
First we added definitions of context-switch node and context node, and their attributes for building a cpl tree in the file <CPL_tree.h>, and the attributes are: owner attribute for context-switch, and location attribute, task attribute, and activity attribute for context node. Then, in the file <cpl_run.c> we modified the function cpl_run_script (struct cpl_interpreter*) to include behavior for processing a context-switch. The implementation of its behavior is described in the file <cpl_switches.h> with the function run_context_switch (struct
cpl_interpreter*). The function reads the parsed context values from the CPL script and compares them with the values retrieved from the user's record in the database. If they match, SER proceeds with the execution of the action specified within the switch. This method calls functions from <context_db.c> for retrieving context parameters from database. Finally, in the <cpl_parser.c> file we added functions for encoding attributes (their types and values) of context-switch and context node: encode_context_switch_attr (xmlNodePtr, char*, char*)
and encode_context_attr (xmlNodePtr, char*, char*). The source code of these files can be found in Appendix C.
5. 5. 5. 5. Measurements evaluationMeasurements evaluationMeasurements evaluationMeasurements evaluation
We measured scalability and performance of call processing in SER for a number of users, when their context is changing. Call processing and forwarding is done upon the execution of CPL script. Context change is performed through uploading different user's ontologies. The questions to answer will be:
1. What is a cost of adding ontologies and reasoning versus simply reading the SIP packet's header values?
Special attention should be given to the added complexity of CPL scripts, e.g. with nesting of context and other switch nodes, so it could be neccessary to go several times into ontology files to check the values.
2. How does the delay increase with the CPL script complexity? How does this affect the SER performanse?
3. How many users whose context is changing can SER serve simultaneously or during some period of time?
In the subsequent measurements we will measure response time (how long does it take for SER from receiving a call request from the client to generate a call processing response). For every series of measurements we did 10 iterations and we calculated the mean value and the standard deviation. Detailed tables are given in Appendix D.
5.1. 5.1. 5.1. 5.1. Measurement 1Measurement 1Measurement 1Measurement 1
In the first measurement we tried to evaluate the SER response time for executing CPL scripts with increasing complexity. We wanted to compare the time difference of SER when executing standard CPL switches that read SIP header fields against our context-switch that retrieves context parameters via an ontology. We will try to answer questions 1 and 2 from above: what is the added delay and what is the cost of adding ontologies. We started the measurements by executing a CPL script with one address-switch, and then each next time we added one more switch, up to 5 in total. We have done the same sorts of
28
text when executing context-dependent CPL scripts. The measurements resulted in the following figure.
0
0,002
0,004
0,006
0,008
0,01
0,012
0,014
1 2 3 4 5
Number of switches
Tim
e d
iffe
rence [s][s]]
Address-switch
Context-switch
Figure 10: Comparison of (standard and context-dependent) CPL scripts response times
We can see from the figure 10 that adding standard CPL switches didn't increase the response time – it remained almost constant, with total increase of 0,15 (in worst case 0,33) miliseconds, which is 4,6% (or 10%). When adding the context-switch, we can see a linear increase of response time with the number of context-switches. Adding one context-switch per time to a CPL script increases the response time from 0,4 up to 2,3 ms, which gives 5%-24% response time increase. The total response time increase for 5 context-switches is 46,60%. The next figure shows the comparison between different types of CPL scripts and their response times: first when we have the CPL script with 2 address-switches, the second script with 2 context-switches, and the third with 1 address-switch and 1 context-switch. The results show that combination of only context-switches is the most expensive one, while the combination of only address or other standard switches is the least expensive.
0
0,001
0,002
0,003
0,004
0,005
0,006
0,007
0,008
2 address-switches 2 context-switches 1 address 1 context
Response tim
e [s][
s]]
Figure 11: Comparison of different types of CPL scripts and their response times
29
5.2. 5.2. 5.2. 5.2. Measurement 2Measurement 2Measurement 2Measurement 2 We created a java program that created 100 users, and for each of them one conventional and one context-based CPL script. The program uploaded CPL scripts for all users: in the first iteration the conventional one and in the second the context-based one. It initiated calls from each of those 100 clients in the loop of ten rounds, so it could look like initiating simultaneous requests from multiple clients, and the program terminated when the transaction timed out. In the meantime SER has reached its processing limit, i.e. wasn't able to process calls anymore. The INVITE messages were arriving at SER, but were not processed, until the transaction has timed out and the program was terminated. We performed three series of these measurements, first when a conventional CPL script was loaded in SER, and then with a context-based CPL script. The graphs show the SER's response time, i.e. how long did it take from receiving the INVITE message until it sent back a provisional response ("603 Reject" in the first case, and "302 Moved temporarily" in the second case). We took the first 10, the middle 10, and the last 10 messages to do the measurements analysis, and present them on the same graph.
Load generator - context switch
0,000000
0,050000
0,100000
0,150000
0,200000
0,250000
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE messages
Response tim
e [s][s]]
First 10 messages
Middle 10 messages
Last 10 messages
Figure 12: First series of measurements with context-based CPL script
30
Load generator - context switch
0
0,05
0,1
0,15
0,2
0,25
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE messages
Response tim
e [s][s]]
First 10 messages
Middle 10 messages
Last 10 messages
Figure 13: Second series of measurements with context-based CPL script
Load generator - context switch
0
0,05
0,1
0,15
0,2
0,25
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE messages
Response tim
e [s][s]]
First 10 messages
Middle 10 messages
Last 10 messages
Figure 14: Third series of measurements with context-based CPL script
In all three measurements, SER has successfully processed 1485 INVITE messages from around 1650 messages in total that it received, and around 180 messages are sent after SER stopped with call processing and the transaction timed out. SER's total processing times (from the moment he received first message until he sent the last provisional response) were 12.44s, 12.05s, and 12,32s in the first, second, and third measurement, respectively. We can calculate the mean processing time per message as:
mean processing time/message = messages INVITE processedly successful ofnumber
timeprocessing total
and it was 7,6ms, 7,3ms, and 7,4ms respectively. The first 10 messages were sent after all hundred of users were registered, and when SER wasn't busy processing other requests. We can see from graphs (figure 12, 13, and 14) that the SER's average response times for processing first 10 messages were 16ms, 13ms, and 17ms.
31
The middle 10 messages were selected from the middle of the recorded traffic (i.e. we took into account all packets until SER was saturated and messages were no longer processed). In all three graphs we can see the same trend of an increase of the SER's response time. The average response time was 60ms, except in the first measurement where we have response time of 14ms, which is even less than in the case of first 10 messages. This phenomenon can be easily explained because we captured the time period right after SER has received and processed a burst of messages, so SER was in the similar state as he was in the phase of first 10 messages. Furthermore, just 30 ms after, the situation with the burst of messages and response time increase repeats. What actually happens is that for each INVITE message it receives, SER opens a new TCP connection. Thus, when it receives a burst of messages, it first opens a connection for each of them, and then starts to process calls. The third graph (fig.14) clearly indicates the response time increase between the 7th and the 8th INVITE message, because during that time SER was processing calls from earlier received messages. The last 10 messages were captured before SER stopped with call processing. In all cases we can observe the significant decrease of the SER's response time in the first five messages, and and then in the last five messages remaining almost a constant reponse time. The average response times were 78ms, 167ms, and 175ms. We have performed the same series of measurements with the conventional CPL script.
Load generator - address switch
0
0,01
0,02
0,03
0,04
0,05
0,06
0,07
0,08
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE messages
Response tim
e [s][s]]
First 10 messages
Middle 10 messages
Last 10 messages
Figure 16: First series of measurements with conventional CPL script
32
Load generator - address switch
0
0,01
0,02
0,03
0,04
0,05
0,06
0,07
0,08
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE messages
Response tim
e [s][s]]
First 10 messages
Middle 10 messages
Last 10 messages
Figure 17: Second series of measurements with conventional CPL script
Load generator - address switch
0
0,01
0,02
0,03
0,04
0,05
0,06
0,07
0,08
0,09
0,1
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE messages
Response tim
e [s][s]]
First 10 messages
Middle 10 messages
Last 10 messages
Figure 18: Third series of measurements with conventional CPL script
In these measurements the situation was different. In the first case, from the total of 1999 received INVITE messages, 1980 were successfully processed and 30 were sent after SER stopped with call processing and the transaction timed out. The second measurement was similar to first, from the total of 2089 received INVITE messages, 1978 were successfully processed and 121 were sent after the SER's saturation. The third case differs from the first two: from the total of 3968 received INVITE messages, 1771 were successfully processed and 1998 were sent afterwards. The mean processing times per message were 7,6ms, 7,3ms, and 7,4ms, so they don't differ from their counterpart context-based measurements. For the first 10 messages SER's reponse times were 10ms, 5ms, and 5ms. The second and the third measurement had the same behavior: they were slowly variating aroung the average value. The first measurement was slightly different which can also be observed from the response time. It has increased from 3ms to 10ms from the 2nd until the 7th message, and after that it dropped again to 3ms. This difference in behavior can be explained so that in the first
33
measurement first seven messages were sent one after another, before SER started to process them. The last three messages were sent after provisional responses were received for all previously received messages. In the last two measurements we can see that SER has starting with processing as soon as he received the first 2 messages. Thus the response times were shorter in the second and third case. In the middle 10 messages SER's average times were 23ms, 29ms, and 55ms. The first and the third measurement have the increasing trend of response time, while the second one shows the decreasing curve from the 5th message onwards (again because of the time interval where SER is busy with processing of requests and there are not many further incoming messages). In the last 10 messages SER's response times were 58ms, 59ms, and 46ms. The first and the second measurement have the same increasing trend, while the third one has a significant decrease of the response time from the 5th until the 7th message, which is caused by SER's late processing of previous burst of messages.
5.3. Measurement 35.3. Measurement 35.3. Measurement 35.3. Measurement 3 We wanted to see the difference of generating INVITE requests all at once or spreading these requests in time, to determine the average and peak SER processing rate. Therefore we have generated interarrival times of INVITE messages according to Poisson distribution model. The program we have created generates a stream of INVITE messages with a given mean throughput (�); the submission of the new message to SER does not depend on the reply status.
5.3.1. 5.3.1. 5.3.1. 5.3.1. Simulation modelSimulation modelSimulation modelSimulation model
We will model the generation of INVITE messages with a rate � and the mean message
interarrival time λ
1, where the probability of receiving an INVITE message follows the
cumulative probability distribution:
y=F(t)=1-e-�t
Traffic is generated by a huge number of users, i.e. n=1500. They are sending simultaneously INVITE messages. The arrival times of INVITE messages for one user are exponentialy distributed with a density probability function:
fx(x)= �*e-�x
The traffic is presented with a sum of arrival times for all n users, and its arrival times are Erlang distributed with the rate �*Erl(n, �). The Erlang distribution is the distribution of the sum of n independent identically distributed random variables each having an exponential
distribution. The density probability function of arrival times {Tn}n�N is:
fTn(x) = )!1( −n
nλ
e-�xxn-1
The interarrival times {An}n�N, An=Tn-Tn-1 (nth interval), are independent, identically distributed with the exponential law with parameter �.
34
The pseudocode of our call generator with Poisson interarrival times is:
n=0; T=0; repeat
A=λ
1(- log(1 - random(0,1))
T=T+A {Next arrival time} n=n+1 print(n,T) until end of simulation
If random function U is uniformly distibuted on [0,1] then λ
1(-log(1–U)) is exponentialy
distributed with rate �.
5.3.2. Simulation results5.3.2. Simulation results5.3.2. Simulation results5.3.2. Simulation results
We have performed several measurements on sending INVITE messages according to the Poisson distribution model with rates �=1, 10, 100, and 1000 packets/second. The measurements were first done with conventional, and then with context-based CPL script. We compare the results obtained in measurements 2 (sending all messages at once) with Poisson's results. In address measurements there were 1770-1980 successfully processed messages.
Poisson address load generator, �=1
0
0,005
0,01
0,015
0,02
0,025
0,03
0,035
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE messages
Response tim
e [s][s]]
First 10 messages
Middle 10 messages
Last 10 messages
Figure 19: Poisson's measurements with conventional CPL script, �=1
For �=1 we found from the measurements that the SER's mean processing time per message is 8,846 ms, meaning that it processes in average 113 messages per second.
35
Poisson address load generator, �=10
0
0,01
0,02
0,03
0,04
0,05
0,06
0,07
0,08
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE messages
Response tim
e [s][s]]
First 10 messages
Middle 10 messages
Last 10 messages
Figure 20: Poisson's measurements with conventional CPL script, �=10
For �=10 we calculated the SER's mean processing time per message to be 6,947 ms, meaning that it processes in average 144 messages per second.
Poisson address load generator, �=100
0
0,01
0,02
0,03
0,04
0,05
0,06
0,07
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE messages
Response tim
e [s][s]]
First 10 messages
Middle 10 messages
Last 10 messages
Figure 21: Poisson's measurements with conventional CPL script, �=100
For �=100 we found that SER's mean processing time per message is 7,018ms, meaning that it processes in average 142 messages per second. Figures 20 and 21 show the same results for the SER's processing time when �=10 and �=100. The processing time for the last 10 messages has increased for about 6-7 times.
36
Poisson's address load generator, �=1000
0
0,01
0,02
0,03
0,04
0,05
0,06
0,07
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE messages
Response tim
e
First 10 messages
Middle 10 messages
Last 10 messages
Figure 22: Poisson's measurements with conventional CPL script, �=1000 For �=1000 we found that SER's mean processing time per message is 6.99ms, meaning that it processes in average 143 messages per second. Figure 22 shows the result for the SER's processing time when �=1000.
Comparison of processing times for conventional CPL script
0
1
2
3
4
5
6
7
8
9
10
all at once �=1 �=10 �=100 �=1000
Generation of INVITE messages
Pro
cessin
g tim
e [m
s]m
s]
Figure 23: Comparison of mean processing times for the conventional CPL script
The comparison of mean processing times per message for the conventional CPL script is shown in fig.23. The expected interarrival times between messages were 1s, 0,1s, 0,01s, and 0,001s for �=1, �=10, �=100, and �=1000. It can be seen that the longest processing time was captured when we have generated INVITE messages according to Poisson distribution with rate �=1. Processing times for �=10, �=100, and �=1000 were almost equal, but shorter than when we generated all messages at once. The result obtained for �=1 is caused by longer interrarival times (in average 20 ms) than was the SER's processing time per message (8,8ms). In case of �=10, �=100, and �=1000 the mean message interarrival time was about the same as the SER's mean processing time (6,9-7 ms). The rate of sending messages to SER is also limited by the network (we were sending messages to SER residing on the another computer). We have investigated the last measurement with �=1000, where messages were generated with the highest rate. 6235 packets were generated in 31 second, which gives an average rate of 198 packets per second and a mean interarrival time of 5ms. The rest of 2 ms were taken by SER's responses (provisional responses and ACKs) and other traffic recorded from other machines in the lab.
37
The number of successfully processed messages is inverse proportional with the mean processing time and it is the biggest for the generation of INVITE messages with �=10 (and a little less for �=100), which had the shortest mean processing time (see fig.24).
Comparison of number of processed messages
0
20
40
60
80
100
120
140
160
all at once �=1 �=10 �=100 �=1000
Generation of INVITE messages
Num
ber
of m
essagesges
Figure 24: Comparison of the number of processed messages for the conventional CPL script
In all subsequent context measurements there were 1980 successfully processed messages.
Poisson context load generator, �=1
0
0,01
0,02
0,03
0,04
0,05
0,06
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE messages
Response tim
e [s][s]]
First 10 messages
Middle 10 messages
Last 10 messages
Figure 25: Poisson's measurements with context-based CPL script, �=1 For �=1 we found that the SER's mean processing time per message was 9,351ms, meaning that it processed in average 107 messages per second. If we compare it to the measurement with the correspondent conventional CPL script, we find an increase of 5,7% response time.
38
Poisson context load generator, �=10
0
0,02
0,04
0,06
0,08
0,1
0,12
0,14
0,16
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE messages
Response tim
e [s][s]]
First 10 messages
Middle 10 messages
Last 10 messages
Figure 26: Poisson's measurements with context-based CPL script, �=10 For �=10 we calculated the SER's mean processing time per message to be 7,645 ms, meaning that it processes in average 131 messages per second. If we compare it to the measurement with the correspondent conventional CPL script, we find an increase of 10% response time.
Poisson context load generator, �=100
0
0,05
0,1
0,15
0,2
0,25
0,3
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE messages
Response tim
e [s] [s
]
First 10 messages
Middle 10 messages
Last 10 messages
Figure 27: Poisson's measurements with context-based CPL script, �=100 For �=100 we found that SER's mean processing time per message is 7,816 ms, meaning that it processes in average 128 messages per second. If we compare it to the measurement with the correspondent conventional CPL script, we find an increase of 11,4% response time.
39
Poisson context load generator, �=1000
0
0,02
0,04
0,06
0,08
0,1
0,12
0,14
1 2 3 4 5 6 7 8 9 10
Sequence number of INVITE message
Response tim
e [s] [s
]
First 10 messages
Middle 10 messages
Last 10 messages
Figure 28: Poisson's measurements with context-based CPL script, �=1000 For �=1000 we found that SER's mean processing time per message is 7,587 ms, meaning that it processes in average 132 messages per second. If we compare it to the measurement with the corresponding conventional CPL script, we find a response time increase of 8,54%, which is an interesting result as the SER's response time decreased when compared to measurements with �=10 (7,645ms) and �=100 (7,816ms). This wasn't the case with Poisson's measurements with the conventional CPL script.
Comparison of processing times for context-based CPL script
0
1
2
3
4
5
6
7
8
9
10
all at once �=1 �=10 �=100 �=1000
Generation of INVITE messages
Mean p
rocessin
g tim
e [m
s][m
s]]
Figure 29: Comparison of the number of processed messages for the context-based CPL
script
A comparison of mean processing times per message for the context-based CPL script is shown in fig.29. It can be seen that the longest processing time was captured when we generated INVITE messages according to Poisson distribution with rate �=1. Processing times for �=10 and �=100 were again almost equal, but shorter than when we generated all messages at once. The results follow the same trend as in the case of measurements with the conventional CPL script, only the processing times are here slightly higher. The processing time for �=1000 was lower than in measurements with �=10 and �=100 and it is expected that it would further decrease up to 7ms with higher �.
40
We tried to determine the increase in mean processing times of context-based CPL script when compared to conventional CPL script in all specified cases. The increase was 12% in "all at once" case, 7% when �=1, 10% when �=10, 11% when �=100, and 8,54% when �=1000. The number of successfully processed messages is the biggest for the generation of INVITE messages with �=10 (and a little less for �=100), which had the shortest mean processing time (see fig.30).
Comparison of number of processed messages
0
20
40
60
80
100
120
140
all at once �=1 �=10 �=100 �=1000
Generation of INVITE messages
Num
ber
of m
essages
Figure 30: Comparison of the number of processed messages for the context-based CPL
script
41
Conclusion Conclusion Conclusion Conclusion
We have shown an effective way on how to extend CPL script with context parameters in the paper, the motivation behind this work, and its benefits. We have built a context-aware VoIP prototype and performed a series of measurements on SER's processing time with a conventional and context-based CPL script. We have compared the SER's peak and average processing rates in both cases for one user where we have started with CPL script with one switch, and then progressively add an additional switch, up to 5 in total. We found out that adding context switches increased the SER's response time for 5-24%, and the increase has followed a linear trend, whereas adding address-switches in conventional CPL script didn't have a significant impact on the increase of response time, it remained almost constant. We made similar measurements further with 100 users sending simultaneously INVITE messages. First we made a java program that sends all requests at once and compared obtained results. The SER's average response time was 7,4ms for both conventional and context-based CPL script (containing one switch in both cases). Furthermore, we made an experiment with distributing INVITE messages according to Poisson's distribution with �=1, 10, 100, and 1000 for both conventional and context-based CPL script. In both cases the longest response time was obtained for �=1, because interarrival times were much longer than the SER's processing time. In measurements with �=10, 100, and 1000 SER's response times were shorter () than in the previous measurements when requests were sent all at once. An interesting difference is captured in case of �=1000 with context-based CPL script, where SER's response time has decreased to 7,587ms compared to results with lower �s (7,816ms and 7,645ms), and in case of conventional CPL script the response time was the same as results with �=10 and 100 (7ms).
Future workFuture workFuture workFuture work
We have several plans for the future work. The ideas include:
• building a proxy between SIP UA and SIP proxy to be able to make "advanced" calls based on the call priority, language that caller has set, and a free form string set (to describe subject of the call, organization of the caller, user-agent name, or some other text which is intended to be displayed to the recipient). These options are used in CPL script switch nodes, but only the default values are possible to use so far. The proxy's function would be to recieve the call from the SIP UA (the INVITE message), start up the application to set up these parameters for the call, add them to SIP header, and forward the modified INVITE message to the SIP proxy. The reason for adding a proxy to the VoIP system and not modifying the SIP UA is to still be able to use the existing SIP phones on the market, while adding aditional features.
42
• adding sensor services like positioning systems (HotSpot, Cell-ID, GPS) and Calendar service to set the available context parameters: location, task, and activity in the ontology. Therefore, it is neccessary to extend the existing ontology to support different location formats and make a communication mechanism (push and pull) for sensor services to upload context values.
• explore other sensors both from the environment and existing sources of information
in the mobile devices, and try to embed them into the existing system. One of the future steps should be to find the way how to dynamically plug-in new sensor services and use them as context providers.
• make an upload of CPL script location-dependant. Using the software that detects and recognizes the Wi-Fi access point and upon entering/leaving the cell triggers a predefined command (developed by Dr. Kilander), we could make an extension that the command calls a batch file which uploads different CPL script based on the cell we have just entered in. This can be seen as an alternative approach to the one when
43
the sensor service is uploading only the context values, and then the match component determines which CPL script will be uploaded to the SER.
• last step of the future work would be to try to combine SIP UA, match component with CPL and context repository, SER proxy, and their respective functionalities into one component representing context-based SIP client, and try to embed it into the PDA. Further measurements on its performance and cost of adding new functionalities
could be later made. • new context-based SIP client could send context parameters within the INVITE
message (instead of using ontologies), so investigating the possibilities of adding new "context header" inside the INVITE message could be a possible scientific result of our work.
References [1] J.Rosenberg, H.Schulzrinne, G.Camarillo, A.Johnston, J.Peterson, R.Sparks, M.Handley, and E.Schooler: SIP: Session Initiation Protocol, RFC 3261, http://www.ietf.org/rfc/rfc3261.txt, June 2002. [2] SIP Express Router, iptel.org, http://www.iptel.org/ser/, April 2006. [3] J.Lennox, X.Wu, and H.Schulzrinne: Call Processing Language: A Language for User Control of Internet Telephony Services, RFC 3880, http://www.ietf.org/rfc/rfc3880.txt, October 2004. [4] CPL XML DTD draft, http://xml.coverpages.org/CPL-DTD-200201.txt, January 2002. [5] CPLEd, A CPL Editor, http://www.iptel.org/products/cpled/, September 2003. [6] L.Duang, C. Jennings, and D.Kelly: "Practical VoIP Using VOCAL", O'Reilly, July 2002. [6] R. Gordon, "Essential JNI: Java Native Interface (Essential Java)", Prentice Hall, 1998. [7] Y.Oukhay, "Context-aware services", Master of Science Thesis, Royal Institute of Technology, March 2006. [8] J. Lennox, H. Schulzrinne, and J. Rosenberg: "Common Gateway Interface for SIP", http://www.ietf.org/rfc/rfc3050.txt, January 2001.
44
AppendixAppendixAppendixAppendix A A A A
Match componentMatch componentMatch componentMatch component source code source code source code source code
<CPLExtensionsDemo.java> package CPLEd.owl;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import CPLEd.network.TransportCPL;
import CPLEd.tree.CPLTree;
import CPLEd.util.Global;
public class CPLExtensionsDemo extends JApplet implements ActionListener{
static private final String newline = "\n";
JButton loadOWLButton, loadCPLButton;
JTextArea log;
JFileChooser fc;
JTextField URLfield, USERfield;
JPasswordField PASSWDfield;
Inter inter;
public void init() {
try {
inter=new Inter();
inter.userData=new UserDataImpl();
getContentPane().setLayout(new BorderLayout());
setSize(250,200);
//Create the log first, because the action listeners
//need to refer to it.
log = new JTextArea(5,20);
log.setMargin(new Insets(5,5,5,5));
log.setEditable(false);
JScrollPane logScrollPane = new JScrollPane(log);
//Create a file chooser
45
fc = new JFileChooser("/home/devlic");
//Create the upload ontology button.
loadOWLButton = new JButton("Upload ontology");
loadOWLButton.addActionListener(this);
//Create the upload CPL button.
loadCPLButton = new JButton("Upload CPL");
loadCPLButton.addActionListener(this);
//For layout purposes, put the buttons in a separate panel
JPanel buttonPanel = new JPanel(); //use FlowLayout
buttonPanel.add(loadOWLButton);
buttonPanel.add(loadCPLButton);
buttonPanel.setVisible(true);
//Add the buttons and the log to this panel.
getContentPane().add(buttonPanel, BorderLayout.PAGE_START);
getContentPane().add(logScrollPane, BorderLayout.CENTER);
setVisible(true);
} catch (Exception e) {
System.out.println(e.getMessage());
System.exit(-1);
}
}
public void actionPerformed(ActionEvent e) {
Parser parser=new Parser();
String pathToScript="";
//Handle upload ontology button action.
if (e.getSource() == loadOWLButton) {
int returnVal = fc.showOpenDialog(CPLExtensionsDemo.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File ontology = fc.getSelectedFile();
//This is where a real application would open the file.
log.append("Loading: " + ontology.getName() + "." +
newline);
try {
parser.parseOWL(ontology, inter.userData);
pathToScript=inter.determineScriptToUse(inter.userData);
inter.userData.setPathToScript(pathToScript);
JFrame frame=getConnPropertiesFrame(inter.userData);
frame.setVisible(true);
} catch (Exception e1) {
e1.printStackTrace();
}
} else {
log.append("Load command cancelled by user." + newline);
}
log.setCaretPosition(log.getDocument().getLength());
//Handle upload CPL button action.
} else if (e.getSource() == loadCPLButton) {
int returnVal = fc.showSaveDialog(CPLExtensionsDemo.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File cpl = fc.getSelectedFile();
//This is where a real application would save the file.
log.append("Loading: " + cpl.getName() + "." + newline);
46
try {
pathToScript=cpl.getAbsolutePath();
System.out.println("cpl absolute path=" +
pathToScript);
inter.userData.setPathToScript(pathToScript);
JFrame frame=getConnPropertiesFrame(inter.userData);
frame.setVisible(true);
} catch (Exception e1) {
e1.printStackTrace();
}
} else {
log.append("Load command cancelled by user." + newline);
}
log.setCaretPosition(log.getDocument().getLength());
} else if ( e.getActionCommand().equals(" OK ") )
{
Global global=Global.getInstance();
String type="SIP";
/* fetch the setting from the graphic elements */
String url = URLfield.getText();
String user = USERfield.getText();
String passwd = new String(PASSWDfield.getPassword());
/* save the setting for the next time */
if ( !user.trim().equals("") )
global.setProp( "user", user);
if ( !url.trim().equals("") )
if ( type.toUpperCase().equals("SIP") )
global.setProp("SipURL", url );
else
global.setProp("HttpURL", url );
if ( !passwd.trim().equals("") )
global.setProp("passwd", passwd );
global.setBoolProp("IsApplet", true);
global.setApplet(this);
global.setProp("DTDlocation",
"/home/devlic/From_Younes/CPLEd/CPLEd/CPLscripts/context.dtd");
global.setProp("SIPport", "5060");
global.setProp("HTTPpath", "./cpl/server.pl");
try {
global.setCPLTree(new CPLTree(global));
pathToScript=inter.userData.getPathToScript();
System.out.println("Gotten path to script=" +
pathToScript);
global.getCPLTree().loadCPL(new
FileInputStream(pathToScript));
TransportCPL cpl_uploader=new TransportCPL(global,
"upload", type);
cpl_uploader.executeOperation();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
private JFrame getConnPropertiesFrame(UserDataImpl userData) {
JFrame frame=new JFrame();
frame.setSize(250,200);
/* build the window contents*/
47
frame.getContentPane().setLayout( new GridLayout( 4 , 0 ) );
/* server url panel */
JPanel p0 = new JPanel( new FlowLayout( FlowLayout.CENTER) );
p0.add( new JLabel("Server URL : ") );
URLfield = new JTextField(20);
p0.add( URLfield );
p0.setVisible( true );
frame.getContentPane().add( p0 );
URLfield.setText("ccsser1.wireless.kth.se");
/* username panel */
JPanel p2 = new JPanel( new FlowLayout( FlowLayout.CENTER) );
p2.add( new JLabel("User name : ") );
USERfield = new JTextField(20);
USERfield.setText(userData.getUserName());
p2.add( USERfield );
p2.setVisible( true );
frame.getContentPane().add( p2 );
/* passward panel */
JPanel pp = new JPanel( new FlowLayout( FlowLayout.CENTER) );
pp.add( new JLabel("Password : ") );
PASSWDfield = new JPasswordField(20);
pp.add( PASSWDfield );
pp.setVisible( true );
frame.getContentPane().add( pp );
/* add control buttons */
JPanel p3 = new JPanel( new FlowLayout( FlowLayout.CENTER,20,5)
);
JButton button1 = new JButton(" OK ");
button1.addActionListener( this );
p3.add( button1 );
JButton button2 = new JButton("Cancel");
button2.addActionListener( this );
p3.add( button2 );
p3.setVisible( true );
frame.getContentPane().add( p3 );
return frame;
}
}
<UserDataHandler.java> package CPLEd.owl;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
public class UserDataHandler extends DefaultHandler {
boolean ownerBoolean;
boolean locationBoolean;
boolean taskBoolean;
boolean activityBoolean;
boolean hostNameBoolean;
boolean hostDomainBoolean;
boolean phoneNumBoolean;
boolean phoneDomainBoolean;
String userName;
String owner;
String location;
48
String task;
String activity;
String hostName;
String hostDomain;
String phoneNumber;
String phoneDomain;
UserDataImpl user;
public UserDataHandler(UserDataImpl userData) {
super();
this.user=userData;
}
public void startElement(String namespaceURI, String localName,
String rawName, Attributes attributes) throws SAXException {
if (rawName.equals("acas:User")) {
for (int index=0; index<attributes.getLength();index++) {
if (attributes.getQName(index).equals("rdf:ID")) {
userName=attributes.getValue(index);
}
}
} else if (rawName.equals("acas:hasOwner")) {
ownerBoolean=true;
} else if (rawName.equals("acas:hasLocation")) {
locationBoolean=true;
} else if (rawName.equals("acas:hasTask")) {
taskBoolean=true;
} else if (rawName.equals("acas:hasActivity")) {
activityBoolean=true;
} else if (rawName.equals("acas:hasHostName")) {
hostNameBoolean=true;
} else if (rawName.equals("acas:hasHostDomain")) {
hostNameBoolean=true;
} else if (rawName.equals("acas:hasPhoneNumber")) {
phoneNumBoolean=true;
} else if (rawName.equals("acas:hasPhoneDomain")) {
phoneDomainBoolean=true;
}
}
public void characters(char[] ch, int start, int end) throws
SAXException {
if (ownerBoolean) {
owner=new String(ch, start, end);
} else if (locationBoolean) {
location=new String(ch, start, end);
} else if (taskBoolean) {
task=new String(ch, start, end);
} else if (activityBoolean) {
activity=new String(ch, start, end);
} else if (hostNameBoolean) {
hostName=new String(ch, start, end);
} else if (hostDomainBoolean) {
hostDomain=new String(ch, start, end);
} else if (phoneNumBoolean) {
phoneNumber=new String(ch, start, end);
} else if (phoneDomainBoolean) {
phoneDomain=new String(ch, start, end);
}
}
49
public void endElement(String namespaceURI, String localName, String
rawName) throws SAXException {
if (ownerBoolean) {
ownerBoolean=false;
}
if (locationBoolean) {
locationBoolean=false;
}
if (taskBoolean) {
taskBoolean=false;
}
if (activityBoolean) {
activityBoolean=false;
}
if (hostNameBoolean) {
hostNameBoolean=false;
}
if (hostDomainBoolean) {
hostDomainBoolean=false;
}
if (phoneNumBoolean) {
phoneNumBoolean=false;
}
if (phoneDomainBoolean) {
phoneDomainBoolean=false;
}
}
public void endDocument() throws SAXException {
user.setUserName(userName);
user.setOwner(owner);
user.setUserLocation(location);
user.setUserTask(task);
user.setUserActivity(activity);
user.setUserHostName(hostName);
user.setUserHostDomain(hostDomain);
user.setUserPhoneNum(phoneNumber);
user.setUserPhoneDomain(phoneDomain);
System.out.println("Finished with analyzing document");
}
}
<UserDataImpl.java> package CPLEd.owl;
import java.sql.*;
import java.util.*;
import java.io.*;
public class UserDataImpl extends UnicastRemoteObject implements
UserDataInterface {
String userName="";
String owner="";
String userLocation="";
String userTask="";
String userActivity="";
String userHostName="";
String userHostDomain="";
String userPhoneNum="";
50
String userPhoneDomain="";
String pathToScript="";
static Connection connection;
private PreparedStatement statement;
private java.sql.Statement statement1;
private java.sql.ResultSet rs;
private void displaySQLErrors(SQLException e) {
System.out.println("SQL exception: " + e.getMessage());
System.out.println("SQL state: " + e.getSQLState());
System.out.println("Vendor error: " + e.getErrorCode());
}
public void connectToDB() {
System.out.println("connectionToDB START");
try {
connection=DriverManager.getConnection("jdbc:mysql://ccsser1.wi
reless.kth.se/ser","ser","puficat");
System.out.println("Connection to DB established reference: " +
connection);
System.out.println("connectionToDB END");
}catch(SQLException e) {
displaySQLErrors(e);
}
}
public static void closeConnection() {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public UserDataImpl(){
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
connectToDB();
} catch (Exception e) {
System.out.println("Unable to find and load driver!");
}
}
public void setOwner(String owner) {
this.owner=owner;
try {
statement=connection.prepareStatement("INSERT INTO
context(user) VALUES('" + owner + "') ON DUPLICATE KEY UPDATE user='" +
owner + "'");
int i=statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public String getOwner() {
return owner;
}
public void setUserLocation(String userLocation) {
this.userLocation = userLocation;
51
try {
statement=connection.prepareStatement("UPDATE context SET
location='" + userLocation + "' WHERE user='" + owner + "'");
int i=statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public String getUserLocation() {
return userLocation;
}
public void setUserTask(String userTask) {
this.userTask = userTask;
try {
statement=connection.prepareStatement("UPDATE context SET
task='" + userTask + "' WHERE user='" + owner + "'");
int i=statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public String getUserTask() {
return userTask;
}
public void setUserActivity(String userActivity) {
try {
statement=connection.prepareStatement("UPDATE context SET
activity='" + userActivity + "' WHERE user='" + owner + "'");
int i=statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public String getUserActivity() {
return userActivity;
}
public void setPathToScript(String pathToScript) {
this.pathToScript=pathToScript;
}
public String getPathToScript() {
return pathToScript;
}
public void setUserHostDomain(String userHostDomain) {
this.userHostDomain = userHostDomain;
}
public String getUserHostDomain() {
return userHostDomain;
}
public void setUserHostName(String userHostName) {
this.userHostName = userHostName;
}
52
public String getUserHostName() {
return userHostName;
}
public void setUserPhoneDomain(String userPhoneDomain) {
this.userPhoneDomain = userPhoneDomain;
}
public String getUserPhoneDomain() {
return userPhoneDomain;
}
public void setUserPhoneNum(String userPhoneNum) {
this.userPhoneNum = userPhoneNum;
}
public String getUserPhoneNum() {
return userPhoneNum;
}
}
(<CPLDataHandler.java>, <Parser.java>, <Inter.java> have already been described in [7])
Appendix BAppendix BAppendix BAppendix B
Wrapper soWrapper soWrapper soWrapper source codeurce codeurce codeurce code
<context_db.c> #include "../../mem/shm_mem.h"
#include "../../db/db.h"
#include "../../dprint.h"
#include "cpl_db.h"
#include "context_db.h"
static db_con_t* db_hdl=0;
static db_func_t cpl_dbf;
int context_db_bind(char* db_url)
{
if (bind_dbmod(db_url, &cpl_dbf )) {
LOG(L_CRIT, "ERROR:context_db_bind: cannot bind to database module! "
"Did you forget to load a database module ?\n");
return -1;
}
/* CPL module uses all database functions */
if (!DB_CAPABILITY(cpl_dbf, DB_CAP_ALL)) {
LOG(L_CRIT, "ERROR:context_db_bind: Database modules does not "
"provide all functions needed by cpl-c module\n");
return -1;
}
return 0;
53
}
int context_db_init(char* db_url, char* db_table)
{
if (cpl_dbf.init==0){
LOG(L_CRIT, "BUG: context_db_init: unbound database module\n");
return -1;
}
db_hdl=cpl_dbf.init(db_url);
if (db_hdl==0){
LOG(L_CRIT,"ERROR:context_db_init: cannot initialize database "
"connection\n");
goto error;
}
if (cpl_dbf.use_table(db_hdl, db_table)<0) {
LOG(L_CRIT,"ERROR:context_db_init: cannot select table
\"%s\"\n",db_table);
goto error;
}
return 0;
error:
if (db_hdl){
cpl_dbf.close(db_hdl);
db_hdl=0;
}
return -1;
}
int get_user_location(str *user, str *location, const char *key){
db_key_t keys_cmp[1];
db_key_t keys_ret[1];
db_val_t vals[1];
db_res_t *res = 0 ;
keys_cmp[0]="user";
keys_ret[0]=key;
DBG("DEBUG:get_user_location: fetching location for user <%s>\n",user-
>s);
vals[0].type = DB_STRING;
vals[0].nul = 0;
vals[0].val.string_val = user->s;
if (cpl_dbf.query(db_hdl, keys_cmp, 0, vals, keys_ret, 1, 1, NULL, &res)<
0){
LOG(L_ERR,"ERROR:cpl-c:get_user_location: db_query failed\n");
goto error;
}
if (res->n==0) {
DBG("DEBUG:get_user_location: user <%.*s> not found in db -> probably "
"he has no location\n",user->len, user->s);
location->s = 0;
location->len = 0;
} else {
if (res->rows[0].values[0].nul) {
DBG("DEBUG:get_user_location: user <%.*s> has a NULL location\n",
user->len, user->s);
location->s = 0;
location->len = 0;
} else {
DBG("DEBUG:get_user_location: we got the location=%s\n",
res->rows[0].values[0].val.str_val.s);
54
location->len = res->rows[0].values[0].val.str_val.len;
location->s = shm_malloc( location->len );
if (!location->s) {
LOG(L_ERR,"ERROR:cpl-c:get_user_location: no free sh_mem\n");
goto error;
}
memcpy(location->s, res->rows[0].values[0].val.str_val.s,location-
>len);
}
}
cpl_dbf.free_result(db_hdl, res);
return 1;
error:
if (res)
cpl_dbf.free_result(db_hdl, res);
location->s = 0;
location->len = 0;
return -1;
}
int get_user_task(str *user, str *task, const char *key){
db_key_t keys_cmp[1];
db_key_t keys_ret[1];
db_val_t vals[1];
db_res_t *res = 0;
keys_cmp[0]="user";
keys_ret[0]=key;
DBG("DEBUG:get_user_task: fetching task for user <%s>\n",user->s);
vals[0].type = DB_STRING;
vals[0].nul = 0;
vals[0].val.string_val = user->s;
if (cpl_dbf.query(db_hdl, keys_cmp, 0, vals, keys_ret, 1, 1, NULL, &res)<
0){
LOG(L_ERR,"ERROR:cpl-c:get_user_task: db_query failed\n");
goto error;
}
if (res->n==0) {
DBG("DEBUG:get_user_task: user <%.*s> not found in db -> probably "
"he has no task\n",user->len, user->s);
task->s = 0;
task->len = 0;
} else {
if (res->rows[0].values[0].nul) {
DBG("DEBUG:get_user_task: user <%.*s> has a NULL task\n",
user->len, user->s);
task->s = 0;
task->len = 0;
} else {
DBG("DEBUG:get_user_task: we got the task=%s\n",
res->rows[0].values[0].val.str_val.s);
task->len = strlen(res->rows[0].values[0].val.str_val.s);
task->s = shm_malloc( task->len );
if (!task->s) {
LOG(L_ERR,"ERROR:cpl-c:get_user_task: no free sh_mem\n");
goto error;
}
memcpy(task->s, res->rows[0].values[0].val.str_val.s,task->len);
}
}
cpl_dbf.free_result(db_hdl, res);
55
return 1;
error:
if (res)
cpl_dbf.free_result(db_hdl, res);
task->s = 0;
task->len = 0;
return -1;
}
int get_user_activity(str *user, str *activity, const char *key){
db_key_t keys_cmp[1];
db_key_t keys_ret[1];
db_val_t vals[1];
db_res_t *res = 0;
keys_cmp[0]="user";
keys_ret[0]=key;
DBG("DEBUG:get_user_activity: fetching activity for user <%s>\n",user-
>s);
vals[0].type = DB_STRING;
vals[0].nul = 0;
vals[0].val.string_val = user->s;
if (cpl_dbf.query(db_hdl, keys_cmp, 0, vals, keys_ret, 1, 1, NULL, &res)<
0){
LOG(L_ERR,"ERROR:cpl-c:get_user_activity: db_query failed\n");
goto error;
}
if (res->n==0) {
DBG("DEBUG:get_user_activity: user <%.*s> not found in db -> probably "
"he has no activity\n",user->len, user->s);
activity->s = 0;
activity->len = 0;
} else {
if (res->rows[0].values[0].nul) {
DBG("DEBUG:get_user_activity: user <%.*s> has a NULL activity\n",
user->len, user->s);
activity->s = 0;
activity->len = 0;
} else {
DBG("DEBUG:get_user_activity: we got the activity=%s\n",res-
>rows[0].values[0].val.str_val.s);
activity->len = strlen(res->rows[0].values[0].val.str_val.s);
activity->s = shm_malloc( activity->len );
if (!activity->s) {
LOG(L_ERR,"ERROR:cpl-c:get_user_activity: no free sh_mem\n");
goto error;
}
memcpy(activity->s, res->rows[0].values[0].val.str_val.s,activity-
>len);
}
}
cpl_dbf.free_result(db_hdl, res);
return 1;
error:
if (res)
cpl_dbf.free_result(db_hdl, res);
activity->s = 0;
activity->len = 0;
return -1;
}
56
Appendix CAppendix CAppendix CAppendix C
CPLCPLCPLCPL----C module extensionsC module extensionsC module extensionsC module extensions
<cpl_switches.h> We present only changes that we made in the file. #include "/usr/include/cntx.h"
#include "context_db.h"
#define DB_URL "mysql://ser:[email protected]/ser"
#define DB_TABLE "context"
static inline char *run_context_switch( struct cpl_interpreter *intr )
{
char *p;
char *kid;
char *not_present_node;
unsigned short attr_name;
int nr_attr;
int i,j;
str owner={'\0',0};
str location = {'\0',0};
str task = {'\0',0};
str activity = {'\0',0};
str cpl_val = {'\0',0};
not_present_node = 0;
p=ATTR_PTR(intr->ip);
get_basic_attr( p, attr_name, owner.len, intr, script_error);
if (attr_name!=OWNER_ATTR) {
LOG(L_ERR,"ERROR:cpl_c:run_context_switch: unknown param type
(%d) for CONTEXT_SWITCH node\n",*p);
goto script_error;
}
get_str_attr( p, owner.s, owner.len, intr, script_error,0);
DBG("DEBUG:cpl-c:run_context_switch_node: owner is"
" [%.*s]\n",owner.len,owner.s);
for( i=0 ; i<NR_OF_KIDS(intr->ip) ; i++ ) {
kid = intr->ip + KID_OFFSET(intr->ip,i);
check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr,
script_error);
switch ( NODE_TYPE(kid) ) {
case NOT_PRESENT_NODE:
if (not_present_node) {
LOG(L_ERR,"ERROR:run_context_switch:
NOT_PRESENT node "
"found -> skipping!\n");
goto script_error;
}
not_present_node = kid;
break;
case OTHERWISE_NODE :
if (i!=NR_OF_KIDS(intr->ip)-1) {
57
LOG(L_ERR,"ERROR:run_context_switch: OTHERWISE
node "
"not found as the last sub-node!\n");
goto script_error;
}
DBG("DEBUG:run_context_switch: matching on
OTHERWISE node\n");
return get_first_child(kid);
case CONTEXT_NODE :
/* check the number of attributes */
nr_attr = NR_OF_ATTR(kid);
if (nr_attr<1 || nr_attr>3) {
LOG(L_ERR,"ERROR:run_context_switch: incorrect
nr of attrs "
"(%d) in CONTEXT node (1,2,or
3)\n",NR_OF_ATTR(kid));
goto script_error;
}
/* get the attributes */
p = ATTR_PTR(kid);
location.s = task.s = activity.s = 0;
location.len = task.len = activity.len = 0;
for(j=0;j<nr_attr;j++) {
get_basic_attr( p, attr_name, cpl_val.len, intr,
script_error);
if (attr_name!=LOCATION_ATTR &&
attr_name!=TASK_ATTR &&
attr_name!=ACTIVITY_ATTR)
{
LOG(L_ERR,"ERROR:run_context_switch:
unknown attribute "
"(%d) in CONTEXT node\n",attr_name);
goto script_error;
}
get_str_attr( p, cpl_val.s,
cpl_val.len, intr, script_error,0);
if (attr_name==LOCATION_ATTR ) {
location = cpl_val;
DBG("DEBUG:cpl-c:run_context_node:
location is"
"
[%.*s]\n",cpl_val.len,cpl_val.s);
} else if (attr_name==TASK_ATTR) {
task = cpl_val;
DBG("DEBUG:cpl-c:run_context_node: task"
" is
[%.*s]\n",cpl_val.len,cpl_val.s);
} else if (attr_name==ACTIVITY_ATTR) {
activity = cpl_val;
DBG("DEBUG:cpl-c:run_context_node:
activity"
" is
[%.*s]\n",cpl_val.len,cpl_val.s);
} else {
LOG(L_ERR,"ERROR:run_context_switch:
unknown attribute"
" (%d) in CONTEXT node\n",attr_name);
goto script_error;
58
}
} /* end of for loop */
DBG("Before invoke class.....\n");
printMsg();
if (context_db_bind(DB_URL)==-1) {
LOG(L_ERR, "ERROR:run_context_switch: cannot
bind to database module!");
}
if (context_db_init(DB_URL, DB_TABLE)==-1) {
LOG(L_ERR, "ERROR:run_context_switch: cannot
establish connection to db!");
}
Context* context=(Context*)malloc(sizeof(Context));
context->cntx_owner=owner;
if ((location.len!=0) && get_user_location(&owner,
&(context->cntx_loc), "location")==-1) {
LOG(L_ERR, "ERROR:run_context_switch: unknown db
location parameter\n");
goto script_error;
}
if ((task.len!=0) && get_user_task(&owner,
&(context->cntx_task), "task")==-1) {
LOG(L_ERR, "ERROR:run_context_switch: unknown db
task parameter\n");
goto script_error;
}
if ((activity.len!=0) && get_user_activity(&owner,
&(context->cntx_activ), "activity")==-1) {
LOG(L_ERR, "ERROR:run_context_switch: unknown db
activity parameter\n");
goto script_error;
}
/* does the value from script match the one from ontology? */
if (owner.len!=0 || location.len!=0 || task.len!=0
|| activity.len!=0){
DBG("All parameters not null\n");
}
if (((owner.len==context->cntx_owner.len) &&
strncasecmp(owner.s, context->cntx_owner.s, context->cntx_owner.len)==0) &&
(((location.len-1==context->cntx_loc.len) &&
strncasecmp(location.s, context->cntx_loc.s, context->cntx_loc.len)==0) ||
(((task.len-1==context->cntx_task.len) &&
strncasecmp(task.s, context->cntx_task.s, context->cntx_task.len)==0) &&
((activity.len-1==context->cntx_activ.len)
&& strncasecmp(activity.s, context->cntx_activ.s, context-
>cntx_activ.len)==0)))) {
DBG("DEBUG:run_context_switch: "
"matching on CONTEXT node (owner,
location)\n");
return get_first_child(kid);
} else {
DBG("WARNING:run_context_switch: "
"owner or location operator do not match\n");
LOG(L_WARN,"WARNING:run_context_node:
context owner=[%.*s] context location=[%.*s]\n",context->cntx_owner.len,
context->cntx_owner.s, context->cntx_loc.len, context->cntx_loc.s);
break;
}
break;
default:
59
LOG(L_ERR,"ERROR:cpl_c:run_context_switch: unknown
output node type (%d) for CONTEXT_SWITCH node\n", NODE_TYPE(kid));
goto script_error;
} /* end switch for NODE type */
} /* end for for all kids */
return DEFAULT_ACTION;
runtime_error:
return CPL_RUNTIME_ERROR;
script_error:
return CPL_SCRIPT_ERROR;
}
<cpl_parser.c> Only these two functions were added to the existing file.
static inline int encode_context_switch_attr(xmlNodePtr node, char
*node_ptr, char *buf_end)
{
xmlAttrPtr attr;
char *p, *p_orig;
unsigned char *nr_attr;
str val;
nr_attr = &(NR_OF_ATTR(node_ptr));
*nr_attr = 0;
p = p_orig = ATTR_PTR(node_ptr);
FOR_ALL_ATTR(node,attr) {
(*nr_attr)++;
/* there is only one attribute -> OWNER_ATTR */
if (attr->name[0]!='O' && attr->name[0]!='o') {
LOG(L_ERR,"ERROR:cpl_c:encode_context_switch_attr:
unknown "
"attribute <%s>\n",attr->name);
goto error;
}
set_attr_type(p, OWNER_ATTR, buf_end, error);
/* get the value of the attribute */
get_attr_val( attr->name , val, error);
val.len++; /* grab also the \0 */
append_str_attr(p,val, buf_end, error);
}
return p-p_orig;
error:
return -1;
}
static inline int encode_context_attr(xmlNodePtr node, char *node_ptr,
char *buf_end)
{
xmlAttrPtr attr;
char *p, *p_orig;
unsigned char *nr_attr;
str val;
nr_attr = &(NR_OF_ATTR(node_ptr));
*nr_attr = 0;
60
p = p_orig = ATTR_PTR(node_ptr);
FOR_ALL_ATTR(node,attr) {
(*nr_attr)++;
switch(attr->name[0]) {
case 'L': case 'l':
set_attr_type(p, LOCATION_ATTR, buf_end, error);
break;
case 'T': case 't':
set_attr_type(p, TASK_ATTR, buf_end, error);
break;
case 'A': case 'a':
set_attr_type(p, ACTIVITY_ATTR, buf_end, error);
break;
default:
LOG(L_ERR,"ERROR:cpl_c:encode_context_attr: unknown
"
"attribute <%s>\n",attr->name);
goto error;
}
/* attribute's encoded value */
get_attr_val( attr->name , val, error);
val.len++; /* grab also the \0 */
append_str_attr(p,val, buf_end, error);
}
return p-p_orig;
error:
return -1;
}
Appendix DAppendix DAppendix DAppendix D
Measurements programMeasurements programMeasurements programMeasurements program souce code souce code souce code souce code
<SIPclient.java> package sip;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Properties;
import java.util.Random;
import org.apache.commons.betwixt.io.id.*;
import poisson.EventGenerator;
import javax.sip.*;
import javax.sip.address.*;
import javax.sip.header.*;
import javax.sip.message.*;
public class SIPclient implements SipListener{
private SipProvider sipProvider;
61
private static SipProvider tcpProvider;
private static SipProvider udpProvider;
private static MessageFactory messageFactory;
private static AddressFactory addressFactory;
private static HeaderFactory headerFactory;
private static SipStack sipStack;
private static final String myAddress="130.237.15.213";
private static String transport="udp";
protected ClientTransaction inviteTid;
private int state=-1;
public static final int INITIAL = 0;
public static final int REGISTERED = 1;
public static final int SESSION_ESTABLISHED = 2;
private static String fromName;
private static String fromSipAddress;
private static String fromDisplayName;
private static String toUser;
private static String toSipAddress;
private static int proxyPort;
private static String proxyAddress;
private static String proxyURI;
private Connection connection;
private String[] users=new String[100];
private String[] addresses=new String[100];
private String[] passwords=new String[100];
private String domain="ccsser1.wireless.kth.se";
public SIPclient() {
this.proxyPort=5060;
this.proxyAddress="ccsser1.wireless.kth.se";
this.proxyURI=proxyAddress + ":" + proxyPort;
}
public void init() {
state=SIPclient.INITIAL;
SipFactory sipFactory=null;
sipFactory=SipFactory.getInstance();
sipFactory.setPathName("gov.nist");
Properties properties=new Properties();
properties.setProperty("javax.sip.IP_ADDRESS", myAddress);
properties.setProperty("javax.sip.STACK_NAME", "SIPclient");
//properties.setProperty("javax.sip.OUTBOUND_PROXY",
"192.168.0.1" + ":" + proxyPort + "/" + transport);
properties.setProperty("javax.sip.RETRANSMISSION_FILTER",
"on");
try {
sipStack=sipFactory.createSipStack(properties);
System.out.println("sipStack = " + sipStack);
} catch (SipException e) {
e.printStackTrace();
System.err.println(e.getMessage());
if (e.getCause() != null)
e.getCause().printStackTrace();
System.exit(-1);
}
62
try {
headerFactory=sipFactory.createHeaderFactory();
addressFactory=sipFactory.createAddressFactory();
messageFactory=sipFactory.createMessageFactory();
ListeningPoint
udpListeningPoint=sipStack.createListeningPoint(proxyPort, "udp");
ListeningPoint
tcpListeningPoint=sipStack.createListeningPoint(proxyPort, "tcp");
SIPclient listener=this;
udpProvider=sipStack.createSipProvider(udpListeningPoint);
System.out.println("udp provider " + udpProvider);
udpProvider.addSipListener(listener);
tcpProvider=sipStack.createSipProvider(tcpListeningPoint);
System.out.println("tcp provider " + tcpProvider);
tcpProvider.addSipListener(listener);
sipProvider = transport.equalsIgnoreCase("udp")?
udpProvider: tcpProvider;
registerUsers();
} catch (Exception e) {
e.printStackTrace();
System.err.println(e.getMessage());
}
}
private void connectToDB() {
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
System.out.println("Driver loaded!");
} catch (Exception e) {
System.err.println("Unable to find and load driver!");
System.err.println(e.getMessage());
}
try {
this.connection=DriverManager.getConnection("jdbc:mysql://ccsser1.wir
eless.kth.se:3306/ser","ser","puficat");
System.out.println("Connection to DB established
reference: " + connection);
} catch (Exception e) {
e.printStackTrace();
}
}
private void createUsers() {
PreparedStatement statement=null;
String phplib_id1, phplib_id2, phplib_id;
try {
connectToDB();
RandomIDGenerator gen=new RandomIDGenerator(true);
String num="";
int j=1;
int k;
for (int i=0; i<100; i++) {
if (i<9)
num="0" + "0" +j;
else if (i>=9 && i<99)
63
num="0" + j;
else
num="" + j;
++j;
users[i]="user_" + num;
passwords[i]="passwd_" + num;
addresses[i]=users[i] + "@" + domain;
phplib_id1=gen.nextId();
phplib_id2=gen.nextId();
phplib_id=phplib_id1+phplib_id2;
String query="INSERT INTO subscriber(phplib_id,
username, domain, password, email_address) VALUES('" + phplib_id + "','" +
users[i] + "','" + domain + "','" + passwords[i] + "','" + addresses[i] +
"');";
System.out.println(query);
statement=connection.prepareStatement(query);
k=statement.executeUpdate();
}
} catch (SQLException e) {
e.printStackTrace();
displaySQLErrors(e);
}
}
private void displaySQLErrors(SQLException e) {
System.out.println("SQL exception: " + e.getMessage());
System.out.println("SQL state: " + e.getSQLState());
System.out.println("Vendor error: " + e.getErrorCode());
}
private void registerUsers() {
String num="";
int j=1;
for (int i=0; i<100; i++) {
if (i<9)
num="0" + "0" +j;
else if (i>=9 && i<99)
num="0" + j;
else
num="" + j;
++j;
users[i]="user_" + num;
passwords[i]="passwd_" + num;
addresses[i]=domain;
Request register=createRegister(users[i], addresses[i],
1200);
try {
inviteTid=sipProvider.getNewClientTransaction(register);
inviteTid.sendRequest();
} catch (TransactionUnavailableException e) {
e.printStackTrace();
} catch (SipException e) {
e.printStackTrace();
}
}
}
private void generateCPLscripts() {
for (int i=0;i<100;i++) {
CPL cpl=new CPL(users[i], passwords[i], addresses[i]);
64
//cpl.createAddressCPLscript();
//cpl.uploadCPLscript("address");
//cpl.createContextCPLscript();
cpl.uploadCPLscript("context");
//Ontology ontology=new Ontology(users[i]);
//ontology.createMeetingOntologyFile();
}
}
private void generateInviteMessages() {
String fromUser, fromSipAddress;
int len=users.length;
for (int j=0; j<20; j++) {
for (int i=len-1; i>0; i--) {
fromUser=users[i];
fromSipAddress=addresses[i];
Request invite=createInvite(fromUser,
fromSipAddress, users[len-i], addresses[len-i]);
try {
inviteTid=sipProvider.getNewClientTransaction(invite);
inviteTid.sendRequest();
} catch (TransactionUnavailableException e) {
e.printStackTrace();
} catch (SipException e) {
e.printStackTrace();
}
}
}
}
/* Generating INVITE messages according to Poisson's distribution */
public void generateInviteMessages (double lambda) {
EventGenerator gen=new EventGenerator(lambda);
double currentTime=0;
int n=0;
String fromUser, fromSipAddress;
int len=users.length;
for (int j=0; j<20; j++) {
for (int i=len-1; i>0; i--) {
fromUser=users[i];
fromSipAddress=addresses[i];
Request invite=createInvite(fromUser, fromSipAddress,
users[len-i], addresses[len-i]);
try {
inviteTid=sipProvider.getNewClientTransaction(invite);
double
interArrival=gen.generateNextInterArrivalTime();
currentTime=currentTime+interArrival;
System.out.println(++n + ". arrival time is " +
(float)(interArrival*1000) + " milliseconds");
try {
Thread.currentThread().sleep((long)interArrival);
} catch (InterruptedException e) {
e.printStackTrace();
}
inviteTid.sendRequest();
} catch (TransactionUnavailableException e) {
e.printStackTrace();
65
} catch (SipException e) {
e.printStackTrace();
}
}
}
}
public Request createRegister(String user, String address, int
timeToExpire) {
Request register=null;
try {
//create From header
FromHeader fromHeader=createFromHeader(user, address,
user);
//create To header
ToHeader toHeader=createToHeader(user, address);
//create request URI
URI requestURI=addressFactory.createURI("sip:" +
proxyAddress);
//create Via headers
ArrayList viaHeaders=new ArrayList();
ViaHeader
viaHeader=headerFactory.createViaHeader(sipStack.getIPAddress(),
sipProvider.getListeningPoint().getPort(), transport, null);
//add via headers
viaHeaders.add(viaHeader);
//create a new Call-ID header
CallIdHeader callId=sipProvider.getNewCallId();
//create a new CSeq header
CSeqHeader cSeqHeader=headerFactory.createCSeqHeader(1,
Request.REGISTER);
//create a new MaxForwards header
MaxForwardsHeader
maxForwards=headerFactory.createMaxForwardsHeader(70);
//create register request
register=messageFactory.createRequest(requestURI,
Request.REGISTER, callId, cSeqHeader, fromHeader, toHeader, viaHeaders,
maxForwards);
//create Contact headers
String host=sipStack.getIPAddress();
//create contact name address
SipURI contactURI=addressFactory.createSipURI(fromName,
host);
contactURI.setPort(sipProvider.getListeningPoint().getPort());
//add the contact address
Address
contactAddress=addressFactory.createAddress(contactURI);
contactAddress.setDisplayName(fromDisplayName);
ContactHeader
contactHeader=headerFactory.createContactHeader(contactAddress);
register.addHeader(contactHeader);
66
//create Expires header
ExpiresHeader
expires=headerFactory.createExpiresHeader(timeToExpire);
register.addHeader(expires);
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
System.exit(0);
}
return register;
}
public Request createInvite(String fromName, String fromSipAddress,
String toName, String toSipAddress) {
Request invite=null;
try {
fromDisplayName=fromName;
//create From header
FromHeader fromHeader=createFromHeader(fromName,
fromSipAddress, fromDisplayName);
//create To header
ToHeader toHeader=createToHeader(toName,
toSipAddress);
//create request URI
SipURI
requestURI=addressFactory.createSipURI(toName, proxyURI);
//create Via headers
ArrayList viaHeaders=new ArrayList();
ViaHeader
viaHeader=headerFactory.createViaHeader(sipStack.getIPAddress(),
sipProvider.getListeningPoint().getPort(), transport, null);
//add via headers
viaHeaders.add(viaHeader);
//create a new Call-ID header
CallIdHeader callId=sipProvider.getNewCallId();
//create a new CSeq header
CSeqHeader
cSeqHeader=headerFactory.createCSeqHeader(1, Request.INVITE);
//create a new MaxForwards header
MaxForwardsHeader
maxForwards=headerFactory.createMaxForwardsHeader(70);
//create invite request
invite=messageFactory.createRequest(requestURI,
Request.INVITE, callId, cSeqHeader, fromHeader, toHeader, viaHeaders,
maxForwards);
//create Contact headers
String host=sipStack.getIPAddress();
//create contact name address
SipURI
contactURI=addressFactory.createSipURI(fromName, host);
contactURI.setPort(sipProvider.getListeningPoint().getPort());
//add the contact address
67
Address
contactAddress=addressFactory.createAddress(contactURI);
contactAddress.setDisplayName(fromDisplayName);
ContactHeader
contactHeader=headerFactory.createContactHeader(contactAddress);
invite.addHeader(contactHeader);
//create Content type header
ContentTypeHeader
contentTypeHeader=headerFactory.createContentTypeHeader("application",
"sdp");
String sdpData="v=0\r\n"
+ "o=alice 15306100 15306115"
+ " IN IP4 " + this.sipStack.getIPAddress()
+"\r\n"
+ "s=SIP call\r\n"
+ "c=IN IP4 " + this.sipStack.getIPAddress() +
"\r\n"
+ "t=0 0\r\n"
+ "m=audio 8000 RTP/AVP 8 98 97 101\r\n"
+ "a=rtpmap:8 pcma/8000\r\n"
+ "a=rtpmap:98 iLBC/8000\r\n"
+ "a=rtpmap:97 speex/8000\r\n"
+ "a=rtpmap:101 telephone-event/8000\r\n"
+ "a=ftmp:101 0-15\r\n";
byte[] contents = sdpData.getBytes();
invite.setContent(contents, contentTypeHeader);
//create Expires header
ExpiresHeader
expires=headerFactory.createExpiresHeader(1800);
invite.addHeader(expires);
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
System.exit(0);
}
return invite;
}
private String createTag() {
long tag = -1;
Random random = new Random();
tag = (random.nextLong() + random.nextLong()) % 1000000L;
return String.valueOf(Math.abs(tag));
}
private FromHeader createFromHeader(String fromName, String
fromSipAddress, String fromDisplayName) {
FromHeader fromHeader=null;
SipURI fromAddress=null;
Address fromNameAddress=null;
try {
fromAddress=addressFactory.createSipURI(fromName,
fromSipAddress);
fromNameAddress=addressFactory.createAddress(fromAddress);
fromNameAddress.setDisplayName(fromDisplayName);
fromHeader=headerFactory.createFromHeader(fromNameAddress,
createTag());
68
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
System.exit(0);
}
return fromHeader;
}
private ToHeader createToHeader(String toUser, String toSipAddress) {
ToHeader toHeader=null;
SipURI toAddress=null;
Address toNameAddress=null;
try {
toAddress=addressFactory.createSipURI(toUser,
toSipAddress);
toNameAddress=addressFactory.createAddress(toAddress);
toHeader=headerFactory.createToHeader(toNameAddress,
null);
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
System.exit(0);
}
return toHeader;
}
public static void main(String[] args) {
SIPclient sip=new SIPclient();
//sip.createUsers();
sip.init();
//sip.generateCPLscripts();
double lambda=300;
sip.generateInviteMessages(lambda);
}
public void processRequest(RequestEvent requestReceivedEvent) {
Request request=requestReceivedEvent.getRequest();
ServerTransaction
serverTransactionId=requestReceivedEvent.getServerTransaction();
System.out.println("\n\nRequest " + request.getMethod() + "
received at " +
sipStack.getStackName() + " with server
transaction id " +
serverTransactionId);
if (request.getMethod().equals(Request.BYE))
processBye(request, serverTransactionId);
}
public void processBye(Request request, ServerTransaction st) {
try {
System.out.println("SIPclient: got a bye .");
if (st == null) {
System.out.println("SIPclient: null TID.");
return;
}
Dialog dialog=st.getDialog();
System.out.println("Dialog State = " +
dialog.getState());
Response response=messageFactory.createResponse(200,
request);
st.sendResponse(response);
69
System.out.println("SIPclient: Sending OK.");
System.out.println("Dialog State = " +
dialog.getState());
} catch (Exception e) {
e.printStackTrace();
System.err.println(e.getMessage());
System.exit(0);
}
}
public void processResponse(ResponseEvent responseReceivedEvent) {
System.out.println("Got a response");
Response
response=(Response)responseReceivedEvent.getResponse();
ClientTransaction
tid=responseReceivedEvent.getClientTransaction();
CSeqHeader
cSeq=(CSeqHeader)response.getHeader(CSeqHeader.NAME);
System.out.println("Response received : Status Code = " +
response.getStatusCode() + " " + cSeq);
if (tid == null) {
System.out.println("Stray response -- dropping ");
return;
}
System.out.println("transaction state is " + tid.getState());
String method=cSeq.getMethod();
int statusCode=response.getStatusCode();
try {
if (statusCode==Response.UNAUTHORIZED &&
state==SIPclient.INITIAL &&
method.equals(Request.REGISTER)) {
processUnauthorizedRegister(response);
}
if (statusCode==Response.OK && state==SIPclient.INITIAL
&&
method.equals(Request.REGISTER)) {
processOkRegister(response);
}
if (statusCode==Response.OK &&
method.equals(Request.INVITE)) {
processOkInvite(response, tid);
}
if (statusCode==Response.SERVER_INTERNAL_ERROR) {
processErrorResponse(response, tid);
}
if (statusCode==Response.MOVED_TEMPORARILY) {
ContactHeader
contactHeader=(ContactHeader)response.getHeader(ContactHeader.NAME);
System.out.println("Response received : Status Code
= " + response.getStatusCode() + " " + contactHeader);
String
redirectSipURIString=contactHeader.getAddress().toString();
System.out.println("Received MOVED_TEMPORARILY to "
+ redirectSipURIString);
}
if ( statusCode==603) {
//Ignore
70
}
} catch(Exception e) {
e.printStackTrace();
System.err.println(e.getMessage());
System.exit(0);
}
}
private void processOkRegister(Response response) {
this.state=SIPclient.REGISTERED;
FromHeader fromHeader=(FromHeader)response.getHeader("From");
String from=fromHeader.getTag();
ToHeader toHeader=(ToHeader)response.getHeader("To");
String to=toHeader.getTag();
System.out.println("Got OK after REGISTER user " + to);
}
private void processOkInvite(Response response, ClientTransaction
transactionId) {
state=SIPclient.SESSION_ESTABLISHED;
//Dialog dialog=transactionId.getDialog();
FromHeader fromHeader=(FromHeader)response.getHeader("From");
String from=fromHeader.getTag();
ToHeader toHeader=(ToHeader)response.getHeader("To");
String to=toHeader.getTag();
System.out.println("Got OK after INVITE from " + to);
}
public void processTimeout(TimeoutEvent timeout) {
System.out.println("Transaction time out");
System.exit(0);
}
private void processUnauthorizedRegister(Response response) {
System.out.println("Request unauthorized");
}
private void processErrorResponse(Response response,
ClientTransaction tid) {
System.out.println("Internal server error: " +
response.getReasonPhrase());
}
}
<Ontology.java> package sip;
import java.io.*;
public class Ontology {
private String user;
public Ontology(String user) {
this.user=user;
}
private String getXMLHeader() {
StringBuffer sb=new StringBuffer();
sb.append("<?xml version=\"1.0\"?>\n");
71
sb.append("<rdf:RDF
xmlns:owl=\"http://www.w3.org/2002/07/owl#\"\n");
sb.append("xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-
ns#\"\n");
sb.append("xmlns:rdfs=\"http://www.w3.org/2000/01/rdf-
schema#\"\n");
sb.append("xmlns:xsd=\"http://www.w3.org/2000/10/XMLSchema#\"\n");
sb.append("xmlns:acas=\"http://localhost/OWL/userContextOntology.owl#\">\n\
n");
return sb.toString();
}
private String getMeetingContext(String owner) {
StringBuffer sb=new StringBuffer();
sb.append("<acas:hasContext>\n");
sb.append("<acas:UserContext rdf:ID=\"" + owner + "Context\">\n");
sb.append("<acas:hasOwner
rdf:datatype=\"http://www.w3.org/2000/10/XMLSchema#string\">" + owner +
"</acas:hasOwner>\n");
sb.append("<acas:hasLocation
rdf:datatype=\"http://www.w3.org/2000/10/XMLSchema#string\">grimeton</acas:
hasLocation>\n");
sb.append("<acas:hasTask
rdf:datatype=\"http://www.w3.org/2000/10/XMLSchema#string\">meeting</acas:h
asTask>\n");
sb.append("<acas:hasActivity
rdf:datatype=\"http://www.w3.org/2000/10/XMLSchema#string\">presenting</aca
s:hasActivity>\n");
sb.append("</acas:UserContext>\n");
sb.append("</acas:hasContext>\n");
return sb.toString();
}
private String getMeetingAddress() {
StringBuffer sb=new StringBuffer();
sb.append("<acas:hasAddress>\n");
sb.append("<acas:UserAddress rdf:ID=\"" + user + "Address\">\n");
sb.append("<acas:hasHostName
rdf:datatype=\"http://www.w3.org/2000/10/XMLSchema#string\">ccsser1.wireles
s.kth.se</acas:hasHostName>\n");
sb.append("<acas:hasHostDomain
rdf:datatype=\"http://www.w3.org/2000/10/XMLSchema#string\">wireless.kth.se
</acas:hasHostDomain>\n");
sb.append("<acas:hasPhoneNumber
rdf:datatype=\"http://www.w3.org/2000/10/XMLSchema#string\">tel:+4673575955
6</acas:hasPhoneNumber>\n");
sb.append("<acas:hasPhoneDomain
rdf:datatype=\"http://www.w3.org/2000/10/XMLSchema#string\">1</acas:hasPhon
eDomain>\n");
sb.append("</acas:UserAddress>\n");
sb.append("</acas:hasAddress>\n");
return sb.toString();
}
private String createMeetingOntology() {
StringBuffer sb=new StringBuffer();
sb.append(getXMLHeader());
sb.append("<owl:Ontology rdf:about=\"\"/>\n\n");
sb.append("<acas:User rdf:ID=\"" + user + "\">\n");
72
sb.append(getMeetingContext(user));
sb.append(getMeetingAddress());
sb.append("</acas:User>\n");
sb.append("</rdf:RDF>\n");
return sb.toString();
}
private void createFile(String name, String ontology) {
try {
FileWriter fileWriter=new FileWriter(new File(name));
fileWriter.write(ontology);
fileWriter.flush();
fileWriter.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void createMeetingOntologyFile() {
String ontology=createMeetingOntology();
System.out.println(ontology);
createFile(user.substring(0,1).toUpperCase() + user.substring(1) +
"Meeting.owl", ontology);
}
}
<CPL.java> package sip;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import CPLEd.network.TransportCPL;
import CPLEd.owl.Inter;
import CPLEd.owl.Parser;
import CPLEd.owl.UserData;
import CPLEd.tree.CPLTree;
import CPLEd.util.Global;
public class CPL {
private String user;
private String passwd;
private String domain;
public CPL(String user, String passwd, String domain) {
this.user=user;
this.passwd=passwd;
this.domain=domain;
}
private String createContextCPL(String owner) {
StringBuffer sb=new StringBuffer();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
sb.append("<!DOCTYPE cpl SYSTEM
\"file:C:/Programs/CPLEd/context.dtd\">\n");
sb.append("<cpl>\n");
sb.append("<incoming>\n");
sb.append("<context-switch owner=\"" + owner +"\">\n");
73
sb.append("<context task=\"meeting\"
activity=\"presenting\">\n");
sb.append("<location url=\"sip:[email protected]\">\n");
sb.append("<redirect/>\n");
sb.append("</location>\n");
sb.append("</context>\n");
sb.append("</context-switch>\n");
sb.append("</incoming>\n");
sb.append("</cpl>\n");
return sb.toString();
}
private String createAddressCPL(){
StringBuffer sb=new StringBuffer();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
sb.append("<!DOCTYPE cpl SYSTEM
\"file:C:/Programs/CPLEd/context.dtd\">\n");
sb.append("<cpl>\n");
sb.append("<incoming>\n");
sb.append("<address-switch field=\"origin\"
subfield=\"host\">\n");
sb.append("<address subdomain-of=\"" + domain + "\">\n");
sb.append("<reject status=\"reject\" reason=\"I am on
vacation\"/>\n");
sb.append("</address>\n");
sb.append("</address-switch>\n");
sb.append("</incoming>\n");
sb.append("</cpl>\n");
return sb.toString();
}
private void createFile(String name, String cpl) {
try {
FileWriter fileWriter=new FileWriter(new File(name));
fileWriter.write(cpl);
fileWriter.flush();
fileWriter.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void createContextCPLscript() {
String cpl=createContextCPL(user);
System.out.println(cpl);
String fileName=user.substring(0,1).toUpperCase() +
user.substring(1) + "InMeeting.xml";
createFile(fileName, cpl);
}
public void createAddressCPLscript() {
String cpl=createAddressCPL();
System.out.println(cpl);
String fileName=user.substring(0,1).toUpperCase() +
user.substring(1) + "OnVacation.xml";
createFile(fileName, cpl);
}
public void uploadCPLscript(String scriptType) {
Global global=Global.getInstance();
String type="SIP";
74
String url="ccsser1.wireless.kth.se";
/* save the setting for the next time */
if ( !user.trim().equals("") )
global.setProp( "user", user);
if ( !url.trim().equals("") )
if ( type.toUpperCase().equals("SIP") )
global.setProp("SipURL", url );
else
global.setProp("HttpURL", url );
if ( !passwd.trim().equals("") )
global.setProp("passwd", passwd );
global.setBoolProp("IsApplet", true);
global.setProp("DTDlocation",
"file:C:/Programs/CPLEd/context.dtd");
global.setProp("SIPport", "5070");
global.setProp("HTTPpath", "./cpl/server.pl");
try {
global.setCPLTree(new CPLTree(global));
String pathToScript="C:/Programs/CPLEd/CPLscripts/";
if ("address".equals(scriptType))
pathToScript += user.substring(0,1).toUpperCase()
+ user.substring(1) + "OnVacation.xml";
else if ("context".equals(scriptType)) {
Inter inter=new Inter();
inter.userData=new UserData();
Parser parser=new Parser();
pathToScript = "C:/Programs/CPLEd/owl/" +
user.substring(0,1).toUpperCase() + user.substring(1);
File ontology = new File (pathToScript +
"Meeting.owl");
parser.parseOWL(ontology, inter.userData);
pathToScript=inter.determineScriptToUse(inter.userData);
System.out.println("Gotten path to script=" +
pathToScript);
inter.userData.setPathToScript(pathToScript);
}
global.getCPLTree().loadCPL(new
FileInputStream(pathToScript));
TransportCPL cpl_uploader=new TransportCPL(global,
"upload", type);
cpl_uploader.executeOperation();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
<EventGenerator.java>
package poisson;
import java.util.Random;
public class EventGenerator extends Random {
private double avgInterArrivalTime;
/* t=1/lambda */
public EventGenerator(double lambda) {
75
this.avgInterArrivalTime=1/lambda;
}
public double generateNextInterArrivalTime() {
double interArrival;
interArrival= -(Math.log(1-nextDouble()))*avgInterArrivalTime;
return interArrival;
}
}
Appendix EAppendix EAppendix EAppendix E
SER measurementsSER measurementsSER measurementsSER measurements tables tables tables tables
Measurement1 Measurement1 Measurement1 Measurement1
A. SER's response time for conventional CPL script with increasing complexity (number
of address-switches)
T(response) - T(invite)
1 address-switch
2 address-switches
3 address-switches
4 address-switches
5 address-switches
t1 0,003008 0,003075 0,003118 0,003531 0,003443
t2 0,003023 0,003096 0,003203 0,00315 0,003116
t3 0,003007 0,003084 0,003152 0,003253 0,003116
t4 0,002982 0,003028 0,003226 0,003121 0,003259
t5 0,003055 0,003039 0,003124 0,003082 0,003167
t6 0,003108 0,003161 0,003052 0,003184 0,003345
t7 0,00325 0,00302 0,00336 0,003155 0,00315
t8 0,003116 0,00317 0,003126 0,003059 0,003121
t9 0,003085 0,003233 0,003082 0,003198 0,003219
t10 0,003063 0,003103 0,002983 0,003119 0,003239
mean(t) 0,0030697 0,0031009 0,0031426 0,0031852 0,0032175
stdev(t) 7,76202E-05 6,87289E-05 0,000103567 0,000133987 0,000108687
B. SER's response time for context-based CPL script with increasing complexity (number
of context-switches)
T(response) - T(invite)
1 context-switch
2 context-switches
3 context-switches
4 context-switches
5 context-switches
t1 0,00953 0,007202 0,009929 0,013086 0,013186
t2 0,007267 0,007139 0,009635 0,011815 0,01322
t3 0,00723 0,007369 0,010429 0,011526 0,01313
t4 0,006001 0,009043 0,008844 0,011288 0,012663
t5 0,005937 0,007506 0,009414 0,012538 0,013526
t6 0,007309 0,007215 0,009458 0,011092 0,013908
t7 0,007437 0,007327 0,009499 0,009706 0,011681
t8 0,005345 0,007817 0,00981 0,009137 0,011568
t9 0,005278 0,007036 0,009803 0,011007 0,013553
t10 0,007793 0,005701 0,009902 0,0095 0,01304
76
mean(t) 0,0069127 0,0073355 0,0096723 0,0110695 0,0129475
stdev(t) 0,001299421 0,00081717 0,000415992 0,001295123 0,000773508
C. Comparison of different types of CPL scripts and their response times
T(response) - T(invite) 2 address-switches 2 context-switches 1 address 1 context
mean T 0,0031009 0,0073355 0,0041942
MeaMeaMeaMeasurement 2surement 2surement 2surement 2
D. Address load generator – generates INVITE messages all at once
Addressload1 file first 10 messages
T(INVITE) T(603status_code) T(response_time) mean T dev T
1 6,805427 6,809151 0,003724 0,0097437 0,005745759
2 6,8073 6,821485 0,014185
3 6,810175 6,820898 0,010723
4 6,811545 6,825105 0,01356
5 6,812922 6,828274 0,015352
6 6,814548 6,830228 0,01568
7 6,815919 6,830822 0,014903
8 6,83216 6,835092 0,002932
9 6,85477 6,857967 0,003197
10 6,882003 6,885184 0,003181
middle 10 messages
T(INVITE) T(603status_code) T(response_time) mean T dev T
1 14,670884 14,679415 0,008531 0,0225821 0,010345715
2 14,673254 14,689207 0,015953
3 14,67488 14,697985 0,023105
4 14,675877 14,689903 0,014026
5 14,677503 14,701344 0,023841
6 14,680128 14,693992 0,013864
7 14,681752 14,710155 0,028403
8 14,683624 14,725659 0,042035
9 14,684875 14,705865 0,02099
10 14,686749 14,721822 0,035073
last 10 messages
T(INVITE) T(603status_code) T(response_time) mean T dev T
1 21,777928 21,828863 0,050935 0,0584522 0,008619704
2 21,77955 21,820336 0,040786
3 21,780298 21,837229 0,056931
4 21,781049 21,833114 0,052065
5 21,781798 21,841256 0,059458
6 21,7843 21,845404 0,061104
77
7 21,789671 21,850645 0,060974
8 21,793044 21,861158 0,068114
9 21,794679 21,86129 0,066611
10 21,796917 21,864461 0,067544
Addressload2 file first 10 messages
T(INVITE) T(603status_code) T(response_time) mean T dev T
1 8,126284 8,129825 0,003541 0,004866 0,002105734
2 8,127907 8,133619 0,005712
3 8,130902 8,138045 0,007143
4 8,1344 8,14394 0,00954
5 8,140023 8,144722 0,004699
6 8,175505 8,180472 0,004967
7 8,197238 8,200468 0,00323
8 8,203109 8,206268 0,003159
9 8,207231 8,210772 0,003541
10 8,214478 8,217606 0,003128
middle 10 messages
T(INVITE) T(603status_code) T(response_time) mean T dev T
1 15,106155 15,148273 0,042118 0,0288689 0,017362173
2 15,112776 15,152671 0,039895
3 15,117026 15,168779 0,051753
4 15,12077 15,161467 0,040697
5 15,124518 15,169141 0,044623
6 15,14263 15,169259 0,026629
7 15,150753 15,173255 0,022502
8 15,211963 15,219283 0,00732
9 15,215714 15,222649 0,006935
10 15,234327 15,240544 0,006217
last 10 messages
T(INVITE) T(603status_code) T(response_time) mean T dev T
1 22,46517 22,512469 0,047299 0,0589492 0,008134352
2 22,466296 22,516504 0,050208
3 22,467045 22,525094 0,058049
4 22,467792 22,529064 0,061272
5 22,468417 22,520686 0,052269
6 22,469167 22,521225 0,052058
7 22,469793 22,533064 0,063271
8 22,471168 22,540132 0,068964
9 22,47179 22,540285 0,068495
10 22,472793 22,5404 0,067607
Addressload3 file first 10 messages
78
T(INVITE) T(603 status_code) T(response_time) mean T dev T
1 7,071804 7,075359 0,003555 0,0051055 0,002653508
2 7,074181 7,081482 0,007301
3 7,077427 7,089151 0,011724
4 7,079803 7,085495 0,005692
5 7,085425 7,089907 0,004482
6 7,109661 7,113869 0,004208
7 7,117278 7,12071 0,003432
8 7,119653 7,123785 0,004132
9 7,166256 7,169149 0,002893
10 7,185865 7,189501 0,003636
middle 10 messages
T(INVITE) T(603status_code) T(response_time) mean T dev T
1 13,487899 13,533669 0,04577 0,0547498 0,006737354
2 13,490772 13,541225 0,050453
3 13,492896 13,54963 0,056734
4 13,497766 13,55897 0,061204
5 13,500516 13,54542 0,044904
6 13,50314 13,55483 0,05169
7 13,507139 13,563034 0,055895
8 13,510133 13,567034 0,056901
9 13,512385 13,579218 0,066833
10 13,514008 13,571122 0,057114
last 10 messages
T(INVITE) T(603status_code) T(response_time) mean T dev T
1 20,00068 20,081292 0,080612 0,0463133 0,037456485
2 20,002553 20,084977 0,082424
3 20,004302 20,088457 0,084155
4 20,005677 20,097389 0,091712
5 20,044535 20,101101 0,056566
6 20,054147 20,097881 0,043734
7 20,125102 20,128871 0,003769
8 20,173947 20,179952 0,006005
9 20,217176 20,220505 0,003329
10 20,220423 20,23125 0,010827
E. Context load generator – generates INVITE messages all at once
Contextload1 file first 10 messages
T(INVITE) T(302status_code) T(response_time) mean T dev T
1 6,356302 6,371589 0,015287 0,015994 0,007005184
2 6,357924 6,376613 0,018689
3 6,360798 6,384061 0,023263
4 6,362169 6,386673 0,024504
5 6,363544 6,388297 0,024753
6 6,364920 6,383282 0,018362
7 6,399775 6,409685 0,009910
8 6,410890 6,416032 0,005142
79
9 6,451243 6,461219 0,009976
10 6,455864 6,465916 0,010052
middle 10 messages
T(INVITE) T(302status_code) T(response_time) mean T dev T
1 12,619485 12,626722 0,007237 0,013915 0,005668046
2 12,655963 12,663843 0,007880
3 12,731916 12,739345 0,007429
4 12,771770 12,779216 0,007446
5 12,779392 12,795 0,015608
6 12,782509 12,79872 0,016211
7 12,784262 12,798223 0,013961
8 12,813993 12,828003 0,014010
9 12,818738 12,837068 0,018330
10 12,821113 12,84547 0,024357
last 10 messages
T(INVITE) T(302status_code) T(response_time) mean T dev T
1 18,557244 18,723942 0,166698 0,078646 0,047364392
2 18,594725 18,73922 0,144495
3 18,595221 18,692723 0,097502
4 18,651187 18,752841 0,101654
5 18,693534 18,764297 0,070763
6 18,736258 18,776178 0,039920
7 18,745130 18,779839 0,034709
8 18,748877 18,78781 0,038933
9 18,750124 18,795211 0,045087
10 18,751625 18,798323 0,046698
Contextload2 file first 10 messages
T(INVITE) T(302status_code) T(response_time) mean T dev T
1 8,220442 8,238696 0,018254 0,0131933 0,003984237
2 8,222813 8,2405 0,017687
3 8,225438 8,239989 0,014551
4 8,226809 8,242263 0,015454
5 8,251795 8,260708 0,008913
6 8,257793 8,267302 0,009509
7 8,266662 8,277037 0,010375
8 8,272411 8,290418 0,018007
9 8,27503 8,285381 0,010351
10 8,277277 8,286109 0,008832
middle 10 messages
T(INVITE) T(302status_code) T(response_time) mean T dev T
1 14,279623 14,324705 0,045082 0,0623173 0,012320766
2 14,280745 14,333608 0,052863
3 14,281746 14,329544 0,047798
4 14,282997 14,342729 0,059732
80
5 14,28612 14,34667 0,06055
6 14,28812 14,369601 0,081481
7 14,290616 14,350843 0,060227
8 14,293116 14,373479 0,080363
9 14,294362 14,359981 0,065619
10 14,295859 14,365317 0,069458
last 10 messages
T(INVITE) T(302status_code) T(response_time) mean T dev T
1 20,011384 20,21051 0,199126 0,1666414 0,030465625
2 20,012753 20,20228 0,189527
3 20,014006 20,206484 0,192478
4 20,025874 20,234993 0,209119
5 20,063601 20,246845 0,183244
6 20,102327 20,239134 0,136807
7 20,111695 20,255555 0,14386
8 20,128063 20,271289 0,143226
9 20,134434 20,267351 0,132917
10 20,137307 20,273417 0,13611
Contextload3 file first 10 messages
T(INVITE) T(302status_code) T(response_time) mean T dev T
1 5,997771 6,014613 0,016842 0,0171952 0,009961039
2 6,000018 6,010453 0,010435
3 6,002517 6,031521 0,029004
4 6,004014 6,030945 0,026931
5 6,005516 6,025176 0,01966
6 6,009763 6,032073 0,02231
7 6,01151 6,041735 0,030225
8 6,033496 6,039295 0,005799
9 6,052985 6,059232 0,006247
10 6,065478 6,069977 0,004499
middle 10 messages
T(INVITE) T(302status_code) T(response_time) mean T dev T
1 12,00349 12,052642 0,049152 0,0681851 0,044942055
2 12,022477 12,083078 0,060601
3 12,044587 12,087156 0,042569
4 12,053955 12,091787 0,037832
5 12,057952 12,096429 0,038477
6 12,060579 12,105492 0,044913
7 12,064951 12,110983 0,046032
8 12,067323 12,22041 0,153087
9 12,068695 12,220227 0,151532
10 12,070197 12,127853 0,057656
last 10 messages
T(INVITE) T(302status_code) T(response_time) mean T dev T
81
1 18,054553 18,249852 0,195299 0,1747764 0,033277656
2 18,056926 18,260176 0,20325
3 18,058423 18,278567 0,220144
4 18,059801 18,286141 0,22634
5 18,109895 18,264369 0,154474
6 18,145873 18,294807 0,148934
7 18,159239 18,321857 0,162618
8 18,167106 18,300331 0,133225
9 18,172107 18,324837 0,15273
10 18,174603 18,325353 0,15075
F. Poisson address load generator
AddressPoisson �=1
first 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 7,360942 7,369241 0,008299 0,0046648 0,002476978
2 7,362438 7,372079 0,009641
3 7,365063 7,369893 0,00483
4 7,375432 7,37875 0,003318
5 7,390172 7,395867 0,005695
6 7,397918 7,400849 0,002931
7 7,409535 7,41262 0,003085
8 7,420654 7,423502 0,002848
9 7,431897 7,434942 0,003045
10 7,442265 7,445221 0,002956
middle 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 16,113233 16,116236 0,003003 0,0078075 0,00497793
2 16,15296 16,155894 0,002934
3 16,159079 16,161989 0,00291
4 16,162201 16,166121 0,00392
5 16,164951 16,171094 0,006143
6 16,182066 16,188689 0,006623
7 16,183568 16,192991 0,009423
8 16,185693 16,200808 0,015115
9 16,186811 16,201095 0,014284
10 16,19156 16,20528 0,01372
last 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 24,784325 24,815714 0,031389 0,0161649 0,011294601
2 24,785074 24,811368 0,026294
3 24,786202 24,815158 0,028956
4 24,788575 24,815281 0,026706
5 24,802692 24,81942 0,016728
82
6 24,838918 24,842436 0,003518
7 24,858407 24,861519 0,003112
8 24,865151 24,87595 0,010799
9 24,866524 24,876269 0,009745
10 24,867527 24,871929 0,004402
AddressPoisson �=10
first 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 6,950675 6,953913 0,003238 0,0040708 0,001192
2 6,953673 6,957552 0,003879
3 6,961796 6,965366 0,00357
4 6,970038 6,974805 0,004767
5 6,97341 6,978119 0,004709
6 7,000769 7,004031 0,003262
7 7,007891 7,014895 0,007004
8 7,010763 7,014443 0,00368
9 7,018882 7,021785 0,002903
10 7,0325 7,036196 0,003696
middle 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 13,891944 13,896031 0,004087 0,0078683 0,005911
2 13,926426 13,929493 0,003067
3 13,940788 13,950925 0,010137
4 13,953531 13,956511 0,00298
5 13,958779 13,962787 0,004008
6 13,960655 13,967024 0,006369
7 13,962524 13,966692 0,004168
8 13,983013 13,990497 0,007484
9 13,985386 14,002166 0,01678
10 13,986887 14,00649 0,019603
last 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 20,6106 20,667068 0,056468 0,0642757 0,007791
2 20,615348 20,671293 0,055945
3 20,617721 20,675564 0,057843
4 20,620222 20,679642 0,05942
5 20,622846 20,683692 0,060846
6 20,624342 20,687602 0,06326
7 20,627342 20,691674 0,064332
8 20,628713 20,703399 0,074686
9 20,630968 20,706561 0,075593
10 20,632338 20,706702 0,074364
83
AddressPoisson �=100
first 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 7,348002 7,35173 0,003728 0,005273 0,002094
2 7,349872 7,355766 0,005894
3 7,354617 7,360315 0,005698
4 7,360365 7,363263 0,002898
5 7,367983 7,372752 0,004769
6 7,375105 7,384123 0,009018
7 7,377979 7,385464 0,007485
8 7,379727 7,386635 0,006908
9 7,411585 7,415 0,003415
10 7,422951 7,425866 0,002915
middle 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 14,269405 14,276011 0,006606 0,009591 0,005346
2 14,272032 14,276731 0,004699
3 14,291398 14,298976 0,007578
4 14,294016 14,303829 0,009813
5 14,295517 14,307256 0,011739
6 14,298766 14,303979 0,005213
7 14,301387 14,311711 0,010324
8 14,31888 14,324524 0,005644
9 14,323876 14,346776 0,0229
10 14,331372 14,342762 0,01139
last 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 21,165083 21,20502 0,039937 0,050592 0,006911
2 21,165707 21,209116 0,043409
3 21,166461 21,213322 0,046861
4 21,168332 21,217711 0,049379
5 21,173708 21,221361 0,047653
6 21,176452 21,225078 0,048626
7 21,18095 21,233179 0,052229
8 21,183323 21,244515 0,061192
9 21,185197 21,244637 0,05944
10 21,187572 21,244763 0,057191
G. Poisson context load generator
84
ContextPoisson �=1
first 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 6,904915 6,919643 0,014728 0,0119767 0,007925519
2 6,906661 6,929636 0,022975
3 6,909661 6,932807 0,023146
4 6,911158 6,93019 0,019032
5 6,914033 6,92912 0,015087
6 6,954131 6,95979 0,005659
7 6,962128 6,965859 0,003731
8 6,968874 6,974856 0,005982
9 6,978368 6,981809 0,003441
10 7,001731 7,007717 0,005986
middle 10 messages
T(INVITE) T(302 status code) T(response time) mean T dev T
1 16,156032 16,199703 0,043671 0,033616 0,013675545
2 16,15803 16,200264 0,042234
3 16,166152 16,183754 0,017602
4 16,168648 16,208313 0,039665
5 16,170522 16,212685 0,042163
6 16,176644 16,229351 0,052707
7 16,186638 16,224838 0,0382
8 16,196008 16,225785 0,029777
9 16,206 16,226249 0,020249
10 16,251347 16,261239 0,009892
last 10 messages
T(INVITE) T(302 status code) T(response time) mean T dev T
1 25,34406 25,375921 0,031861 0,0300057 0,011706284
2 25,348685 25,381363 0,032678
3 25,358429 25,389431 0,031002
4 25,360178 25,394746 0,034568
5 25,361925 25,395459 0,033534
6 25,363177 25,418282 0,055105
7 25,369047 25,39877 0,029723
8 25,396403 25,419388 0,022985
9 25,402649 25,419764 0,017115
10 25,408524 25,42001 0,011486
ContextPoisson �=10
first 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
85
1 6,996488 7,019143 0,022655 0,0160448 0,009061345
2 6,99836 7,023886 0,025526
3 7,000858 7,011295 0,010437
4 7,002359 7,024446 0,022087
5 7,003981 7,032829 0,028848
6 7,010355 7,032224 0,021869
7 7,027715 7,03868 0,010965
8 7,065944 7,070167 0,004223
9 7,074563 7,07955 0,004987
10 7,087053 7,095904 0,008851
middle 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 14,511038 14,585038 0,074 0,0860387 0,012319204
2 14,513659 14,601913 0,088254
3 14,523779 14,596892 0,073113
4 14,525652 14,597445 0,071793
5 14,527276 14,610483 0,083207
6 14,528273 14,641411 0,113138
7 14,529148 14,614402 0,085254
8 14,534642 14,620246 0,085604
9 14,537395 14,628739 0,091344
10 14,538392 14,633072 0,09468
last 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 21,973491 22,022094 0,048603 0,0746117 0,043007534
2 21,988734 22,027511 0,038777
3 21,990734 22,031219 0,040485
4 21,992103 22,045687 0,053584
5 21,993354 22,035589 0,042235
6 21,994104 22,134096 0,139992
7 21,994982 22,051017 0,056035
8 21,99573 22,134345 0,138615
9 22,002726 22,060765 0,058039
10 22,004473 22,134225 0,129752
ContextPoisson �=100
first 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 7,499734 7,511579 0,011845 0,0082633 0,003482071
2 7,501856 7,514827 0,012971
3 7,506229 7,51563 0,009401
4 7,531966 7,53781 0,005844
5 7,544577 7,548732 0,004155
6 7,550826 7,556035 0,005209
7 7,556695 7,561164 0,004469
86
8 7,561693 7,57397 0,012277
9 7,565944 7,576441 0,010497
10 7,600422 7,606387 0,005965
middle 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 15,175057 15,272607 0,09755 0,0450156 0,023326399
2 15,214034 15,264472 0,050438
3 15,229525 15,281139 0,051614
4 15,23677 15,304553 0,067783
5 15,264752 15,289424 0,024672
6 15,268627 15,294059 0,025432
7 15,276874 15,299301 0,022427
8 15,278868 15,312891 0,034023
9 15,281995 15,316783 0,034788
10 15,283866 15,325295 0,041429
last 10 messages
T(INVITE) T(603 status code) T(response time) mean T dev T
1 22,604536 22,872031 0,267495 0,17268 0,051020037
2 22,648885 22,880815 0,23193
3 22,684986 22,895227 0,210241
4 22,719216 22,903469 0,184253
5 22,742951 22,914617 0,171666
6 22,773307 22,923635 0,150328
7 22,786296 22,934711 0,148415
8 22,808659 22,939497 0,130838
9 22,828772 22,950721 0,121949
10 22,850135 22,95982 0,109685