Это копия того что я написал в симовскую рассылку, кто ее читает, тот здесь нового не прочтет
Я закончил писать новый базовый объект для реализации iq запросов в Jabber-плагине Sim-IM'а. Хочу на эту тему сказать несколько слов, а потом получить комментарии...
ИСХОДНЫЕ ЗАДАЧИ
Нюанс обработки входящих iq-запросов заключается в том, что в симе XML-парсер интегрирован в объект который отвечает за работу с запросом. Проблема в том, что когда приходит первый тег запроса (не
путать с ответом на запрос), еще непонятно, что именно это за запрос будет. Это может быть станет ясно только со вторым или более поздним тегом... А принимать это все должен таки какой-то объект, который
должен помнить от кого запрос пришел, и все такое. В имеющейся версии Sim-IM'а это решалось путем создания одного мега-объекта JabberClient::IqRequest отвечающего за прием входящих iq-запросов.
Это приводило к двум следующим проблемам:
Во-первых мега-объект JabberClient::IqRequest и так уже не являющийся примером ясности, при попытке добавить в него обработку запросов, необходимых для реализации передачи файлов, явно превратится в совершенно неудобоваримую кучу кода, в которой и автор ногу сломит, не то что вновь пришедший разработчик.
Во-вторых вынесение обработки входящих запросов в отдельный объект приводит к тому, что фактически в коде есть два места, отвечающие за запрос jabber:iq:version. JabberClient::IqRequest и JabberClient::versionInfo. И они как два мента один умеет читать другой писать... То есть один получает и отвечает, а другой отправляет и ждет ответа... Конечно, если знаешь об этой двойственности, то жить можно. Но я ее осознал далеко не сразу... = непонятность кода и сложность работы с ним.
ЧТО В СВЯЗИ С ЭТИМ БЫЛО СДЕЛАНО.
1. Во-первых как предлагается бороться с двойственностью. Для этого создан объект JabberClient::Iq, который умеет работать в обоих направлениях, как [получить запрос]-[отправить ответ], так и [послать
запрос]-[получить ответ]
В каком направлении будет происходить работа, сообщается при создании объекта(и попадает в m_direction)
Внутри объекта есть два подкласса, которые отвечают за данные запроса и данные ответа.
GenericRequest* m_request;
GenericResponse* m_response;
В зависимости от значения m_direction элементы приходящие в парсер направляются либо одному либо другому объекту...
И m_request и m_response умеют работать и на получение и на отправку.
Для получения в нем необходимо реализовать методы element_start, element_end, char_data, которые умеют ловить входящие теги и проч. XML для отправки, реализовать метод AsString, который на основании данных
в классе умеют выдавать строку с XML-кодом iq-пакета.
По получению пакета целиком, вызывается функция OnReceive, переопределив которую наследники могут предпринять какие-то действия, при получении пакета. Например послать ответ.
Собственно говоря, для того чтобы создать обработчик нового iq-запроса надо создать наследников объектов JabberClient::Iq JabberClient::Iq::GenericRequest и JabberClient::Iq::GenericResponse, и переоприделить перечисленные выше функции... Но этого не достаточно...
читаем дальше...
2. Как бороться с тем, что мы не знаем какой запрос нам идет на вход:
Для этого создан специальный наследник JabberClient::Iq JabberClient::UnknownIq, который по мере получения на вход тегов во-первых умеет сообщать внешней программе о том, что он теперь знает что это за запрос (метод CanPromote) и во-вторых умеет создать новый объект который по должен этот запрос получать, добавив в него всю уже полученную информацию (метод Promote)
Соответственно, функция отвечающая за раздачу приходящий из сокета тегов по подходящим объектам, должна в случае если приходит входящий "get" или "set" запрос, создать объект JabberClient::UnknownIq,
скармливать все входящие теги ему, и после скармливания каждого тега, проверять, не определился ли этот запрос со своим типом (CanPromote()). Ежели вдруг определился, то тогда надо создать новый подобающий объект при помощи Promote() и дальше скармливать теги ему, а исходный UnknownIq -- убить.
Соответственно, при добавлении в систему обработчика нового запроса, нужно поправить методы element_start и Promote класса JabberClient::UnknownIq, так чтобы они умели распознать и создать вновь
добавленный объект
Далее JabberClient::UnknownIq (а точнее вего m_request) надо будет добавить обработчик OnResieve, чтобы если он так и не определился со своим типом к последнему тегу, то послать удаленному клиенту error,
типа not-implemented или какой там правильно. Сейчас у нас сим непонятные запросы глотает молча... Кажется...
ЧТО ЕЩЕ
Еще на базе этого всего реализован класс JabberClient::VersionInfoIq, который умеет ловить jabber:iq:version запросы и отвечать на них.
Отправлять сам такие запросы и ждать ответа он еще не умеет. Научу позже... Пока это до сих пор делает JabberClient::VersionInfoIq
ПОПУТНО РЕШЕННЫЕ ЗАДАЧИ
Я сторонник максимальной декомпозиции... Если можно отделить формирование пакета от процесса его доставки, то я считаю это нужно сделать, благо пакеты у нас небольшие, и в памяти легко помещаются.
Соответственно есть метод AsString который умеет делать текст пакета, и Send который знает куда текст запихивать для отправки. Я считаю что это упростит дальнейшую работу с кодом.
ЧЕГО Я ХОЧУ
Хочу я комментариев, старших, и не старших товарищей. Как по сути так и по мелочам. Что касается по мелочам: может быть какие-то названия надо поменять на более подходящие, может быть я накосячил с разделением функциональности на protected и public зоны, я это не очень умею, еще я не совсем понимаю из каких соображений одни идентификаторы пишут с маленькой буквы, а другие с большой, и если я тут как-то не соблюл традицию, то тоже было бы интересно об этом узнать...
Собственно написанный новый код живет в файлах
http://svn.berlios.de/viewcvs/sim-im/branches/playground/jabber_filetransfer/jabber/iq.cpp?view=markup
http://svn.berlios.de/viewcvs/sim-im/branches/playground/jabber_filetransfer/jabber/iq.h?view=markup
и вот такие изменения были сделаны в остальных файлах, чтобы оно стало
использовать новые объекты:
http://svn.berlios.de/viewcvs/sim-im/branches/playground/jabber_filetransfer/jabber/jabberclient.h?rev=2061&r1=2099&r2=2061
http://svn.berlios.de/viewcvs/sim-im/branches/playground/jabber_filetransfer/jabber/jabberclient.cpp?rev=2061&r1=2099&r2=2061
http://svn.berlios.de/viewcvs/sim-im/branches/playground/jabber_filetransfer/jabber/jabber_rosters.cpp?rev=2061&r1=2099&r2=2061
Я закончил писать новый базовый объект для реализации iq запросов в Jabber-плагине Sim-IM'а. Хочу на эту тему сказать несколько слов, а потом получить комментарии...
ИСХОДНЫЕ ЗАДАЧИ
Нюанс обработки входящих iq-запросов заключается в том, что в симе XML-парсер интегрирован в объект который отвечает за работу с запросом. Проблема в том, что когда приходит первый тег запроса (не
путать с ответом на запрос), еще непонятно, что именно это за запрос будет. Это может быть станет ясно только со вторым или более поздним тегом... А принимать это все должен таки какой-то объект, который
должен помнить от кого запрос пришел, и все такое. В имеющейся версии Sim-IM'а это решалось путем создания одного мега-объекта JabberClient::IqRequest отвечающего за прием входящих iq-запросов.
Это приводило к двум следующим проблемам:
Во-первых мега-объект JabberClient::IqRequest и так уже не являющийся примером ясности, при попытке добавить в него обработку запросов, необходимых для реализации передачи файлов, явно превратится в совершенно неудобоваримую кучу кода, в которой и автор ногу сломит, не то что вновь пришедший разработчик.
Во-вторых вынесение обработки входящих запросов в отдельный объект приводит к тому, что фактически в коде есть два места, отвечающие за запрос jabber:iq:version. JabberClient::IqRequest и JabberClient::versionInfo. И они как два мента один умеет читать другой писать... То есть один получает и отвечает, а другой отправляет и ждет ответа... Конечно, если знаешь об этой двойственности, то жить можно. Но я ее осознал далеко не сразу... = непонятность кода и сложность работы с ним.
ЧТО В СВЯЗИ С ЭТИМ БЫЛО СДЕЛАНО.
1. Во-первых как предлагается бороться с двойственностью. Для этого создан объект JabberClient::Iq, который умеет работать в обоих направлениях, как [получить запрос]-[отправить ответ], так и [послать
запрос]-[получить ответ]
В каком направлении будет происходить работа, сообщается при создании объекта(и попадает в m_direction)
Внутри объекта есть два подкласса, которые отвечают за данные запроса и данные ответа.
GenericRequest* m_request;
GenericResponse* m_response;
В зависимости от значения m_direction элементы приходящие в парсер направляются либо одному либо другому объекту...
И m_request и m_response умеют работать и на получение и на отправку.
Для получения в нем необходимо реализовать методы element_start, element_end, char_data, которые умеют ловить входящие теги и проч. XML для отправки, реализовать метод AsString, который на основании данных
в классе умеют выдавать строку с XML-кодом iq-пакета.
По получению пакета целиком, вызывается функция OnReceive, переопределив которую наследники могут предпринять какие-то действия, при получении пакета. Например послать ответ.
Собственно говоря, для того чтобы создать обработчик нового iq-запроса надо создать наследников объектов JabberClient::Iq JabberClient::Iq::GenericRequest и JabberClient::Iq::GenericResponse, и переоприделить перечисленные выше функции... Но этого не достаточно...
читаем дальше...
2. Как бороться с тем, что мы не знаем какой запрос нам идет на вход:
Для этого создан специальный наследник JabberClient::Iq JabberClient::UnknownIq, который по мере получения на вход тегов во-первых умеет сообщать внешней программе о том, что он теперь знает что это за запрос (метод CanPromote) и во-вторых умеет создать новый объект который по должен этот запрос получать, добавив в него всю уже полученную информацию (метод Promote)
Соответственно, функция отвечающая за раздачу приходящий из сокета тегов по подходящим объектам, должна в случае если приходит входящий "get" или "set" запрос, создать объект JabberClient::UnknownIq,
скармливать все входящие теги ему, и после скармливания каждого тега, проверять, не определился ли этот запрос со своим типом (CanPromote()). Ежели вдруг определился, то тогда надо создать новый подобающий объект при помощи Promote() и дальше скармливать теги ему, а исходный UnknownIq -- убить.
Соответственно, при добавлении в систему обработчика нового запроса, нужно поправить методы element_start и Promote класса JabberClient::UnknownIq, так чтобы они умели распознать и создать вновь
добавленный объект
Далее JabberClient::UnknownIq (а точнее вего m_request) надо будет добавить обработчик OnResieve, чтобы если он так и не определился со своим типом к последнему тегу, то послать удаленному клиенту error,
типа not-implemented или какой там правильно. Сейчас у нас сим непонятные запросы глотает молча... Кажется...
ЧТО ЕЩЕ
Еще на базе этого всего реализован класс JabberClient::VersionInfoIq, который умеет ловить jabber:iq:version запросы и отвечать на них.
Отправлять сам такие запросы и ждать ответа он еще не умеет. Научу позже... Пока это до сих пор делает JabberClient::VersionInfoIq
ПОПУТНО РЕШЕННЫЕ ЗАДАЧИ
Я сторонник максимальной декомпозиции... Если можно отделить формирование пакета от процесса его доставки, то я считаю это нужно сделать, благо пакеты у нас небольшие, и в памяти легко помещаются.
Соответственно есть метод AsString который умеет делать текст пакета, и Send который знает куда текст запихивать для отправки. Я считаю что это упростит дальнейшую работу с кодом.
ЧЕГО Я ХОЧУ
Хочу я комментариев, старших, и не старших товарищей. Как по сути так и по мелочам. Что касается по мелочам: может быть какие-то названия надо поменять на более подходящие, может быть я накосячил с разделением функциональности на protected и public зоны, я это не очень умею, еще я не совсем понимаю из каких соображений одни идентификаторы пишут с маленькой буквы, а другие с большой, и если я тут как-то не соблюл традицию, то тоже было бы интересно об этом узнать...
Собственно написанный новый код живет в файлах
http://svn.berlios.de/viewcvs/sim-im/branches/playground/jabber_filetransfer/jabber/iq.cpp?view=markup
http://svn.berlios.de/viewcvs/sim-im/branches/playground/jabber_filetransfer/jabber/iq.h?view=markup
и вот такие изменения были сделаны в остальных файлах, чтобы оно стало
использовать новые объекты:
http://svn.berlios.de/viewcvs/sim-im/branches/playground/jabber_filetransfer/jabber/jabberclient.h?rev=2061&r1=2099&r2=2061
http://svn.berlios.de/viewcvs/sim-im/branches/playground/jabber_filetransfer/jabber/jabberclient.cpp?rev=2061&r1=2099&r2=2061
http://svn.berlios.de/viewcvs/sim-im/branches/playground/jabber_filetransfer/jabber/jabber_rosters.cpp?rev=2061&r1=2099&r2=2061