81
ZOOM Quality Management Suite 5.0 Developer API Guide AUTHOR ZOOM International VERSION QMS-50-API-3-EN DATE October 2012 ZOOM International Havlíčkovo náměstí 2 130 00 Prague 3 Czech Republic Phone:+420 222 554 111

ZOOM Quality Management Suite Documentation · . 2 Chapter1QMSuiteDeveloperAPIs. 2 TheCallRECWebAPI ... TERMINAL_SEP,JTAPI_CISCO_ CALLMANAGER_ID,JTAPI_CISCO_

  • Upload
    dodang

  • View
    216

  • Download
    1

Embed Size (px)

Citation preview

ZOOMQualityManagement Suite 5.0Developer API Guide

AUTHOR

ZOOM International

VERSION

QMS-50-API-3-EN

DATE

October 2012

ZOOM International

Havlíčkovo náměstí 2

130 00 Prague3

CzechRepublic

Phone:+420 222 554 111

For requesting technical support, downloading latest software and documentation,visit: http://portal.zoomint.com.

ZOOM International Support CenterGlobal phone: +420 222 554 112 | Americas phone: +1 (512) 553 4569E-mail: [email protected]

About ZOOM InternationalZOOM International provides interaction recording and quality management solutions forContact Center & Unified Communications environments focused on Cisco and Genesysplatforms. ZOOM QMSuite helps you to improve your contact center quality andperformance by offering multichannel interaction recording, screen capture, agentevaluation, live monitoring, speech analytics and workforce management integration.

ZOOM International has a diverse client base around the world including financialinstitutions, healthcare organizations, telecommunication providers and emergencyservices.

© 2012 ZOOM International. All rights reserved.

ZOOM International HQ EuropeHavlíčkovo náměstí 2130 00 Prague 3, Czech Republic

Phone: (+420) 222 554 111Email: [email protected]

ZOOM International North America761 Old Hickory Blvd, Suite 201 ,Brentwood TN 37027, USA

Phone: +1 615 435 3575Email: [email protected]

ZOOM International Middle EastBPM FZ LLC Dubai Media City, Building 8,Office 55, P.O.Box 214371, Dubai, UAE

Phone: +971 (50) 443 4881Email: [email protected]

ZOOM International Russia – CIS117218 Krzhyzhanovskogo str. 14 bld. 3 office 227,Moscow, Russia

Phone: +7 495 967 9079Email: [email protected]

ZOOM International Ukraine25, Petra Sahaidachnoho streetKyiv, Ukraine

Phone: +7 495 967 9079Email: [email protected]

Documentation issuesPlease contact [email protected] provide the name of the document in your communication.

Table of Contents

1 QM Suite Developer APIs 11.1 Requirements 1

2 The CallREC Web API 32.1 Log In Service 32.1.1 Service Pre-requisites 42.1.2 Service Invocation 42.1.3 Input Parameters 42.1.4 Output 42.2 Download Token Service 52.2.1 Service Pre-requisites 52.2.2 Service Invocation 52.2.3 Input Parameters 52.2.4 Output 62.2.5 Sequence Diagram 62.3 AudioData Service 72.3.1 Service Pre-requisites 72.3.2 Service Invocation 82.3.3 Input Parameters 82.3.4 Input Parameters 82.3.5 Output 102.4 Send Call File Service 112.4.1 Service Pre-requisites 112.4.2 Service Invocation 112.4.3 Input Parameters 122.4.4 Parameters for Download Token Service 122.4.5 Output 122.5 Send Log File Service 132.5.1 Service Pre-requisites 142.5.2 Service Invocation 142.5.3 Input Parameters 142.5.4 Output 152.6 Examples 162.6.1 Log In ToCallREC 162.6.2 Request a Media File with the AudioData Service 182.6.3 Request a Download Token 202.6.4 Request a Media File with the Send Call File Service 212.6.5 Request a Log File with the Send Log File Service 23

3 The Pause/Resume Web API 25

3.1 Basic Procedure 253.2 Detailed Steps 263.2.1 Log In 263.2.2 List Calls 273.2.3 Obtain Call Details 283.2.4 Pause or Resume Call/Screen Recording 293.2.5 LogOut 303.3 Known Issues and Limitations 30

4 The RMI API 314.1 Obtain access to the CallREC RMI API 314.2 Basic Core Objects 324.2.1 Call Object 324.2.2 Couple object 334.2.3 Stream object 334.2.4 Screen object 334.3 Initial Object States 334.3.1 CREATED 344.3.2 REMOVED 344.3.3 FINISHED 354.3.4 DESTROYED 354.4 API objects 354.5 Observer Connection 364.6 Receiving Events 384.7 Core-Generated Events 394.7.1 InfoEv 394.7.2 CallEv 404.7.3 CoupleEv 404.7.4 StreamEv 424.7.5 ScreenEv 434.7.6 ErrorEv 434.8 RMI Objects and Their Methods 444.8.1 IRMICall 444.8.2 IRMICouple 454.8.3 IRMIStream 474.8.4 IRMIScreen 474.8.5 IRMIExternalData 484.8.6 ExternalData 484.8.7 StreamInfo 484.9 Possible Object States 494.9.1 RecordStatus 494.9.2 ProblemStatus 49

4.9.3 CoupleStatus 504.9.4 CoupleTypeStatus 514.9.5 CommpresionType 514.9.6 StoreStatus 52

Appendix A Pause/Resume Web API cURL Examples 53A.1 Basic Procedure 53A.2 cURL Examples 54A.3 Log in to Pause/Resume API 54A.4 List of Calls in Progress 56A.4.1 No Filter - NoCalls in Progress 57A.4.2 No Filter - One Call in Progress 58A.4.3 Filter for Phone Number - One Call in Progress 58A.5 Get Call Details - Active Call 59A.6 Pause Selected Call 60A.7 Get Call Details - Paused Call 61A.8 Resume Selected Call 62A.9 Get Call Details - Resumed Call 62A.10 LogOut from Pause/Resume API 63

Appendix B Sample Java Web Client Source Code forPause/Resume API 65

Introduction

Document Purpose

This document describes the Application Programming Interfaces (APIs)provided by ZOOM QM Suite to enable customized system-levelintegration with third party applications.

Advanced QM Suite configuration is described in other documents - forexampleCallREC Administrator Guide.

Audience

This document is intended for system engineers, programmers, andadministrators responsible for integration of the ZOOM QMSuite with otherexisting third party applications.

Expected Knowledge

Readers of this document are expected to have the following skills orknowledge:

l Basic knowledge of the ZOOM CallREC system features andfunctionality

l Knowledge of a suitable programming language that can leverage theAPIs, e.g. Java

l Unix system administration skills

l Network administration skills

Typographical Conventions

Names of functions and buttons are in bold. For example:Upload.

File names, file paths, command parameters and scripts launched from thecommand line are in non-proportional font.

Referred documents are in italics. For example: see the document This is aDocument for more information.

Code is placed on a gray background and bordered

Hyperlinks are shown in blue and underlined: http://www.zoomint.com.

1 QM Suite Developer APIs

This document covers the followingAPIs:

TheCallREC Web API - includingmethods to retrieve recorded data filesand logs.

The Pause/ResumeWeb API - enabling third party applicationpause/resume control of both call and screen recording.

TheCallREC RMI API - a lower level API enabling deep integration withthird party Java applications.

1.1 Requirements

The following are required in order to successfully access and use theseAPIs:

l A licensed, functioning instance of ZOOMQualityManagement Suite5.0.x (specifically the CallREC call recording component must berunning)

l Administrator access toCallREC and ideally a number of test API useraccountswith at least the following permissions and filters:

l Call List (all APIs)

l Export,Display Video Calls (Send Call File Service,AudioData Service)

l Pause and Resume Calls (Pause and Resume API)

l Other Settings (Send Log File Service)

l User and group call selection filters defined appropriately in theWeb GUI for the API user accounts

l For the Web APIs: A web client library (which fully supports HTTPcookies) for your preferred programming language is necessary. Theexamples for the Pause/Resume API assume the use of a sample Javaweb client application, the full source of which is included in AppendixB.For initial testing of API functionality, a simple utility such as the opensource cURL application will suffice.

1

l For the RMI API: a Java Development Environment (JDE). A tutorial onJava RMI Applications is available from Oracle:http://docs.oracle.com/javase/tutorial/rmi/overview.html.

2 Chapter 1 QM Suite Developer APIs

2 The CallREC Web API

TheCallRECWeb API enables third party applications to:

l Specify (using search parameters) a single audio file for download(AudioData Service)

l Request one or more media files for streaming or download by thelogged in user (Send Call File Service)

l Request a server log file for download (Send Log File Service)

The API has the following basic features:

l Fully HTTP-based API for maximum compatibility and ease of use

l User authenticated sessions (with additional download token accessrequirement for call data) prevent unauthorized use of the API

This guide includes an Examples section, showingworking calls to theseservices both from a browser and the command line.

Chapter Topics: 2.1 Log In Service 32.2 Download Token Service 52.3 Audio Data Service 72.4 SendCall File Service 112.5 Send Log File Service 132.6 Examples 16

2.1 Log In Service

AllWeb API services require the API client to be authenticated withCallREC before issuing a request. Authentication is achieved using the LogIn Service.

See a working example.

3

2.1.1 Service Pre-requisites

None.

2.1.2 Service Invocation

AnHTTP POSTmethod must be addressed to the followingURL:

http://<server-url>/callrec/loginservlet

2.1.3 Input Parameters

The service requires two parameters:

l username

l password

These twoparameters should be sent with the following content-type:application/x-www-form-urlencoded.

l The username is sent in the form: loginname=<username>

l The password is sent in the form: password=<username>

2.1.4 Output

The service will return:

l AnHTTP status. This status can be:l 200 OK: always returned

l A body response in text/xmlwith details about the operation; forexample: Access is authorized.

l A Java session ID (JSESSIONID); for example:sessionid="E3D794FEE4638143C1C89C18F693EB6B".

This identifier will be returned both as a cookie and in the body of theresponse. It will be needed for all further API operations.

4 Chapter 2 The CallREC Web API

Important: This statuswill be sent in all cases, includingwhen the username andpassword are correct, incorrect, or when the request ismalformed.

2.2 Download Token Service

The Download Token Service creates a download token used for one-timerequests for restricted resources, like call files. A token holds all parametersrequired for obtaining the resource, and the following restrictions apply:

l The token can be used only once and is discarded after use.

l A download token's lifetime is limited by the user's session lifetime.

See a working example.

2.2.1 Service Pre-requisites

The service requires the JSESSIONID obtained in the Log In Service.

2.2.2 Service Invocation

A GETor POSTHTTP method must be addressed to the followingURL:

http://<server>/callrec/downloadtoken

2.2.3 Input Parameters

The service expects to receive:

l A JSESSIONID to identify the user in the form of:l A cookie

l An append to the URL immediately after the servlet name, asfollows: jsessionid=<sessionid>

Chapter 2 The CallREC Web API 5

l Search criteria used to identify audio files (Send Call File Service only)

l More than one criteria parameter can be used at the sametime

l Parameters are dependent on the service used afterwards(check the service description for more details)

l All parameters for the search criteria must be sent in theformat: application/x-www-form-urlencoded

2.2.4 Output

The service will return:

l AnHTTP statusl This status can be: 200 OK: always returned

l A body response including the download token if the operation wassuccessful

l The response will be sent as text/xml, and the tokenwill beincluded inside the tag<reply>

Important: This statuswill be sent in all cases, includingwhen the parameters arecorrect, incorrect, or when the request ismalformed.

2.2.5 Sequence Diagram

The following diagram illustrates the use of the Download Token Serviceduring an operation to download call media files using the Send Call FileService.

6 Chapter 2 The CallREC Web API

Figure 1: Web API Send Call File Service Sequence Diagram

2.3 Audio Data Service

The AudioData Service can be used to request a single audio data file fromthe server. The user needs to be logged in and authorized for the audio filerequested, however a download token is not required in order to retrievefiles.

See a working example.

2.3.1 Service Pre-requisites

The service requires:

Chapter 2 The CallREC Web API 7

l The JSESSIONID obtained from the Log In Service

l Search criteria for calls provided as input parameters

2.3.2 Service Invocation

AnHTTP POSTmethod must be addressed to the followingURL:

http://<server>/callrec/audiodata

2.3.3 Input Parameters

The service expects to receive:

l The previously obtained JSESSIONID to identify the user in the form of:

l A cookie

l An append to the URL immediately after the servlet name, asfollows: jsessionid=<sessionid>

2.3.4 Input Parameters

When requesting audio data, search criteria must be specified. TheAudio Data Service accepts one or more of the following searchparameters:

Parameter Name Description Format Notes

callingPartyName Name of theuser whoinitiated the call

String The String must be URL encoded (forexample: %20 for spaces).

calledPartyName Name of thedestinationuser

String The String must be URL encoded (forexample: %20 for spaces).

callingParty Phoneextension ofthe user that

Integer

8 Chapter 2 The CallREC Web API

Parameter Name Description Format Notes

initiated the call

calledParty Phoneextension ofthe destinationuser

Integer

coupleType The type ofcoupledepends on thekind of call; forexample anormal call or aconference call.

String Valid values are: NORMAL,CONFERENCE, RECONN, PARK,UNPARK, BARGE.

problemStatus Specifiesproblems withthe couple (ifany), such asnot having anyaudio streamsor no problemsfound.

String Valid values are: NO_PROBLEM, ALL_STREAM_DELETED, STILL_SAVING_STREAM, ONLY_ONE_STREAM,STREAM_IS_NULL, NO_STREAMS.

callingIpAddress IP address thatinitiated the call(source).

IP Address

calledIpAddress Destination IPaddress

IP Address

b_method Used forsynchronizationpurposes.

String Possible values are: TAPE, DISK_A,DISK_N, ARC.

b_location Used forsynchronizationpurposes.

String The path where the archive is stored.

dbId The value of thecolumn ID fromthe coupletable in thedatabase.

Integer

dbIdParentCall Value of thecallid column inthe databasecouple table.

Integer A call can be split into multiple copies. Toidentify them, all couples reference theparent call that they belong to.

Chapter 2 The CallREC Web API 9

Parameter Name Description Format Notes

startTime The start timeof the call.

yyyy-MM-ddHH:mm:ss.SSS

The String must be URL encoded (forexample: %20 for spaces).

stopTime The end time ofthe call.

yyyy-MM-ddHH:mm:ss.SSS

The String must be URL encoded (forexample: %20 for spaces).

timeLength Duration of thecall. Expressedin seconds.

Number(integer)

externalData External dataattached to acall such asuser ID or callmetadata.

String. Manyvalues can besent at once.The format is:

<external_key>$!$<external_value>

These values depend on the configuration.The most commons ones are: CALLED_STREAM_PAYLOAD, CALLED_URL,CALLING_STREAM_PAYLOAD,CALLING_URL, COUPLE_END_REASON, COUPLE_START_REASON,GROUP_ID, JTAPI_CALLED_TERMINAL_SEP, JTAPI_CALLING_TERMINAL_SEP, JTAPI_CISCO_CALLMANAGER_ID, JTAPI_CISCO_GLOBAL_CALL_ID, JTAPI_CISCO_ID, SPANLESS_CALLED_REC_ID,SPANLESS_CALLING_REC_ID,SPANLESS_REC_ID, SPANLESS_REC_INFO.

Table 1: AudioData Search Parameters

2.3.5 Output

The service will return:

l AnHTTP statusl This status can be: 200 OK: always returned

l A body responsel The response will be sent as text/xml, and will includedetails about the operation; for example: problem withconnection

l The actual media file returned as application/octet-stream, ifthe operation was successful

10 Chapter 2 The CallREC Web API

Important: This statuswill be sent in all cases, includingwhen the parameters arecorrect, incorrect, or when the request ismalformed.

The service expects the search criteria to identify a single audio file. Ifmultiple audio filesmeet the search criteria, then no audio file will bereturned, and the body response will return the list of couple IDs. Themaximum size of this list is 1000.

2.4 Send Call File Service

The Send Call File Service sends a media file(s) to the user. The user needsto be logged in and authorized for each file requested. All files are retrievedusing a Download Token. If the file is an unmixed .recd, mixingmust berequested before the file(s) can be sent. See the Encode recd file outputformat in this Service for details.

All requests are treated the same way, as there is noway to differentiatebetween a request received from amedia player or from a browser, due tothe wide spectrum of embedded and standalone players used (forexample:QuickTime,WindowsMedia Player, Advanced Player).

See a working example.

2.4.1 Service Pre-requisites

The service requires:

l The JSESSIONID obtained from the Log In Service.

l A token from the Download Token Service:l Provides an authorizationmechanism

l The token contains data associated with the search criteria

2.4.2 Service Invocation

AnHTTP POSTmethod must be addressed to the followingURL:

http://<server>/callrec/sendcallfile.mp3

Chapter 2 The CallREC Web API 11

2.4.3 Input Parameters

The service expects to receive:

l The download token, as follows: token=<token>

l The previously obtained JSESSIONID to identify the user in the form of:

l A cookie

l An append to the URL immediately after the servlet name, asfollows: jsessionid=<sessionid>

2.4.4 Parameters for Download Token Service

When requesting a token from the Download Token Service, search criteriamust be specified. The search criteria are dependent on the service that willbe invoked afterwards. In the case of the Send Call File Service, therelevant search parameters are:

l id_call[] - An array of couple IDs

l sid - Couple SID. If specified, id_call[] is ignored

l type - requested media type. 1 - call, 16 - video. This determines thepermissions required

l action - specifies the intended action. This determines thepermissions required:

l download - call file(s) will be sent to the client

l prepare - encode .recd file into videowith audio

l <any other value or nothing> - stream playback is assumed

2.4.5 Output

The response format depends on the combination of parameters passed tothe servlet.

l Audio streamAn audio stream is returned under the following conditions:

12 Chapter 2 The CallREC Web API

l sid is specified, or call_id[]with a single value

l type = 1

l action is not specified or not download

l Video streamA video stream is returned under the following conditions:

l sid is specified, or call_id[]with a single value

l type = 16

l action is not specified or not download

l Encode .recd fileEncodes and mixes a .recd file with the corresponding calls andreturns a new download token after the encoding is finished.

l sid is specified, or call_id[]with a single value

l type = 16

l action is prepare

l Single media fileA single media file is returned under these conditions:

l sid or call_id[] is specified

l action is download

l The media file is returned as an attachment in the format it hasbeen recorded in

l Multiple media filesMultiple media files are returned under these conditions:

l call_id[] containsmultiple couple IDs

l action is download

l The files are returned in a .zip archive with an enclosed text filedescribing its contents

Additionally, the Service will return an HTTP status, such as:

l 200: if the operation was successful

l 403: if the tokenwas non-existent or already used

l 302: if the session expired

2.5 Send Log File Service

Sends a log file specified by the parameter.

See a working example.

Chapter 2 The CallREC Web API 13

2.5.1 Service Pre-requisites

The service requires the JSESSIONID obtained in the Log In Service.

2.5.2 Service Invocation

AnHTTP GET or POST method must be addressed to the followingURL:

http://<server>:8080/callrec/sendlogfile

2.5.3 Input Parameters

The service expects to receive:

l The previously obtained JSESSIONID to identify the user in the form of:

l A cookie

l An append to the URL immediately after the servlet name, asfollows: jsessionid=<sessionid>

l The sendLogIdx log ID parameterl This integer parameter specifies the log file to download

l Sample log file IDs are shown in the following table (exact IDnumberswill vary based on the CallREC installation, number ofintegration and recordingmodules used, and so on):

Log File ID Log File Name

0 core.log

1 audit.log

2 rs_eth1.log

3 rts_jtapi.log

4 ds.log

14 Chapter 2 The CallREC Web API

Log File ID Log File Name

5 web.log

6 webadmin.log

7 genesys.log

8 screenrec.log

9 mixer.log

10 naming.log

11 msgs.log

12 configmanager.log

13 rmi.log

14 ipcc.log

15 ipccex.log

16 prerecording.log

17 rts_skinny_1.log

18 rts_sip_1.log

19 slr.log

20 instreamer.log

21 tools.log

22 migration.log

Table 2: Sample Log File IDs

Tip: Onemethod of discovering the log file IDs for a CallREC installation is byviewing the JavaScript log file links in theWeb GUI (Settings > Logs). Forexample, the link for core.log is: javascript:changeLog(0,'core.log'), where 0 is the log ID.

2.5.4 Output

The specified log file is returned in a .zip archive named in a similar mannerto the log file itself, for example: core_log.zip.

Chapter 2 The CallREC Web API 15

2.6 Examples

The following examples use both aWeb browser (in this case Firefox), andthe command line tool cURL, an open source tool available for all majoroperating systems.

Replace strings shown between angle brackets (for example: <server>,<username>, <password>) with appropriate values (suchas: http://callrec.company.com, admin, mypassword).

2.6.1 Log In To CallREC

With Firefox:

To log intoCallREC, point to this address:

http://<server>/callrec/loginservlet?loginname=<username>&password=<password>

The Service will reply with a response similar to the following:

<ok sessionid="BA803B97624F0880A4C58B78C733EB5B"number="1">Access is authorized.</ok>

Note that the reply includes a JSESSIONID value. It will be needed later forthe other services.

With cURL:

curl -v -b cookies.txt -c cookies.txt -X POST -d"loginname=<username>" -d "password=<password>" \-H"Content-Type: application/x-www-form-urlencoded"-H"Accept: text/plain"http://<server>/callrec/loginservlet

The service will reply with a JSESSIONID (present both in the XMLresponse and the created cookies.txt cookie file). This is required laterfor accessing the other services:

16 Chapter 2 The CallREC Web API

* About to connect() to myserver port 80 (#0)* Trying 10.0.0.1... connected* Connected to myserver (10.0.0.1) port 80 (#0)> POST /callrec/loginservlet HTTP/1.1> User-Agent: curl/7.21.0 (x86_64-pc-linux-gnu)libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4libidn/1.18> Host: myserver> Content-Type: application/x-www-form-urlencoded> Accept: text/plain> Content-Length: 33>< HTTP/1.1 200 OK< Server: Apache-Coyote/1.1* Added cookieJSESSIONID="E3D794FEE4638143C1C89C18F693EB6B" fordomain myserver, path /callrec, expire 0< Set-Cookie:JSESSIONID=E3D794FEE4638143C1C89C18F693EB6B;Path=/callrec< Transfer-Encoding: chunked< Date: Fri, 20 May 2011 10:10:18 GMT<<?xml version="1.0" encoding="UTF-8"?>

<ok sessionid="E3D794FEE4638143C1C89C18F693EB6B"number="1">Access is authorized.</ok>* Connection #0 to host myserver left intact* Closing connection #0

A cookies.txt file will be created with the following content:

# Netscape HTTP Cookie File# http://curl.haxx.se/rfc/cookie_spec.html# This file was generated by libcurl! Edit at yourown risk.

myserver FALSE /callrec FALSE 0JSESSIONID E3D794FEE4638143C1C89C18F693EB6B

See the description of the Log In Service for more details.

Chapter 2 The CallREC Web API 17

2.6.2 Request aMedia File with the Audio Data Service

The following sample search criteria will be used for this request:

l timeLength: 10 seconds

l callingParty: 5655

l problemStatus: NO_PROBLEM

l startTime: 2011-05-20 12:14:14.684

With Firefox:

Enter the followingURL into the browser, including the JSESSIONID valueobtained in the previous request and the above search criteria (the \characters represents line breaks that should be removed and thecomplete URL pasted in one line):

http://<server>/callrec/audiodata;jsessionid=<jsessionid>\?startTime=2011-05-20%2012:14:14.684&problemStatus=NO_PROBLEM\&callingParty=5655&timeLength=10

The server will respond by sending the media file, triggering the browser todisplay the file open/save dialog.

With cURL:

Use the following command, specifying the location of the cookies.txtcookie file obtained in the previous request together with the sample searchcriteria:

curl -v -b cookies.txt -c cookies.txt -X POST -d"startTime=2011-05-20%2012:14:14.684" -d \"problemStatus=NO_PROBLEM" -d "callingParty=5655" -d"timeLength=10" \-H"Content-Type: application/x-www-form-urlencoded"-H"Accept: application/octet-stream" \-o media.mp3 http://<server>/callrec/audiodata

The server will respond by sending the requested media file:

* About to connect() to myserver port 80 (#0)* Trying 10.0.0.1... % Total % Received %Xferd Average Speed Time Time Time

18 Chapter 2 The CallREC Web API

CurrentDload Upload

Total Spent Left Speed0 0 0 0 0 0 0 0 --:-

-:-- --:--:-- --:--:-- 0connected* Connected to myserver (10.0.0.1) port 80 (#0)> POST /callrec/audiodata HTTP/1.1> User-Agent: curl/7.21.0 (x86_64-pc-linux-gnu)libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4libidn/1.18> Host: myserver> Cookie:JSESSIONID=E3D794FEE4638143C1C89C18F693EB6B> Content-Type: application/x-www-form-urlencoded> Accept: application/octet-stream> Content-Length: 26>} [data not shown]

0 0 0 0 0 26 0 0 --:--:-- 0:01:01 --:--:-- 0< HTTP/1.1 200 OK< Server: Apache-Coyote/1.1< Content-Disposition: attachment;filename=1305886454684_5655_5682_29.mp3< Content-Type: application/octet-stream;;charset=UTF-8< Content-Length: 29936< Date: Fri, 20 May 2011 10:28:46 GMT<{ [data not shown]100 29936 100 29936 0 26 468 00:01:03 0:01:03 --:--:-- 7875* Connection #0 to host myserver left intact* Closing connection #0

If multiple audio filesmeet the search criteria specified, the server willrespond with an error similar to the following:

<?xml version="1.0" encoding="UTF-8"?><error sessionid="5DFF7181E7CF8C9B76F33418D2BBFFBA"number="5">Multiple results found.</error>

See the description of the AudioData Service for more details about validsearch parameters.

Chapter 2 The CallREC Web API 19

2.6.3 Request a Download Token

The download token is required before requesting a media file from theSend Call File Service.

For this example, the following search criteria will be specified:

l id_call[]: 1165, 1166

l type: 1

l action: download

With Firefox:

Enter the followingURL into the browser:

http://<server>/callrec/downloadtoken;jsessionid=<jsessionid>?id_call[]=1165,1166&type=1&action=download

The server will respond with a token similar to the following:

<reply>NWnMJ4iKhVLOY0sUlGDX</reply>

With cURL:

Use the following command:

curl -v -b cookies.txt -c cookies.txt -X POST -d"id_call[]=1165,1166" -d "type=1" -d"action=download" \-H"Content-Type: application/x-www-form-urlencoded"-H"Accept: text/xml"http://<server>/callrec/downloadtoken

The server will respond with a token similar to the following:

* About to connect() to myserver port 80 (#0)* Trying 10.0.0.1... connected* Connected to myserver (10.0.0.1) port 80 (#0)> POST /callrec/downloadtoken HTTP/1.1> User-Agent: curl/7.21.0 (x86_64-pc-linux-gnu)libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4

20 Chapter 2 The CallREC Web API

libidn/1.18> Host: myserver> Cookie:JSESSIONID=E3D794FEE4638143C1C89C18F693EB6B> Content-Type: application/x-www-form-urlencoded> Accept: text/xml> Content-Length: 92>< HTTP/1.1 200 OK< Server: Apache-Coyote/1.1< Transfer-Encoding: chunked< Date: Fri, 20 May 2011 10:27:30 GMT<<?xml version="1.0" encoding="UTF-8"?>

<reply>y1vqe7xvFffKA1R5xgS6</reply>* Connection #0 to host myserver left intact* Closing connection #0

See the description of the Download Token Service for more details.

2.6.4 Request aMedia File with the Send Call File Service

Now that we have the download token, the Send Call File Service can becalled:

With Firefox:

Enter the followingURL into the browser to invoke the Send Call FileService (substituting in the download token obtained):

http://<server>/callrec/sendcallfile.mp3;jsessionid=<jsessionid>?token=<download_token>

The server will respond by sending a zip containing the requested mediafiles, triggering the browser to display the file open/save dialog.

With cURL:

Invoke the Send Call Service with the following request:

curl -v -b cookies.txt -c cookies.txt -X POST -d"token=<download_token>" \

Chapter 2 The CallREC Web API 21

-H"Content-Type: application/x-www-form-urlencoded"-H"Accept: application/octet-stream" \-o file.zip http://<server>/callrec/sendcallfile.mp3

The server will respond with a zip containing the requested media files:

* About to connect() to myserver port 80 (#0)* Trying 10.0.0.1... % Total % Received %Xferd Average Speed Time Time TimeCurrent

Dload UploadTotal Spent Left Speed

0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0connected* Connected to myserver (10.0.0.1) port 80 (#0)> POST /callrec/sendcallfile.mp3 HTTP/1.1> User-Agent: curl/7.21.0 (x86_64-pc-linux-gnu)libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4libidn/1.18> Host: myserver> Cookie:JSESSIONID=8A355EC61B5D804447704B8CB64DF535> Content-Type: application/x-www-form-urlencoded> Accept: application/octet-stream> Content-Length: 26>} [data not shown]< HTTP/1.1 200 OK< Server: Apache-Coyote/1.1< Content-Disposition: attachment;filename=calldata.zip< Content-Type: application/zip;< Transfer-Encoding: chunked< Date: Fri, 20 May 2011 13:55:18 GMT<{ [data not shown]100 40521 0 40521 0 26 440k 289 --:--:-- --:--:-- --:--:-- 454k* Connection #0 to host myserver left intact* Closing connection #0

See the description of the Send Call File Service for more details aboutvalid search parameters.

22 Chapter 2 The CallREC Web API

2.6.5 Request a Log File with the Send Log File Service

With Firefox:

Enter the followingURL into the browser to download the audit log file, afterfirst logging in using the Log In Service step above:

http://<server>/callrec/sendlogfile?sendLogIdx=1

The server will respond by sending the log file in a zip, triggering the browserto display the file open/save dialog.

With cURL:

Use the following command, after first completing the Log In Service stepabove:

curl -v -b cookies.txt -c cookies.txt -X POST -d"sendLogIdx=1" -H"Content-Type: application/x-www-form-urlencoded" \-H"Accept: application/octet-stream" -o audit-

log.zip http://<server>/callrec/sendlogfile

The server will respond by sending the log file in a zip:

* About to connect() to myserver port 80 (#0)* Trying 10.0.0.1... % Total % Received %Xferd Average Speed Time Time Time Current

Dload UploadTotal Spent Left Speed

0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0connected* Connected to myserver (10.0.0.1) port 80 (#0)> POST /callrec/sendlogfile HTTP/1.1> User-Agent: curl/7.21.0 (x86_64-pc-linux-gnu)libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4libidn/1.18> Host: myserver> Cookie:JSESSIONID=8A355EC61B5D804447704B8CB64DF535

Chapter 2 The CallREC Web API 23

> Accept: */*> Content-Length: 12> Content-Type: application/x-www-form-urlencoded>} [data not shown]< HTTP/1.1 200 OK< Server: Apache-Coyote/1.1< Content-Disposition: attachment; filename=audit_log.zip< Content-Type: application/zip;< Transfer-Encoding: chunked< Date: Wed, 15 Jun 2011 13:46:56 GMT<{ [data not shown]100 301k 0 301k 0 12 644k 25 --:--:-- --:--:-- --:--:-- 666k* Connection #0 to host myserver left intact* Closing connection #0

See the description of the Send Log File Service for more details about logfile IDs.

24 Chapter 2 The CallREC Web API

3 The Pause/Resume Web API

The Pause/Resume API is a simple API to enable third party applicationpause control of call and screen recording. A simple usage scenario is a callcenter employing PC-based agent dashboards that include a call-holdfeature. A third party developer can leverage the Pause/Resume API toensure that both the call and the screen recording pause when the call-holdfeature is enabled, and that recording resumeswhen the call-hold feature isdisabled.

The Pause/Resume API has the following basic features:

l Fully HTTP (REST)-based API for maximum compatibility and ease ofuse.

l User authenticated sessions to prevent pause/resume control ofunauthorized calls.

l List calls in progresswith filter parameters to restrict results based onthe IP address or phone number and call direction.

Sample API calls using the cURL tool are displayed in Appendix A.

Chapter Topics: 3.1 Basic Procedure 253.2 Detailed Steps 263.3 Known Issues and Limitations 30

3.1 Basic Procedure

The following procedure outlines the steps required in order for anapplication to pause or resume call recording for a call currently in progress.

1. Log in via anHTTP POST request using a valid CallREC username andpassword.

2. Send anHTTP GET request (with optional filter parameters) to list callsin progress. Calls that the logged-in user has access to (permissions tosee call details of) will include a call details URI in the result list.

25

3. Send anHTTP GET request to one specific call details URI. The reply isan XML document that includes a pause/resume URI if the logged-inuser has appropriate permissions for this action.

4. Send an HTTP POST/PUT request to the pause/resume URI withpaused=true to pause the call/screen recording or pause=false toresume call/screen recording.

5. Log out, again via anHTTP POST request.

3.2 Detailed Steps

The following detailed steps assume the use of the sample Java web client;the code for which is in Appendix B. Alternatively, sample API calls usingthe cURL tool are displayed in Appendix A.

Youwill need to know the fully qualified name of your CallREC Server, forexample, http://myserver.mycompany.com – this will be referred toas the <server>.A test user login should also be used, with the credentials referred to hereas <username> and <password>.

3.2.1 Log In

To log in, issue a POST request to the followingURI:

<server>/callrec/loginAction.do

The request body should be form-encoded (application/x-www-form-urlencoded), with the following parameters:

login=<username>&password=<password>

A known issue requires the login request to include an arbitrary HTTPcookie (the contents of the cookie are not important). See the Java clientsource code for a code example.

26 Chapter 3 The Pause/Resume Web API

Important: The response of this POST request will not indicate the success or failure ofthe login.

3.2.2 List Calls

To list calls, issue a GET request to the followingURI:

<server>/callrec/restful/couples.xml

This request can be accompanied by the following additional searchparameters:

To filter by IP address, use parameters in the following form:ipAddress=<value>&ipAddressDirection=<direction>

l <value> specifies the actual IP address.

l <direction>may be one of the following: CALLING, CALLED orBOTH.

l ipAddressDirection is optional. If this ismissing, BOTH is assumed.

To filter by phone number, use parameters in the following form:phoneNumber=<value>&phoneNumberDirection=<direction>

l <value> specifies the phone number.

l <direction> is the same as above (same possible values, samedefault value).

These searchesmay be combined, but it is unnecessary since both filterswill return only one call at a time.

A 403 (Forbidden) response means the user is not logged in. Aresponse with status 200 (OK)will contain an xml document listing thecalls in progress that satisfy the search condition. Each call MAY contain aURI to its details in Atom format (if it does not, thismeans that the user doesnot have the permissions to see the details of this call), with the rel attributeset to self (the URI can be found by usingXPath:

/couples/couples/link[@rel='self']/@href).

Example:

A sample response XML document for the request URI (one call inprogress):

Chapter 3 The Pause/Resume Web API 27

http://<server>/callrec/restful/couples.xml

<couples> <couples id="24197" callId="24142">

<ns2:link type="application/xml"href="http://<server>/callrec/restful/couples.xml/24197"rel="self"/>

<from>5630</from> <paused>false</paused> <start>2011-03-21T12:51:55.364+01:00</start> <to>5509</to>

</couples> </couples>

3.2.3 Obtain Call Details

By following this call details URI (GET request), the user will receive an XMLdocument with the call details, whichMAY (if the user has the sufficientrights to the operation) contain an Atom URI to pause/resume. The URI canbe found by usingXPath:

/couple/link[@rel='pause/resume']/@href).

Example:

The XPath:

/couples/couples/link[@rel='self']/@href

...for this XML evaluates to:

http://<server>/callrec/restful/couples.xml/24197

A GET request to this URI results in a response that is wrapped by thefollowing XML: 

<couple id="24197" callId="24142"> <ns2:link type="application/xml"

28 Chapter 3 The Pause/Resume Web API

href="http://<server>/callrec/restful/couples.xml/24197"rel="self"/>

<ns2:link type="application/xml"href="http://<server>/callrec/restful/couples.xml/24197/pausedState"rel="pause/resume"/>

<from>5630</from> <paused>false</paused> <start>2011-03-21T12:51:55.364+01:00</start> <to>5509</to>

</couple>

3.2.4 Pause or ResumeCall/Screen Recording

To pause or resume call/screen recording, issue a POST or PUT request tothe given URI. The body should be ofmime type application/x-www-form-urlencoded and should be either paused=true orpaused=false, depending onwhether a pause or a resume is requested.The response may safely be ignored.

Example:

The XPath:

/couple/link[@rel='pause/resume']/@href

...for this XML evaluates to the URI:

http://<server>/callrec/restful/couples.xml/24197/pausedState

A GET request to this URI returns the current state (not paused). In thiscase the false string. A PUT request to this URI with the form-urlencoded request bodycontainingpaused=true, pauses this call. A PUT request containing theparameter paused=false resumes the call again.

<couples> <couples id="24197" callId="24142">

<ns2:link type="application/xml"href="http://<server>/callrec/restful/couples.xml/24197"rel="self"/>

<from>5630</from>

Chapter 3 The Pause/Resume Web API 29

<paused>true</paused> <start>2011-03-21T12:51:55.364+01:00</start> <to>5509</to>

</couples> </couples>

3.2.5 LogOut

Issue aGET request to the followingURI:

<server>/callrec/logoutprocess

3.3 Known Issues and Limitations

l An arbitrary (random) cookie must be included in the initial loginPOSTrequest, otherwise the login will not be successful.

l The response to the initial loginPOST request currently does notindicate if authentication was successful.

30 Chapter 3 The Pause/Resume Web API

4 The RMI API

The CallREC Remote Method Invocation Application ProgrammingInterface (RMI API) module enables custom applications to do thefollowing:

l Connect to the CallREC recording software

l Monitor activities and system core objects

l Change the settings

l Add custom information to calls

Core operationmonitoring is implemented bymeans of connecting anobserver to specific parts of the CallREC API that report core changesthrough these observers. It is possible to limit the number of generatedevents. The events are gradually dispatched in the order that theyoccurred. If you are unable to receive the events, theywill be collecteddirectly through the API.

To to viewwhich settings of certainmodules supported by the RMI API canbe changed, request a module interface from the API.

4.1 Obtain access to the CallREC RMI API

The RMI API communicates using the RMI protocol. The core provides theRMI a reference to anRMICallRecAPI object through the RMI Registry.The IP address and port of the RMI Registry depends on theCallRecAPI_RMI bind name setting. You can obtain the reference toRMICallRecAPIobject either by calling it directly (this option is deprecated but functional)

IRMICallRecAPI callRecAPI = (IRMICallRecAPI)Naming.lookup("//ip_address:port/CallRecAPI_RMI");

or using theRMICallRecAPILookUp class.

31

RMICallRecAPILookUp lookUp = new RMICallRecAPILookUp(ip_address, port);IMICallRecAPI callRecAPI = lookUp.getCallRecAPI();

After obtaining this reference, you can request CoreAPI object (usinggetCoreAPI()method), from which you can obtain one of three referencesto objects that generate the events. By registering observerswith theseobjects, you will subscribe to receiving these events. Youwill obtain themodule interface usinggetCallRecModule(String moduleName).

RMICallRecAPILookUp class enables connecting an object thatimplements the ICallRecAPIConnectionStatus interface tomonitor APIavailability status. Alongwith this object, an availability polling interval is set(the default value is 60 sec). The interface has twomethods –onLostConnection(), which is called on disconnection andonGetConnection(IRMICallRecAPI), which is called when the connectionis re-established.When the connection is re-established, it is necessary toreconnect all of the observers, as the IRMICallRecAPI object that wasreturned as a method parameter will be an entirely new object. When theobject reconnects, a method that depends on the connection state iscalled immediately. Further on, it is only called upon a change of state.

RMICallRecAPILookUp class offers one more option to verify APIavailability using isConnectionAlive()method that returns a true value ifthe connection is working and a false value if it is not.

4.2 Basic Core Objects

An object-oriented program contains different types of objects. AnObjectis a data structure that manages and safeguards data, and ensures that thedata is used properly. Each type of object corresponds to a specific kind ofdata to be managed.

4.2.1 Call Object

A CallObject contains all the information concerning one call. One call canbe composed ofmultiple individual partial calls (Couple objects). Forexample, conference or forwarded calls consist ofmultiple partial calls.

32 Chapter 4 The RMI API

4.2.2 Couple object

Couple objects are the individual parts of the call. A couple object containsinformation about telephone numbers, the start and end of a call and so on.TheCouple object under an existingCall object may not be moved underanotherCall object or deleted. EachCouple object will contain twoStreamobjects.

4.2.3 Stream object

A Stream object is an object that stores information about a single RTPstream used to transfer data between terminals. This includes the IPaddress, port and the type of compression used. EachCouple objectshould contain twoStream objects. One relates to the calling terminal whilethe other relates to the called terminal. The Stream object is created underan existingCouple object and may not be moved under anotherCoupleobject or deleted.

4.2.4 Screen object

A Screen object is an object that stores information about a single screenstored for one of calling parties. This includes IP address and number ofplace for which screen can be recorded if required. EachCouple objectshould contain twoScreen objects. One relates to the calling terminal whilethe other relates to the called terminal. The Screen object is created underan existingCouple object and may not be moved under anotherCoupleobject or deleted.

4.3 Initial Object States

Each Core object goes through the following four states:

l CREATED

l REMOVED

Chapter 4 The RMI API 33

l FINISHED

l DESTROYED

4.3.1 CREATED

The object is created by the core (driver). An xxxCreatedEv (x105) event isgenerated.

4.3.2 REMOVED

The object is processed by the driver. When the object is no longer neededby the driver, all references to the object are deleted, and axxxRemovedEv (x106) event is triggered. If the client is unable to processthis event, the object is automatically blocked from being in the FINISHEDstate. It is then able to execute a method requiring a state other thanFINISHED. If you need to call up on such a method at a later point, you canblock the object from being in the FINISHED state using setFinishLock()method. The object may be unblocked usingunsetFinishLock()method.The methods are part of the IRMICallRecObject interface that isimplemented by all three types of objects. setFinishLock()method needsto be called the same number of times as the unsetFinishLock()method.An object stays blocked if one of its sub-objects are blocked. For example,theCall object may not be in FINISHED state if one of itsCouple objectsremains inREMOVED state. The setFinishLock()method may trigger anObjectIsFinishedException exception, even if the object is in FINISHEDstate.

Properties of objects that may be set inREMOVED state (R – propertymaybe read through the API; W – propertymay be modified; Ev – a change ofproperty triggers an event):

l CoupleType - Couple - R, Ev

l RecordStatus - Couple - R,W, Ev

l RecordScreenStatus - Couple - R,W, Ev

l Email - Couple - R,W

l CoupleStatus - Couple - R, Ev

34 Chapter 4 The RMI API

4.3.3 FINISHED

All objects that were blocked from being in the FINISHED state releasedthe lock. In this stage it is no longer possible to set properties related torecording rules, as the decoder operation has already started, andanxxxFinishedEv (x107) event was triggered. Similarly to the previousstate, if the client application is unable to process this event, the object isautomatically blocked from being in theDESTROYED state. UsingsetDestroyLock() and unsetDestroyLock()methods, you can control howlong the object remains in this state. setDestroyLock()method may triggeranObjectIsDestroyedException exception.

The following object propertiesmay be set in the FINISHED state:

l ProblemStatus - Couple - R, Ev

l StoreFileName - Couple - R

l ExternalData - Call, Couple - R,W

l DBId - Call, Couple - R

l StoreStatus - Stream, Screen - R, Ev

4.3.4 DESTROYED

The object no longer needs to be changed. Everything is locked againstwrite operations and all properties are read-only. An xxxDestroyedEv(x108) event is triggered.

4.4 API objects

CallRECRMI API provides following object types:

l TheRMIEvent object implements the IRMIEvent interface from whichall incoming events are inherited. The object contains the getID()method that returns the exact type of event.

l TheRMICall object represents the CallREC Core’sCall object throughRMI API.

l TheRMICouple object represents the CallREC Core’sCouple objectthrough RMI API.

Chapter 4 The RMI API 35

l TheRMIStream object represents the CallREC Core’sStream objectthrough RMI API.

l TheRMIScreen object represents the CallREC Core’sScreen objectthrough RMI API.

l The StreamInfo object contains basic information about the calling andcalled Stream objects. StreamInfo objects are returned toCoupleobject (getCallingStreamInfo() and getCalledStreamInfo()methods).

l TheRMIExternalData contains additional data forCall and Coupleobjects. The data is identified by a String-type key and also containString-type information.

4.5 Observer Connection

TheCallRecAPI.getCoreAPI()method returns aCoreAPI object thatpoints to four possible sources of core-generated events:

l RMICalls

l RMICouples

l RMIStreams

l RMIScreens

The returned RMI references enable the registering and discarding ofobserverswith these objects. The registered observers are notified of newand discarded RMICall,RMICouple,RMIStream orRMIScreen objectsthat againmay be observed tomonitor their states.

The method may trigger aCoreAPINotReadyException exception that isonly launched when API support is not enabled in the system core (see theconfiguration).

Connectable observer is a class instance inherited from theRemoteObserver class that can be found in cz.zoom.util.observerpackage, which is a part of CallREC RMI API 1.1 distribution.

Objects implementing IRMICallsAPI, IRMICouplesAPI, IRMIStreamsAPIand IRMIScreensAPI interfaces provide six methods:

l void addObserver(IRemoteObserver remoteObserver) – enablesconnecting an observer to an object. The first event received by theobserver upon connecting isXXXsObservationStartedEv (X001).Afterwards, the observer is informed on creation and destruction(CREATED and DESTROYED states) of allCall objects (Couple,Stream, Screen) –XXXCreatedEv (X105) and XXXDestroyedEv(X108) events.

36 Chapter 4 The RMI API

l void deleteObserver(IRemoteObserver remoteObserver) –disconnects an observer from the monitored object. From the receipt ofXXXsObservationEndedEv (X002) event, no more XXXCreatedEv(X105) and XXXDestroyedEv (X108) events are received.

l int countObservers() – returns the number of observers registeredwith an object.

l void setAutoRegistration(IRemoteObserver observer) ensures thatallCall objects (Couple, Stream, Screen) will be automaticallyregistered with the particular observer upon their creation.

l void unsetAutoRegistration(IRemoteObserver observer) disablesautomatic registration.

l int getCountXXX – returns the number ofCall objects (Couple,Stream, Screen) used by core. State of all these objects isCREATED.

Therefore, IRMICalls (IRMICouples, IRMIStreams, IRMIScreens) objectsonly inform the registered observers on the creation and discharge ofindividual objects. If you want to be informed about all the events related toa particular object (such as state changes), you have to register an observerdirectly with this object (upon obtaining IRMICall (IRMICouple,IRMIStream, IRMIScreen) reference from one of the events). If you need tomonitor all objects, it is sufficient to call setAutoRegistration()method thatwill perform automatic registration for the XXXCreatedEv (X105) event. Ifyou are registered with an object (manually or using the auto-registrationfeature) and the object’s state changes toDESTROYED, it is automaticallyde-registered. The de-registrationmay also be performed manually.

Object methods implementing the IRMICall, IRMICouple, IRMIStreamand IRMIScreen interfaces include the following (the same aswithIRMICallsAPI, IRMICouplesAPI, IRMIStreamsAPI, IRMIScreensAPI):

l void addObserver(IRemoteObserver remoteObserver) – enablesconnecting an observer to an object. The first event received by theobserver upon connecting isXXXObservationStartedEv (X101).Beginningwith this event, the observer is informed about all changes ofthe object.

l void deleteObserver(IRemoteObserver remoteObserver) –disconnects an observer from the monitored object. From the receipt ofXXXObservationEndedEv (X102) event, nomore events related to theobject are received, except for XXXDestroyedEv (X108) eventgenerated by the IRMIXXXsAPI object.

l int countObservers() – returns the number of observers registeredwith an object.

Chapter 4 The RMI API 37

4.6 Receiving Events

Upon registration with one of the objects, the observer starts receivingCallREC core-generated events through the remoteUpdate(Object o)method.Object o is a field of events implementing the IRMIEventinterface. The first event carries a 0 index. ThereforeObject o, handed overas a parameter to the remoteUpdate()method, may always betransformed to IRMIEvent[].

The IRMIEvent interface contains the getID()method enabling to recognizethe type of event. There are five basic types of events:

InfoEv – events informing on start and end ofRMICallsAPI(RMICouplesAPI,RMIStremsAPI,RMIScreensAPI) objectsmonitoringby an observer. This is an informative-only event without any furthermethods.

ErrorEv – events generated by core in case of problem. ContainsgetMessage()method that returns a brief description of occurring error.

CallEv – events informing onCall object changes. This event containsgetCall()method that returns RMI reference to theCall object, whichstores information about the particular coreCall object.

CoupleEv – events informing onCouple object changes. This eventcontainsgetCouple()method that returns RMI reference to theCoupleobject, which stores information about the particular coreCouple object.

StreamEv – events informing onStream object changes. This eventcontainsgetStream()method that returns RMI reference to the Streamobject, which stores information about the particular core Stream object.

ScreenEv – events informing onStream object changes. This eventcontainsgetScreen()method that returns RMI reference to the Screenobject, which stores information about the particular core Screen object.

If you receive any RMI reference to aCore object (IRMICall, IRMICouple,IRMIStream, IRMIScreen), the reference is always the same. Thereferencesmay be compared between themselves.

Example:

IRMICall call = ((CallCreatedEv)event).getCall;

IRMICouple couple = ((CoupleCreatedEv)event).getCouple;if (call == couple.getCall()) . . .

38 Chapter 4 The RMI API

4.7 Core-Generated Events

By transforming the IRMIEvent interface to a particular type of event, youare able to leverage other methods of these inherited events. ID type of agiven event is final static, therefore it can be leveraged by linking directlyfrom the object.

Example:

if (event.getID() == CallsObservationStartedEv.ID) .. .

4.7.1 InfoEv

This event doesn’t provide any further methods.

Following events are derived from InfoEv:

CallsObservationStartedEv (ID = 1001) Event generated upon registeringan observer withRMICallsAPI. This is always the first event related toCallobjects the observer receives.

CallsObservationEndedEv (ID = 1002) Event generated upondisconnection of an observer from RMICallsAPI.

CouplesObservationStartedEv (ID = 2001) Event generated uponregistering an observer withRMICouplesAPI. This is always the first eventrelated toCouple objects the observer receives.

CouplesObservationEndedEv (ID = 2002) Event generated upondisconnection of an observer from RMICouplesAPI.

StreamsObservationStartedEv (ID = 3001) Event generated uponregistering an observer withRMIStreamsAPI. This is always the first eventrelated to Stream objects the observer receives.

StreamsObservationEndedEv (ID = 3002) Event generated upondisconnection of an observer from RMIStreamsAPI.

ScreensObservationStartedEv (ID = 4001) Event generated uponregistering an observer withRMIScreensAPI. This is always the first eventrelated to Screen objects the observer receives.

ScreensObservationEndedEv (ID = 4002) Event generated upondisconnection of an observer from RMIScreensAPI.

Chapter 4 The RMI API 39

4.7.2 CallEv

This event providesgetCall()method that returns theRMICall the eventsrelate to.

Following events are derived from CallEv:

CallObservationStartedEv (ID = 1101) occurs upon registering anobserver with the particularCall object (also ifRMICallsAPI auto-registration is enabled).

CallObservationEndedEv(ID = 1102) occurs upon the disconnection of anobserver from this object. If the object expires, it is automatically de-registered and the event is received too. Following this event, onlyCallDestroyedEv is received.

CallCreatedEv (ID = 1105) the creation of aCall object in CallREC core.Occurs as the very first event related to aCall object. Observer is notified ofthis event byRMICallsAPI object.

CallRemovedEv(ID = 1106) the object’s state changed toREMOVED.

CallFinishedEv (ID = 1107) the object’s state changed toFINISHED.

CallDestroyedEv (ID = 1108) the object’s state isDESTROYED. This is thelast event triggered in relation to the particular Call object. It is not triggeredby the Call object itself but rather byRMICallsAPI.

ChildCallsChangedEv (ID = 1110) Occurs if child Call objects of aCallobject change.Call objects almost never have any child Call objects.

ParentCallChangedEv (ID = 1115) Occurs if parent Call object of aCallobject changes. For most parent Call objects, this is null.

CouplesChangedEv (ID = 1120) This event is triggered ifCouple objects ofaCall object change. The nature of the change may be only an addition of anewCouple object.Couple objectsmay not be discarded or moved underanotherCall object.

4.7.3 CoupleEv

ProvidesgetCall()method that returns theRMICouple the events relate to.

Following events are derived from CoupleEv:

CoupleObservationStartedEv (ID = 2101) occurs upon registering anobserver with the particularCouple object (also if RMICouplesAPI auto-registration is enabled).

40 Chapter 4 The RMI API

CoupleObservationEndedEv (ID = 2102) occurs upon the disconnectionof an observer from this object. If the object expires, it is automatically de-registered and the event is received too. Following this event, onlyCoupleDestroyedEv is received.

CoupleCreatedEv(ID = 2105) the creation of aCouple object in CallRECcore. Occurs as the very first event related to aCouple object. Observer isnotified of this event byRMICouplesAPI object.

CoupleRemovedEv (ID = 2106) the object’s state changed toREMOVED.

CoupleFinishedEv(ID = 2107) the object’s state changed toFINISHED.

CoupleDestroyedEv(ID = 2108) the object’s state isDESTROYED. This isthe last event triggered in relation to the particularCouple object. It is nottriggered by theCouple object itself but rather byRMICouplesAPI.

CallingStreamChangedEv (ID = 2110) occurs ifStream object of the callerparty changes (is established). The Stream object can not be changed,therefore this event will be received only once per oneCouple object. Theevent will be received followingStreamCreatedEv event (if you subscribeto events from RMIStreamsAPI).

CalledStreamChangedEv (ID = 2111) occurs if the Stream object of thecalled party changes (is established). The Stream object can not bechanged, therefore this event will be received only once per oneCoupleobject. The event will be received followingStreamCreatedEv event (if yousubscribe to events from RMIStreamsAPI).

CallingScreenChangedEv (ID = 2112) occurs if the Screen object of thecaller party changes (is established). The Screen object can not bechanged, therefore this event will be received only once per oneCoupleobject. The event will be received followingScreenCreatedEv event (if yousubscribe to events from RMIScreensAPI).

CalledScreenChangedEv (ID = 2113) occurs ifScreen object of the calledparty changes (is established). Screen object can’t be changed, thereforethis event will be received only once per oneCouple object. The event willbe received followingScreenCreatedEv event (if you subscribe to eventsfrom RMIScreensAPI).

RecordStatusChangedEv (ID = 2120) occurs ifRecordStatus of theparticularCouple object changes (see States). As theRecordStatusmaybe changed only if the object’s state is no higher thanREMOVED, you can’treceive this event after the receipt ofCoupleFinishedEv event.

RecordScreenStatusChangedEv (ID = 2121) occurs ifRecordScreenStatus of the particularCouple object changes (seeStates). As theRecordScreenStatusmay be changed only if the object’sstate is no higher thanREMOVED, you can’t receive this event after thereceipt ofCoupleFinishedEv event.

Chapter 4 The RMI API 41

CoupleStatusChangedEv (ID = 2125) occurs ifCoupleStatus of theparticularCouple object changes (see States). As theCoupleStatusmaybe changed only if the object’s state is no higher thanREMOVED, you can’treceive this event after the receipt ofCoupleFinishedEv event.

CoupleTypeStatusChangedEv (ID = 2130) occurs ifCoupleTypeStatusof the particularCouple object changes (see States). As theCoupleTypeStatusmay be changed only if the object’s state is no higherthanREMOVED, you cannot receive this event after the receipt ofCoupleFinishedEv event.

ProblemStatusChangedEv (ID = 2135) occurs ifProblemStatus of theparticularCouple object changes (see States). This event may be receivedeven ifCouple object’s state is FINISHED.

CouplePausedResumedEv (ID = 2140) occurs if the particularCoupleobject changes its paused state (is paused or resumed).

4.7.4 StreamEv

Provides getStream()method that returns theRMIStream the eventsrelate to.

Following events are derived from StreamEv:

StreamObservationStartedEv (ID = 3101) occurs upon registering anobserver with the particular Stream object (also ifRMIStreamsAPI auto-registration is enabled).

StreamObservationEndedEv (ID = 3102) occurs upon the disconnectionof an observer from this object. If the object expires, it is automatically de-registered and the event is received too. Following this event, onlyStreamDestroyedEv is received.

StreamCreatedEv (ID = 3105) the creation of a Stream object in CallRECcore. Occurs as the very first event related to a Stream object. Observer isnotified of this event byRMIStreamsAPI object.

StreamRemovedEv (ID = 3106) the object’s state changed toREMOVED.

StreamFinishedEv (ID = 3107) the object’s state changed toFINISHED.

StreamDestroyedEv (ID = 3108) the object’s state isDESTROYED. This isthe last event triggered in relation to the particular Stream object. It is nottriggered by the Stream object itself but rather byRMIStreamsAPI.

StoreStatusChangedEv (ID = 3110) occurs ifStoreStatus of the particularStream object changes (see States). This event may be received even ifStream object’s state is FINISHED.

42 Chapter 4 The RMI API

4.7.5 ScreenEv

ProvidesgetScreen()method that returns theRMIScreen the eventsrelate to.

Following events are derived from ScreenEv:

StreamObservationStartedEv (ID = 4101) occurs upon registering anobserver with the particular Screen object (also ifRMIScreensAPI auto-registration is enabled).

ScreenObservationEndedEv (ID = 4102) occurs upon the disconnectionof an observer from this object. If the object expires, it is automatically de-registered and the event is received too. Following this event, onlyScreenDestroyedEv is received.

ScreenCreatedEv (ID = 4105) the creation of a Screen object in CallRECcore. Occurs as the very first event related to a Screen object. Observer isnotified of this event byRMIScreensAPI object.

ScreenRemovedEv (ID = 4106) the object’s state changed toREMOVED.

ScreenFinishedEv (ID = 4107) the object’s state changed toFINISHED.

ScreenDestroyedEv (ID = 4108) the object’s state isDESTROYED. This isthe last event triggered in relation to the particular Screen object. It is nottriggered by the Screen object itself but rather byRMIScreensAPI.

ScreenStoreStatusChangedEv (ID = 4110) occurs ifStoreStatus of theparticular Screen object changes (see States). This event may be receivedeven ifScreen object’s state is FINISHED.

4.7.6 ErrorEv

Provides the getMessage()method that returns Stringwith errordescription.

Following events are derived from ErrorEv:

OverloadedQueueEv (ID = 9001) this event is sent if the maximum possiblenumber of events in observer queue is exceeded. If you receive this event,your observer was unable to accept a large number of events. The queuedeventswere cleared and this event was sent instead. From thismoment, allsubsequent events are sent or queued again. If you are unable to acceptthe events, evaluate if it is necessary to register with all of the event-generating objects you are registered with. Youmay alsowant to disableone of the auto-registrations and opt instead for manual registration ofthose objects that are required.

Chapter 4 The RMI API 43

CoreEndedEv (ID = 9101) event sent by core to all observers to notify ofCallREC shutdown. CallREC waits until all the observers accept the event.This period is limited, so if you are unable to accept events in a timelymanner, youmay not receive the event.

4.8 RMI Objects and Their Methods

4.8.1 IRMICall

long getCallId() returns object’s IDCall determined by core. This ID isunique for each Call within a single CallREC instance. Its numbering runsfrom 1 and in case of CallREC restart, it runs again from the start.

IRMICall getParentCall() returns the reference to the parents of thisCallobject. The parent may change during a call, for example, if the driverrecognizes that the call is actually a sub-call of a previous call (call-forwarding, conference). Only the driver maymanipulate these objects insuch a manner. Upon each change, a ParentCallChangedEv (ID = 1115)event is triggered. After aCallRemovedEv (ID = 1106) event, no one canmanipulate with the objects and their state is ultimate. Most often, thisvalue is null.

IRMICall[] getChildCalls() returns an array of references to child Callobjects. Upon each change, aChildCallsChangedEv (ID = 1110) event istriggered. More information about the possibility of child Call objectscreation can be found in previous section under getParentCall()method.Call objects usually do not have any child objects.

IRMICouple[] getCouples() returns an array of references to allCoupleobjects included within a particularRMICall. Upon each change, aCouplesChangedEv (ID = 1120) event is triggered. As the creation of newCouple objects ismanaged by the driver, after aCallRemovedEv (ID =1106) event, no one canmanipulate the objects and their state is ultimate.Couple objects are created directly under specificCall objects and maynot be moved or discarded. Therefore at the moment ofCouple object’screation it is clear under whichCall object it belongs. A standard simple callcontains oneCouple object.

Date getStartTime() returns the date and time of the creation of aCallobject.

Date getStopTime() returns the date and time of the termination of aCallobject.

IRMIExternalData getExternalData() returns an RMI reference toRMIExternalData for the particularCall object. Using this reference, you

44 Chapter 4 The RMI API

can find or add new ExternalData. You can always read ExternalData, butadding new is only possible beforeDESTROYED state. To save theexternal data at a later time, use theCall Storagemodule.

long getDatabaseId() if a call is saved to a database, thismethod returnsthe database ID of the specificCall object. The database ID is unique(primary key), and is available in theDESTROYED state. If you choose tocall thismethod before the call is saved or if the call has never saved, thenthe return value will be null.

4.8.2 IRMICouple

long getCoupleId() returns the object’s IDCouple determined by the core.For each Couple within a single CallREC instance, this ID is unique. Itsnumbering starts from 1 every time CallREC restarts.

IRMICall getCall() returns the RMI reference to theCall object.TheCallobject may not change withCouple object and therefore this value is setwithCoupleCreatedEv (ID = 2105) event. EachCouple object needs tohave itsCall object.

StreamInfo getCallingInfo() returns an object containing information onthe calling terminal. The information that needs to be set in specific times isoutlined in the section describingStreamInfo.

StreamInfo getCalledInfo() returns an object containing information on thecalled terminal. The information that needs to be set in specific times isoutlined in the section describingStreamInfo.

IRMIScreen getCallingScreen() returns the associated callingScreenobject.

IRMIScreen getCalledScreen() returns the associated called Screenobject.

Date getStartTime() returns date and time of a partial call start. The time isfirst set at the moment of aCouple object creation. If the driver learns thattheCouple object’s state is onlyRING, it can later modify the start timeuponCALL state. A definitive call start time is set upon the receipt ofCoupleStatusChangedEv (ID = 2125) event if the state isRING.

Date getStopTime() returns the date and time of the end of a partial call.The time is set by the driver in core at the moment of call end.When theCoupleRemovedEv (ID = 2106) event is received, the time has to be set.

int getRecordStatus() returns recording status information (for an overviewof statuses see Possible Object States).

EnumRecordStatus getEnumRecordStatus() returns a recording statusinformation (for an overview of statuses see Possible Object States).

Chapter 4 The RMI API 45

EnumRecordStatus getRecordScreenStatus() returns a recordingscreen status information (for an overview of statuses see Possible ObjectStates).

int getCoupleStatus() returns status information on the state of theparticularCouple object, for example ringing, on call, see Possible ObjectStates.

EnumCoupleStatus getEnumCoupleStatus() returns status informationon the state of the particularCouple object, for example ringing, on call,see Possible Object States

int getCoupleTypeStatus() returns status information on the type of call,such as conference, normal call, see Possible Object States.

EnumCoupleType getCoupleType() returns status information on thetype of call, such as conference, normal call, see Possible Object States.

int getProblemStatus() returns status information on potential problem ofaCouple object. See Possible Object States.

EnumProblemStatus getEnumProblemStatus() returns statusinformation on potential problem of aCouple object. See Possible ObjectStates.

int setRecordStatus(int newRecordStatus()Sets a recording status (foran overview of statuses see Possible Object States).

EnumRecordStatus setRecordStatus(EnumRecordStatusnewRecordStatus()Sets a recording status (for an overview of statusessee Possible Object States).

EnumRecordStatus setRecordScreenStatus(EnumRecordStatusnewRecordStatus()Sets a recording screen status (for an overview ofstatuses see Possible Object States).

void setEmail(String[] emails)Sets an e-mail where the recorded phonecall should be sent upon the completion of the call. Record statusmust beset.

IRMIExternalData getExternalData()Returns an RMI reference toRMIExternalData for the particularCouple object. Using this reference,you can find or add new ExternalData. You can always read ExternalDatawhile adding new is possible only in states earlier thanDESTROYED. If youwant to save the external data later, you can do this usingCall Storagemodule.

long getDatabaseId() If a partial call,Couple object was saved to adatabase, thismethod returns the database ID of the specific Coupleobject. The database ID is a unique primary key. The ID is certainly availablein theDESTROYED state. If you choose to call thismethod before the callis saved or if the call has never saved, then the return value will be null.

String getStoreAudioFileName() If a call was recorded and saved, youwillobtain the name of the file containing the audio data by calling thismethod.

46 Chapter 4 The RMI API

boolean isPaused()Returnswhether the Couple is paused.

void pause()Pause the Couple, if it is not paused yet.

void resume()Resume the Couple, if it is paused.

4.8.3 IRMIStream

long getStreamId() returns the object’s ID Stream determined by core. Foreach Stream within a single CallREC instance, this ID is unique. Itsnumbering runs from 1 and in case of CallREC restart, it runs again from thestart.

IRMICouple getCouple() returns the RMI reference to theCouple object.TheCouple object may not change with the Stream object and thereforethis value is set withStreamCreatedEv (ID = 3105) event. EachStreamobject needs to have itsCouple object.

String getIpAddress()Returns the IP address of a Stream object where acall is occurring.

int getPort()Returns the port of a Stream object where a call is occurring.

int getCommpresionType()Returns the type of compression employed tocompress the particular Stream, see Possible Object States.

EnumMediaPayload getMediaPayload()Returns the type of compressionemployed to compress the particular Stream, see Possible Object States.

int getStoreStatus()Returns a status that determines if the specificStream is being recorded, was recorded, and so on. See Possible ObjectStates.

EnumStoreStatus getEnumStoreStatus()Returns status thatdetermines if the specific Stream is being recorded, was recorded, and soon. See Possible Object States.

4.8.4 IRMIScreen

IRMICouple getCouple()Returns RMI reference toCouple object. TheCouple object may not change withStream object and therefore this valueis set withScreenCreatedEv (ID = 4105) event. EachScreen object needsto have itsCouple object.

String getIpAddress()Returns the IP address of a Screen object where acall is occurring.

String getNumber()Returns the telephone number of a Screen.

Chapter 4 The RMI API 47

EnumStoreStatus getEnumStoreStatus()Returns a status thatdetermines if the specific Screen is being recorded, was recorded, and soon. See Possible Object States.

4.8.5 IRMIExternalData

ExternalData[] getAllExternalData()Returns the fields of allExternalDataobjects stored with a particular object. ExternalData may be read even intheDESTROYED state.

ExternalData[] getExternalData(String key)Returns an array of allExternalData objectswith the specific key.

void addExternalData(ExternalData exData) Inserts a newly createdExternalData object. New ExternalDatamay be inserted only in statesearlier thanDESTROYED.

4.8.6 ExternalData

ExternalData(String key, String value)Construction; the first parameteris a keywhile the second parameter is a value. The key doesn’t have to beunique; it may be used an unlimited number of times. The followingIRMIExternalData interface methodswork with the object:

l String getKey() returns a key.

l String getValue() returns a value.

4.8.7 StreamInfo

StreamInfo object returns IRMICoupleRMI referenceswhengetCallingInfo() and getCalledInfo()methods are called. The objectcontains a reference toStream object’s RMI reference, phone number of aterminal, phone number description, and terminal’s IP address. Theselection and timing of defined information depend on the core driver. Atthe time of a Couple object’s creation, both phone numbersmust beknown. Therefore, following theCoupleCreatedEv (ID = 1105) event, thephone numbers are known. As the informationmay be changed only by thedriver, none of this information will be changed after the receipt of theCoupleRemovedEv (ID = 1106) event. None of this informationmay be setmore than once.

IRMIStream getStream() returns a link to Stream object’s RMI reference.

48 Chapter 4 The RMI API

String getNumber() returns a phone number. This has to be set at the timeofCouple object creation.

String getPartyName() returns a phone number’s description. It dependson the driver whether it is set.

String getAddress() returns a phone number’s IP address. It depends onthe driver whether it is set.

4.9 Possible Object States

4.9.1 RecordStatus

Possible values can be found at thecz.zoom.callrec.core.rules.EnumRecordStatus interface.

NONE (0) – default, set at the time of object creation.

RECORD_AFTER_PRE_RECORD (1) – record after PRE_RECORD state.

RECORD_EMAIL (2) – record but don’t save, only send by e-mail.

PRE_RECORD (3) – pre-record.

NO_RECORD (4) – don’t record.

NEVER (5) – do not record any partial calls.

QUEUED (6) – awaits in RecordingRules server queue until the status is set.

RECORD (7) – record and save.

MAY_BE (10) - Couple did not match any recording rules. Recording canbe started by settingRECORD value.

NOT_ANY_MORE (11) - recording of Couple was stopped before finish ofCouple and it was started before.

4.9.2 ProblemStatus

Possible values can be found atcz.zoom.callrec.core.calls.EnumProblemStatus.

NO_PROBLEM (0) - no problem occurred.

ONLY_ONE_STREAM (1) - only one stream was recorded.

NO_STREAMS (2) - no stream was recorded.

UNKNOWN_CODEC (3) - recorded streams have unknown codec.

Chapter 4 The RMI API 49

NO_REPLY_FROM_DECODER (4) - decoder did not response to decoderrequirement.

NO_REPLY_FROM_RECORDER (5) - recorder did not respond torecorder requirement.

CAPTURE_FILE_NOT_FOUND (6) - one of the captured fileswas notfound.

DECODER_IO_ERROR (7) - decoder failed to decode file(s).

DECODER_DIFF_PAYLOAD (8) - captured files have different codecs.

DECODER_OVERSIZE_TARGET (9) - decoded target file has reachedsize limit during decoding.

DECODER_NO_DEST_FORMAT (10) - no possible destination format forsource files found.

NO_RECORDING_DEVICE (11) - no recording device was available.

RECORDER_LICENSE_PROBLEM (20) - recorder has reached its licencelimits and cannot record Couple files.

CAPTURE_FILE_INCOMPLETE (30) - one of the captured files is notcomplete, likely because no space available on disk.

ONE_STREAM_INCOMPLETE (31) - only one stream was recorded and itis not complete.

4.9.3 CoupleStatus

Possible values can be found at cz.zoom.callrec.core.calls.EnumStatus.

NONE (0) - default, Couple object was created but RING has not yet beenset.

RING (1) - the phone is ringing.

CALL (2) - call is under way.

END (3) - end of call.

END_RING (4) - end of ringing, call didn’t occur.

50 Chapter 4 The RMI API

Figure 2: State Diagram

4.9.4 CoupleTypeStatus

Possible values can be found atcz.zoom.callrec.core.calls.EnumStatusCoupleType.

NORMAL (0) - default, standard call.

CONF (1) - conference call.

RECONN (2) - transferred call.

PARK (3) - parked call.

UNPARK (4) - retrieved call (from PARK).

BARGE (5) - barged call.

4.9.5 CommpresionType

Possible values can be found atcz.zoom.callrec.core.calls.EnumCommpresionType.

UNKNOWN (0)

SCCP_Media_Payload_NonStandard (1101)

SCCP_Media_Payload_G711Alaw64k (1102)

SCCP_Media_Payload_G711Alaw56k (1103)

SCCP_Media_Payload_G711Ulaw64k (1104)

SCCP_Media_Payload_G711Ulaw56k (1105)

SCCP_Media_Payload_G722_64k (1106)

SCCP_Media_Payload_G722_56k (1107)

SCCP_Media_Payload_G722_48k (1108)

Chapter 4 The RMI API 51

SCCP_Media_Payload_G7231 (1109)

SCCP_Media_Payload_G728 (1110)

SCCP_Media_Payload_G729 (1111)

SCCP_Media_Payload_G729AnnexA (1112)

SCCP_Media_Payload_Is11172AudioCap (1113)

SCCP_Media_Payload_Is13818AudioCap (1114)

SCCP_Media_Payload_G729AnnexB (1115)

SCCP_Media_Payload_G729AnnexAwAnnexB (1116)

SCCP_Media_Payload_GSM_Full_Rate (1117)

SCCP_Media_Payload_GSM_Half_Rate (1119)

SCCP_Media_Payload_GSM_Enhanced_Full_Rate (1120)

SCCP_Media_Payload_Wide_Band_256k (1125)

SCCP_Media_Payload_Data64 (1132)

SCCP_Media_Payload_Data56 (1133)

SCCP_Media_Payload_GSM (1180)

SCCP_Media_Payload_ActiveVoice (1181)

SCCP_Media_Payload_G726_32K (1182)

SCCP_Media_Payload_G726_24K (1183)

SCCP_Media_Payload_G726_16K (1184)

4.9.6 StoreStatus

Possible values can be found atcz.zoom.callrec.core.calls.EnumStoreStatus.

NOT_YET_SAVE (0) - default, recording hasn’t started yet.

SAVED (1) - the stream (screen) has recorded.

SAVING (2) - stream (screen) is being recorded.

NOT_SAVED (3) - recording has not started and will not start, an erroroccurred before or during recording, or recordingmay have started but wasstopped (rules).

SAVED_NOT_RULES (4) - the stream (screen) was recorded but it notknown if it was supposed to be recorded. For example, this can occur withpre-recorded call after the recording ends and the object’s state doesn’tchange to FINISHED. Then the call is either saved with its status changed toSAVEDor deleted with its status changed toNOT_SAVED.

52 Chapter 4 The RMI API

Appendix APause/Resume Web API cURL Examples

This appendix provides examples of accessing the Pause/ResumeWebAPI using the open source cURL tool, which can be used to simulate HTTPrequests and responses.

Important: This sample code is purely illustrative - it should only be used duringdevelopment and testing phases. ZOOM International will endeavor tokeep the code in this document up to date, but does not guarantee orprovide support for the sample code included here.

Chapter Topics: A.1 Basic Procedure 53A.2 cURL Examples 54A.3 Log in to Pause/Resume API 54A.4 List of Calls in Progress 56A.5 Get Call Details - Active Call 59A.6 Pause SelectedCall 60A.7 Get Call Details - PausedCall 61A.8 Resume SelectedCall 62A.9 Get Call Details - ResumedCall 62A.10 LogOut fromPause/Resume API 63

A.1 Basic Procedure

1. Log in via an HTTP POST request using a valid CallREC username andpassword.

2. For the login to complete normally, the client is advised to present anycookie (this is a known limitation that if the cookies are not sent to the

53

login request, the request will not do the actual logging in. Any cookiecan be used). The response to this POST request will NOT indicate thesuccess or failure of the login.

3. Send an HTTP GET request (with optional filter parameters) to list calls inprogress. Calls accessible to the logged-in user (for which the user hascall detail permissions) will include a call details URI in the result list. A403 (Forbidden) response means the user is not logged in. Aresponse with status 200 (OK)will contain an XML document listingthe calls in progress that satisfy the search condition.

Send an HTTP GET request to one specific call details URI. The replyis an XML document that includes a pause/resume URI if the logged-inuser has appropriate permissions for this action. The pause/resumeURI can be constructed also programmatically as:http://<server>/callrec/restful/couples.xml/<coupleId>/pausedState

4. Send an HTTP POST/PUT request to the pause/resume URI with anadditional paused=true parameter to pause the call or screenrecording, or use a pause=false parameter to resume call or screenrecording.

5. Log out, again via an HTTP POST request.

A.2 cURL Examples

The following samples demonstrate this procedure in practice using thecURL tool.

To reproduce these calls, ensure that for the curl commands displayed,you update the login and password parameter valueswith thecredentials of a user you have created solely for API purposes, with at leastPause and Resume Calls and Call List permissions. Youwill also need toreplace the myserver hostname with the resolvable hostname of your QMSuite server running the Core service and Tomcat web server.

A.3 Log in to Pause/Resume API

curl -v -b cookies.txt -c cookies.txt -X POST -d"login=admin&password=Password" myserver/callrec/loginAction.do

54 Appendix A Pause/Resume Web API cURL Examples

* About to connect() to myserver port 80* Trying 192.168.1.2... connected* Connected to myserver (192.168.1.2) port 80> POST /callrec/loginAction.do HTTP/1.1> User-Agent: curl/7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5> Host: myserver> Accept: */*> Cookie: JSESSIONID=2B80FC14142D6A6F64D75EA323FC9AA0> Content-Length: 29> Content-Type: application/x-www-form-urlencoded>> login=admin&password=PasswordHTTP/1.1 200 OK< Server: Apache-Coyote/1.1< Content-Type: text/html< Content-Length: 2779< Date: Wed, 25 Apr 2012 07:14:37 GMT

<html><head>

<link rel="SHORTCUT ICON" href="favicon.ico"/><title>ZOOM CallREC 4.7.6, build: 111110_0145 | myserver</title>

</head>

<script type="text/javascript"><!-- JavaScript removed for clarity --></script>

<frameset id='frameset_main' rows="0px,81px,*,0px"><frame name="audio" src="copyright.html" scrolling="no"

frameborder="no" noresize="noresize"><frame name="main_menu" src="menu_reccall.jsp" scrolling="no"

noresize="noresize" frameborder="no"><frameset id='frameset_middle_row' cols="*,0px">

<frame name="main" src="filteroperation?view_type=couple"frameborder="no" scrolling="yes" noresize="noresize">

<frame name="search" src="filters.jsp" frameborder="no"scrolling="yes" noresize="noresize">

</frameset><frame name="copyright" src="copyright.html" scrolling="no"

noresize="noresize" frameborder="no"></frameset>

Appendix A Pause/Resume Web API cURL Examples 55

</html>* Connection #0 to host myserver left intact* Closing connection #0

Important: For clarity, the following examples omit thismandatory step to log in to thePause/Resume API.

A.4 List of Calls in Progress

curl -v -b cookies.txt -c cookies.txt -Gmyserver/callrec/restful/couples.xml* About to connect() to myserver port 80* Trying 192.168.1.2... connected* Connected to myserver (192.168.1.2) port 80> GET /callrec/restful/couples.xml HTTP/1.1> User-Agent: curl/7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5> Host: myserver> Accept: */*> Cookie: JSESSIONID=415DC8389E8F276EF9C12EB25F07463E>< HTTP/1.1 403 Forbidden< Server: Apache-Coyote/1.1< Content-Type: text/html;charset=utf-8< Content-Length: 991< Date: Wed, 25 Apr 2012 14:50:38 GMT<html><head><title>Apache Tomcat/6.0.24 - Errorreport</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2{font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY{font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A{color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 403 - Forbidden</h1><HRsize="1" noshade="noshade"><p><b>type</b> Status

56 Appendix A Pause/Resume Web API cURL Examples

report</p><p><b>message</b><u>Forbidden</u></p><p><b>description</b> <u>Access to the specifiedresource (Forbidden) has been forbidden.</u></p><HR size="1"noshade="noshade"><h3>Apache Tomcat/6.0.24</h3></body></html>Connection #0 to host myserver left intact* Closing connection #0

The following examples show variousmethods of specifying calls inprogress.

A.4.1 No Filter - No Calls in Progress

curl -v -b cookies.txt -c cookies.txt -X GETmyserver/callrec/restful/couples.xml* About to connect() to myserver port 80* Trying 192.168.1.2... connected* Connected to myserver (192.168.1.2) port 80> GET /callrec/restful/couples.xml HTTP/1.1> User-Agent: curl/7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5> Host: myserver> Accept: */*> Cookie: JSESSIONID=FC1218F4EE0A64706EC3E9A96ED1AE62>< HTTP/1.1 200 OK< Server: Apache-Coyote/1.1< Cache-Control: no-cache, no-transform< Expires: Wed, 25 Apr 2012 11:31:13 GMT< Content-Type: application/xml< Content-Length: 105< Date: Wed, 25 Apr 2012 11:31:13 GMT<?xml version="1.0" encoding="UTF-8" standalone="yes"?><couplesxmlns:ns2="http://www.w3.org/2005/Atom"/>Connection #0 to host myserver left intact* Closing connection #0

Appendix A Pause/Resume Web API cURL Examples 57

A.4.2 No Filter - One Call in Progress

curl -v -b cookies.txt -c cookies.txt -Gmyserver/callrec/restful/couples.xml* About to connect() to myserver port 80* Trying 192.168.1.2... connected* Connected to myserver (192.168.1.2) port 80> GET /callrec/restful/couples.xml HTTP/1.1> User-Agent: curl/7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5> Host: myserver> Accept: */*> Cookie: JSESSIONID=FC1218F4EE0A64706EC3E9A96ED1AE62>< HTTP/1.1 200 OK< Server: Apache-Coyote/1.1< Cache-Control: no-cache, no-transform< Expires: Wed, 25 Apr 2012 12:06:57 GMT< Content-Type: application/xml< Content-Length: 364< Date: Wed, 25 Apr 2012 12:06:58 GMT<?xml version="1.0" encoding="UTF-8" standalone="yes"?><couplesxmlns:ns2="http://www.w3.org/2005/Atom"><couples id="5"callId="5"><ns2:link type="application/xml"href="http://myserver/callrec/restful/couples.xml/5"rel="self"/><from>5688</from><paused>false</paused><start>2012-04-25T13:58:25.985+02:00</start><to>5680</to></couples></couples>Connection #0 to host myserver left intact* Closing connection #0

A.4.3 Filter for Phone Number - One Call in Progress

curl -v -b cookies.txt -c cookies.txt -G -d"phoneNumber=5680&phoneNumberDirection=BOTH"myserver/callrec/restful/couples.xml* About to connect() to myserver port 80* Trying 192.168.1.2... connected* Connected to myserver (192.168.1.2) port 80

58 Appendix A Pause/Resume Web API cURL Examples

> GET/cal-lrec/restful/couples.xml?phoneNumber=5680&phoneNumberDirection=BOTHHTTP/1.1> User-Agent: curl/7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5> Host: myserver> Accept: */*> Cookie: JSESSIONID=FC1218F4EE0A64706EC3E9A96ED1AE62>< HTTP/1.1 200 OK< Server: Apache-Coyote/1.1< Cache-Control: no-cache, no-transform< Expires: Wed, 25 Apr 2012 12:06:25 GMT< Content-Type: application/xml< Content-Length: 364< Date: Wed, 25 Apr 2012 12:06:25 GMT<?xml version="1.0" encoding="UTF-8" standalone="yes"?><couplesxmlns:ns2="http://www.w3.org/2005/Atom"><couples id="5"callId="5"><ns2:link type="application/xml"href="http://myserver/callrec/restful/couples.xml/5"rel="self"/><from>5688</from><paused>false</paused><start>2012-04-25T13:58:25.985+02:00</start><to>5680</to></couples></couples>Connection #0 to host myserver left intact* Closing connection #0

A.5 Get Call Details - Active Call

curl -v -b cookies.txt -c cookies.txt -Gmyserver/callrec/restful/couples.xml/5* About to connect() to myserver port 80* Trying 192.168.1.2... connected* Connected to myserver (192.168.1.2) port 80> GET /callrec/restful/couples.xml/5 HTTP/1.1> User-Agent: curl/7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5> Host: myserver> Accept: */*> Cookie: JSESSIONID=FC1218F4EE0A64706EC3E9A96ED1AE62>

Appendix A Pause/Resume Web API cURL Examples 59

< HTTP/1.1 200 OK< Server: Apache-Coyote/1.1< Cache-Control: no-cache, no-transform< Expires: Wed, 25 Apr 2012 12:24:44 GMT< Content-Type: application/xml< Content-Length: 479< Date: Wed, 25 Apr 2012 12:24:44 GMT<?xml version="1.0" encoding="UTF-8" standalone="yes"?><couplexmlns:ns2="http://www.w3.org/2005/Atom" id="5" callId="5"><ns2:linktype="application/xml"href="http://myserver/callrec/restful/couples.xml/5"rel="self"/><ns2:link type="application/xml"href="http://myserver/callrec/restful/couples.xml/5/pausedState"rel="pause/-resume"/><from>5688</from><paused>false</paused><start>2012-04-25T13:58:25.985+02:00</start><to>5680</to></couple>Connection #0 to host myserver left intact* Closing connection #0

A.6 Pause Selected Call

curl -v -b cookies.txt -c cookies.txt -d "paused=true" -X PUTmyserver/callrec/restful/couples.xml/5/pausedState* About to connect() to myserver port 80* Trying 192.168.1.2... connected* Connected to myserver (192.168.1.2) port 80> PUT /callrec/restful/couples.xml/5/pausedState HTTP/1.1> User-Agent: curl/7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5> Host: myserver> Accept: */*> Cookie: JSESSIONID=FC1218F4EE0A64706EC3E9A96ED1AE62> Content-Length: 11> Content-Type: application/x-www-form-urlencoded>> paused=trueHTTP/1.1 303 See Other< Server: Apache-Coyote/1.1< Location: http://myserver/callrec/restful/couples.xml/5/< Content-Length: 0

60 Appendix A Pause/Resume Web API cURL Examples

< Date: Wed, 25 Apr 2012 12:23:47 GMT* Connection #0 to host myserver left intact* Closing connection #0

A.7 Get Call Details - Paused Call

curl -v -b cookies.txt -c cookies.txt -Gmyserver/callrec/restful/couples.xml/5* About to connect() to myserver port 80* Trying 192.168.1.2... connected* Connected to myserver (192.168.1.2) port 80> GET /callrec/restful/couples.xml/5 HTTP/1.1> User-Agent: curl/7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5> Host: myserver> Accept: */*> Cookie: JSESSIONID=FC1218F4EE0A64706EC3E9A96ED1AE62>< HTTP/1.1 200 OK< Server: Apache-Coyote/1.1< Cache-Control: no-cache, no-transform< Expires: Wed, 25 Apr 2012 12:24:44 GMT< Content-Type: application/xml< Content-Length: 479< Date: Wed, 25 Apr 2012 12:24:44 GMT<?xml version="1.0" encoding="UTF-8" standalone="yes"?><couplexmlns:ns2="http://www.w3.org/2005/Atom" id="5" callId="5"><ns2:linktype="application/xml"href="http://myserver/callrec/restful/couples.xml/5"rel="self"/><ns2:link type="application/xml"href="http://myserver/callrec/restful/couples.xml/5/pausedState"rel="pause/-resume"/><from>5688</from><paused>true</paused><start>2012-04-25T13:58:25.985+02:00</start><to>5680</to></couple>Connection #0 to host myserver left intact* Closing connection #0

Appendix A Pause/Resume Web API cURL Examples 61

A.8 Resume Selected Call

curl -v -b cookies.txt -c cookies.txt -d "paused=false" -X PUTmyserver/callrec/restful/couples.xml/5/pausedState* About to connect() to myserver port 80* Trying 192.168.1.2... connected* Connected to myserver (192.168.1.2) port 80> PUT /callrec/restful/couples.xml/5/pausedState HTTP/1.1> User-Agent: curl/7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5> Host: myserver> Accept: */*> Cookie: JSESSIONID=FC1218F4EE0A64706EC3E9A96ED1AE62> Content-Length: 12> Content-Type: application/x-www-form-urlencoded>> paused=falseHTTP/1.1 303 See Other< Server: Apache-Coyote/1.1< Location: http://myserver/callrec/restful/couples.xml/5/< Content-Length: 0< Date: Wed, 25 Apr 2012 12:25:40 GMT* Connection #0 to host myserver left intact* Closing connection #0

A.9 Get Call Details - Resumed Call

curl -v -b cookies.txt -c cookies.txt -Gmyserver/callrec/restful/couples.xml/5* About to connect() to myserver port 80* Trying 192.168.1.2... connected* Connected to myserver (192.168.1.2) port 80> GET /callrec/restful/couples.xml/5 HTTP/1.1> User-Agent: curl/7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5> Host: myserver> Accept: */*> Cookie: JSESSIONID=FC1218F4EE0A64706EC3E9A96ED1AE62

62 Appendix A Pause/Resume Web API cURL Examples

>< HTTP/1.1 200 OK< Server: Apache-Coyote/1.1< Cache-Control: no-cache, no-transform< Expires: Wed, 25 Apr 2012 12:25:59 GMT< Content-Type: application/xml< Content-Length: 480< Date: Wed, 25 Apr 2012 12:25:59 GMT<?xml version="1.0" encoding="UTF-8" standalone="yes"?><couplexmlns:ns2="http://www.w3.org/2005/Atom" id="5" callId="5"><ns2:linktype="application/xml"href="http://myserver/callrec/restful/couples.xml/5"rel="self"/><ns2:link type="application/xml"href="http://myserver/callrec/restful/couples.xml/5/pausedState"rel="pause/-resume"/><from>5688</from><paused>false</paused><start>2012-04-25T13:58:25.985+02:00</start><to>5680</to></couple>Connection #0 to host myserver left intact* Closing connection #0

A.10 Log Out from Pause/Resume API

curl -v -b cookies.txt -c cookies.txt -X POSTmyserver/callrec/logoutprocess* About to connect() to myserver port 80* Trying 192.168.1.2... connected* Connected to myserver (192.168.1.2) port 80> POST /callrec/logoutprocess HTTP/1.1> User-Agent: curl/7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5> Host: myserver> Accept: */*> Cookie: JSESSIONID=A7BF26C94EE0EA7F5F36DABFC9CDE08F>< HTTP/1.1 302 Moved Temporarily< Server: Apache-Coyote/1.1* Replaced cookie JSESSIONID="415DC8389E8F276EF9C12EB25F07463E" fordomain myserver, path /callrec, expire 0< Set-Cookie: JSESSIONID=415DC8389E8F276EF9C12EB25F07463E;Path=/callrec

Appendix A Pause/Resume Web API cURL Examples 63

< Location: http://myserver/callrec/index.jsp< Content-Length: 0< Date: Wed, 25 Apr 2012 14:32:25 GMT* Connection #0 to host myserver left intact

64 Appendix A Pause/Resume Web API cURL Examples

Appendix BSample Java Web Client Source Code forPause/Resume API

This appendix provides the source code for a complete sample clientapplication in Java, demonstrating the principles of use for thePause/Resume API.

Important: This source code is provided by ZOOM International but is purely illustrative- it should not be used unchanged in a production environment. ZOOMInternational will endeavor to keep the code in this document up to date,but does not guarantee or provide support for the sample code includedhere.

The Java source code listing begins on the next page.

package cz.zoom.callrec.restful.client;

import java.io.BufferedReader;import java.io.Console;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.Arrays;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;

import javax.xml.XMLConstants;import javax.xml.namespace.NamespaceContext;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.xpath.XPath;import javax.xml.xpath.XPathConstants;

65

import javax.xml.xpath.XPathExpression;import javax.xml.xpath.XPathExpressionException;import javax.xml.xpath.XPathFactory;

import org.apache.commons.httpclient.Cookie;import org.apache.commons.httpclient.HttpClient;import org.apache.commons.httpclient.HttpMethod;import org.apache.commons.httpclient.HttpState;import org.apache.commons.httpclient.cookie.CookiePolicy;import org.apache.commons.httpclient.methods.GetMethod;import org.apache.commons.httpclient.methods.PostMethod;import org.apache.commons.httpclient.methods.PutMethod;import org.apache.commons.httpclient.methods.StringRequestEntity;import org.w3c.dom.Document;import org.w3c.dom.NodeList;

public class Main {private HttpState myInitialState;private HttpClient myClient;private String myServer;private XPath myXpath;

private static final Console console = System.console();private static final String PAUSED_STATE = "paused";

private interface ResponseProcessor<T> {T read(InputStream result) throws IOException;

void onStatus(int status) throws IOException;}

public static void main(String[] args) {String phone = null;boolean pause = false;boolean resume = false;String user = null;String server = null;for (int i = 0; i < args.length; i++) {

if ("-p".equals(args[i]) || "--phone".equals(args[i])) {phone = args[++i];

}if ("-u".equals(args[i]) || "--user".equals(args[i])) {

user = args[++i];}

66 Appendix B Sample Java Web Client Source Code for Pause/Resume API

if ("-s".equals(args[i]) || "--server".equals(args[i])) {server = args[++i];

}if ("pause".equals(args[i])) {

pause = true;}if ("resume".equals(args[i])) {

resume = true;}

}try {

if (user == null) {user = readLine("Please enter user name");

}char[] password = readPassword("Please enter password");//Normally, the phone number should also be enforced, but

for this example, we do not require it.Main main = null;try {

try {main = new Main(server, user, password);

} finally {Arrays.fill(password, (char)0);

}List<String> items = main.list(phone);if (items.isEmpty()) {

System.out.println("There is no call in progress thatmatches the criteria");

}for (String next : items) {

//If phone number is not null, there will be only oneelement here.

if (pause) {main.setPausedState(next, true);

}if (resume) {

main.setPausedState(next, false);}

}} finally {

if (main != null) {main.logout();

}}

Appendix B Sample Java Web Client Source Code for Pause/Resume API 67

System.exit(0);} catch (Exception e) {

e.printStackTrace();System.exit(1);

}}

private static String readLine(String prompt) throws IOException{

if (console != null) {return console.readLine(prompt);

} else {System.out.println(prompt);return new BufferedReader(new InputStreamReader

(System.in)).readLine();

}}

private static char[] readPassword(String prompt) throwsIOException {

if (console != null) {return console.readPassword(prompt);

} else {System.out.println(prompt);return new BufferedReader(new InputStreamReader

(System.in)).readLine().toCharArray();

}}

private Main(String server, String user, char[] password) throwsIOException {

myServer = server;myInitialState = new HttpState();Cookie cookie = new Cookie();cookie.setDomain(myServer);cookie.setPath("/");cookie.setName("aaa");cookie.setValue("bbb");myInitialState.addCookie(cookie);myClient = new HttpClient();myClient.setState(myInitialState);myClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_

68 Appendix B Sample Java Web Client Source Code for Pause/Resume API

COMPATIBILITY);login(user, password);

}

private void login(String user, char[] password) throwsIOException {

String uri = String.format("http://%s/callrec/loginAction.do",myServer);

PostMethod login = new PostMethod(uri);login.addParameter("login", user);login.addParameter("password", String.valueOf(password));executeMethod(login, IGNORE_OUTPUT);

}

private void logout() throws IOException {String uri = String.format("http://%s/callrec/logoutprocess",

myServer);GetMethod logout = new GetMethod(uri);executeMethod(logout, IGNORE_OUTPUT);

}

private List<String> list(String phone) throws Exception {String uri = String.format

("http://%s/callrec/restful/couples.xml/", myServer); //single pointof access to RS.

if (phone != null) {uri += "?phoneNumber=" + phone;

}GetMethod list = new GetMethod(uri);return executeMethod(list, new MultiValuedProducer

("/couples/couples/link[@rel='self']/@href"));}

private void setPausedState(String url, boolean isPaused) throwsException {

String pauseResumeUrl = getPauseResumeLink(url);if (pauseResumeUrl == null) {

throw new RuntimeException("A problem prevents you fromcalling pause or resume. Probably, you don't have the sufficientrights to this operation");

}// PostMethod changeState = new PostMethod(pauseResumeUrl);// changeState.addParameter(PAUSED_STATE, String.valueOf

(isPaused));

Appendix B Sample Java Web Client Source Code for Pause/Resume API 69

PutMethod changeState = new PutMethod(pauseResumeUrl);changeState.setRequestEntity(new StringRequestEntity

(String.format(PAUSED_STATE + "=%b", isPaused), "application/x-www-form-urlencoded", "UTF-8"));

executeMethod(changeState, IGNORE_OUTPUT);}

private String getPauseResumeLink(String callUrl) throwsException {

GetMethod details = new GetMethod(callUrl);return executeMethod(details, new SingleValuedProducer

("/couple/link[@rel='pause/resume']/@href"));}

private <T> T executeMethod(HttpMethod method,ResponseProcessor<T> processor) throws IOException {

try {int result = myClient.executeMethod(method);try {

processor.onStatus(result); //let it fail on statuses itdoes not want.

} catch (IOException e) {IGNORE_OUTPUT.read(method.getResponseBodyAsStream());throw new IOException("status=" + result);

}return processor.read(method.getResponseBodyAsStream());

} finally {method.releaseConnection();

}}

private XPath getXpath() {if (myXpath == null) {

myXpath = XPathFactory.newInstance().newXPath();myXpath.setNamespaceContext(new ListNamespaceContext("atom",

"http://www.w3.org/2005/Atom"));}return myXpath;

}

private static final ResponseProcessor<Void> IGNORE_OUTPUT = newResponseProcessor<Void>() {

public void onStatus(int status) throws IOException {if (status > 399) {

70 Appendix B Sample Java Web Client Source Code for Pause/Resume API

throw new IOException("Status=" + status);}

}

@Overridepublic Void read(InputStream body) throws IOException {

try {byte[] ignored = new byte[2048];int len = 0;while (len >= 0) {

len = body.read(ignored);// System.out.print(new String(ignored));

}} finally {

body.close();}return null;

}};

private class MultiValuedProducer implementsResponseProcessor<List<String>> {

private final XPathExpression myExpression;

public MultiValuedProducer(String xpathExpression) throwsXPathExpressionException {

myExpression = getXpath().compile(xpathExpression);}

public void onStatus(int status) throws IOException {if (status > 399) {

throw new IOException("Status=" + status);}

}

@Overridepublic List<String> read(InputStream response) throws

IOException {try {

Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(response);

NodeList items = (NodeList) myExpression.evaluate(doc,XPathConstants.NODESET);

List<String> result = new ArrayList<String>

Appendix B Sample Java Web Client Source Code for Pause/Resume API 71

(items.getLength());for (int i = 0, iMax = items.getLength(); i < iMax; i++) {

result.add(items.item(i).getNodeValue());}return result;

} catch (IOException e) {throw e;

} catch (Exception e) {throw new RuntimeException(e);

}}

}

private class SingleValuedProducer implementsResponseProcessor<String> {

private final MultiValuedProducer myDelegate;

public SingleValuedProducer(String xpathExpression) throwsXPathExpressionException {

myDelegate = new MultiValuedProducer(xpathExpression);}

@Overridepublic void onStatus(int status) throws IOException {

myDelegate.onStatus(status);}

@Overridepublic String read(InputStream response) throws IOException {

List<String> result = myDelegate.read(response);if (result.isEmpty()) {

return null;}return result.get(0);

}}

private static class ListNamespaceContext implementsNamespaceContext {

private Map<String, String> myNamespaces = new HashMap<String,String>();

public ListNamespaceContext(String...context) {if (context == null) {

return; //not particularly useful, but ok.

72 Appendix B Sample Java Web Client Source Code for Pause/Resume API

}if ((context.length % 2) == 1) {

throw new IllegalArgumentException();}for (int i = 0; i < context.length; i += 2) {

myNamespaces.put(context[i], context[i+1]);}

}@Overridepublic String getNamespaceURI(String prefix) {

if ("xml".equals(prefix)) {return XMLConstants.XML_NS_URI;

}String result = myNamespaces.get(prefix);if (result == null) {

return XMLConstants.NULL_NS_URI;}return result;

}@Overridepublic String getPrefix(String namespaceURI) {

throw new UnsupportedOperationException();}@Overridepublic Iterator getPrefixes(String namespaceURI) {

throw new UnsupportedOperationException();}

}}

Appendix B Sample Java Web Client Source Code for Pause/Resume API 73