Примеры программирования Parlay с использованием средств разработки от Appium.
Реальный пример иллюстрирует особенности реализации известного сервиса "виртуальный номер" - звонок на входящий виртуальный номер переадресуется на один из реальных номеров из некоторого списка. Смысл этой услуги состоит в том, что пользователь делает видимым (публикует) для своих партнеров (клиентов) только один реальный номер. А входящие звонки в реальности будут переадресовываться на другие номера (возможно, меняющиеся в динамике).
Почему мы остановились на этом примере, входящем в стандартную поставку Appium? На самом деле этот пример содержит практически полную реализацию достаточно популярной на российском рынке услуги "виртуальный номер" (например, продукт Logic Line компании МТУ-Информ). Обратите внимание на эту строку в коде, содержащую список обзваниваемых номеров (используются четырехзначные номера программного эмулятора):
/** список номеров, используемых при прозвоне */
private String[] addresses = {"1101", "1102", "1103"};
В модельном примере перебираются номера из некоторого фиксированного списка. Собственно, все, что нужно для трансформации этого примера в реальный сервис – это добавить выбор списка перебираемых номеров из базы данных. Это может быть простым программированием с использованием JDBC и в любом случае никак не связано с особенностями телефонной сети. А главное, это объем потребного кода: текст программы занимает меньше 200 строк.
Интересно узнать больше? Пишите: info@abavanet.ru
Ниже приводится текст программы:
package com.company.apps;
// подключение библиотек сервера
import com.appium.Basement.Def.ConfigDescriptor;
import com.appium.Statistics.Statistic;
import com.appium.TAS.SessionContainer.AbstractSessionHandler;
import com.appium.TAS.SessionContainer.StaticSessionHandler;
import com.appium.TAS.SessionContainer.TelecomApplicationMainBase;
/**
* Базовый класс, представляющий собой Telecom Application. Именно этот файл
* запускается в JBuilder
*
*/
public class TelecomApplicationMain extends TelecomApplicationMainBase {
/**
* здесь и создается CallHandler (поддержка звонков)
*/
public AbstractSessionHandler createNewSessionHandler() {
return new CallHandler();
}
/**
* Каждое приложение имеет некоторый идентификатор (их может
* быть более одного). Этот идентификатор представляет собой
* адресацию приложения внутри контейнера.
*
* Приложение также создает набор буферов для связи с контейнером
* Код для этих функций опущен (на практике этот код создается автоматически)
*/
. . .
}
Необходимо отметить, что такой класс будет выглядеть практически идентично
(за исключением, естественно, имени и идентификатора) для всех приложений.
Шаблон для этого файла создается автоматически при создании приложения.
Собственно реализация:
/**
* Copyright owned by Appium AB, 2001
*
*/
package com.company.apps;
// подключение бибилиотек
import com.appium.Parlay.*;
import com.appium.Parlay.cc.*;
import com.appium.Parlay.cc.gccs.*;
import com.appium.Parlay.ui.*;
import com.appium.Parlay.mm.*;
import com.appium.Parlay.mm.us.*;
import com.appium.TAS.SessionContainer.SessionState;
import com.appium.TAS.SessionBase.AsynchronousCallHandlerStateMachine;
/**
* Базовая идея – предлагаемый framework поддерживает некоторую машину
* состояний (или автомат). В соответствии с моделью вызова, описанной в Parlay
* пользовательское приложение задает конкретные реализации (действия,
* которые нужно выполнить) для возникающих состояний.
* Базовый класс есть AsynchronousCallHandlerStateMachine
*
* Вызовы enterState() и setNextState() позволяют организовать переходы между
* состояниями.
*
* В каждом состоянии приложение получает сигналы от сервера (то есть, от
* телефонной части нашего приложения). Здесь они называются callback,
* поскольку это и есть callback в терминах Corba
*
* В случае машины состояний возникает важный вопрос – что есть (точнее что
* делать) в начальном состоянии. То есть с чего все запускается? В модели Appium
* TAS в начальном состоянии приложение должно реализовывать один из
* следующих методов (это то, что вызывается при звонке):
*
* public void handleCall()
* public void createCall(TpAddress address)
* public void createCall(TpAddress from, TpAddress to)
* public void createCall(TpAddress[] addresses, String[] stringParameters, Any[]
* anyParameters)
*/
public class CallHandler extends AsynchronousCallHandlerStateMachine {
// в данном приложении – дозвонка по списку будет два состояния:
// начальное и разговор (когда дозвонились)
/** начальное состояние */
private InitialState initialState = new InitialState();
/** разговор */
private Talking talking = new Talking();
/** список номеров, используемых при прозвоне */
private String[] addresses = {"1101", "1102", "1103"};
/** индек текущего номера в списке номеров */
private int index;
/**
*
* Этот метод вызывается контейнером для получения начального состояния
* Схема следующая: для приложения (которое имеет некоторый ID) вызывается
* метод getInitialState() Это метод возвращает экземпляр объекта,
* ответственного за начальное состояние. И у этого объекта будет вызван
* метод handleCall()
*/
public SessionState getInitialState(String applicationID, String state)
{
if(state.equals("")) {
return initialState;
} else {
return talking;
}
}
/**
* Начальное состояние: звоним по первому адресу из нашего списка и ждем
* реакции. Это описание класса, который отвечает за поддержку начального
* состояния
*/
class InitialState extends AbstractActiveState {
// этот метод вызывается при входе в состояние (после метода start())
public void handleCall() throws Exception {
index = 0;
callControl.routeReq(addresses[0]);
}
// этот метод вызывается при обработке звонка
public void routeRes(TpCallReport eventReport, int callLegSessionID)
throws Exception {
if (eventReport.CallReportType.equals(TpCallReportType.P_CALL_REPORT_ANSWER))
{ // если дозвонились – то переходим в другое состояние
enterState(talking);
}
else { // не дозвонились
// меняем индекс (переходим к следующему номеру)
index++;
if (index < addresses.length) { // еще есть куда звонить
callControl.routeReq(addresses[index]);
} else { // все перебрали. Закрываем приложение
shutdownCallHandler(false);
}
}
}
/**
* этот метод вызывается при ошибках
*/
public void routeErr(TpCallError errorIndication, int callLegSessionID)
throws Exception {
// просто переходим к следующему номеру (если он есть)
// или завершаем приложение
index++;
if (index < addresses.length) {
callControl.routeReq(addresses[index]);
} else {
shutdownCallHandler(false);
}
}
}
/**
* State 2 – состояние разговора. Переходим сюда, если получен ответ на
* звонок. В данном примере нет никаких специальных действий.
*/
public class Talking extends AbstractActiveState {
/**
* инициализационный метод. Запоминает текущее состояние
*/
public void start() {
getSessionContext().setState("talking");
saveSession();
}
/**
* обработка. Ничего не делаем
*/
public void handleCall() {
/* то есть просто ждем окончания */
}
// Этот метод вызывается при ошибках
public void routeRes(TpCallReport eventReport, int callLegSessionID) {
// завершаем приложение
shutdownCallHandler(false);
}
}
}
Здесь routeReq() есть вызов из сервисной оболочки, который в точности
соответствует одноименному вызову из Parlay API.
Для контактов: info@abavanet.ru
© AbavaNet 2004 all rights reserved