пятница, 9 октября 2009 г.

[comp.prog.flame] Более конструктивные мысли после двух суток выкуривания gSOAP

Данную заметку можно рассматривать как логическое продолжение предыдущей. Лыжи таки поехали. Местами я был идиотом, местами пришлось отлаживать gSOAP 2.7.14 для того, чтобы самому найти уже известный баг (который нашли за неделю до меня). Но в итоге все заработало.

Довольно своеобразная штука этот gSOAP. С одной стороны, очень удобно: берешь WSDL, натравливаешь на него wsdl2h, затем soapcpp2 и вуаля – в твоем распоряжении набор классов для обращения к удаленной стороне (ну или для реализации собственного сервиса).

Но с другой стороны… Видно, что gSOAP вырос из C-шной реализации. И уши этого наследия торчат из всех щелей. Взять, например, управление памятью. gSOAP хранит указатели на все динамически созданные в процессе десериализации SOAP-запроса объекты. И, если пользователь не взял их под собственную ответственность, автоматически уничтожает их. Но все это хорошо только для ответов сервера (если говорить о клиенте). А ведь запрос к серверу так же нужно подготовить. И вот тут-то начинается форменное корявство. Например, gSOAP генерирует структуры типа вот такой:

class SOAP_CMAC PPGw__Sms : public PPGw__Request
{
public:
   int dataCodingScheme;
   std::string *fromNumber;
   xsd__base64Binary *header;
   PPGw__PremiumInfo *premiumInfo;
   std::string *protocolIdentifier;
   std::string *reportLevel;
   std::string *toNumber;
   LONG64 validityPeriod;
public:
   ...
            PPGw__Sms() :
         dataCodingScheme(0),
         fromNumber(NULL),
         header(NULL),
         premiumInfo(NULL),
         protocolIdentifier(NULL),
         reportLevel(NULL),
         toNumber(NULL),
         validityPeriod(0) { this->soap_default(NULL); }
   virtual ~PPGw__Sms() { }
};

Можно видеть, что конструктор инициализирует элементы нулями, а деструктор ничего не делает. В это и весь фокус. Либо атрибуту PPGw__Sms назначается значение, время жизни которого определяет программист. Либо же ему назначается значение, время жизни которого будет определять gSOAP. В первом случае (когда мы отвечаем за объекты) код подготовки запроса будет иметь вид:

void
do_send_sms( PPGwBindingProxy & proxy )
   {
      std::cout << "...sending sms..." << std::endl;

      std::string baID = "***";
      std::string text_sms_selector = "TextSms";
      std::string text_of_message = "Just a Test!";
      std::string to_number = "+420***";
      std::string msg_id = "c0465d63-38f0-4f31-93b3-0dc80a063baf";

      PPGw__TextSms text_sms;
      text_sms.text = &text_of_message;
      text_sms.dataCodingScheme = 0;
      text_sms.toNumber = &to_number;
      text_sms.baID = &baID;
      text_sms.msgID = &msg_id;

      PPGw__MessageContainer mc;
      mc.selector = &text_sms_selector;
      mc.textSms = &text_sms;

      PPGw__send request;
      request.mc = &mc;

      PPGw__sendResponse response;

      int error_code = proxy.send( &request, &response );
      if( error_code )
      ...

А во втором случае (чтобы память почистил за нас gSOAP), вот такой:

void
do_send_sms( PPGwBindingProxy & proxy )
   {
      std::cout << "...sending sms..." << std::endl;

      PPGw__TextSms * text_sms = soap_new_PPGw__TextSms( &proxy, -1 );
      (text_sms->text = soap_new_std__string( &proxy, -1 ))->assign( "Just a Test!" );
      text_sms->dataCodingScheme = 0;
      (text_sms->toNumber = soap_new_std__string( &proxy, -1 ))->assign( "+420***" );
      (text_sms->baID = soap_new_std__string( &proxy, -1 ))->assign( "***" );
      (text_sms->msgID = soap_new_std__string( &proxy, -1 ))->assign(
            "c0465d63-38f0-4f31-93b3-0dc80a063baf" );

      PPGw__MessageContainer * mc = soap_new_PPGw__MessageContainer( &proxy, -1 );
      (mc->selector = soap_new_std__string( &proxy, -1 ))->assign( "TextSMS" );
      mc->textSms = text_sms;

      PPGw__send * request = soap_new_PPGw__send( &proxy, -1 );
      request->mc = mc;

      PPGw__sendResponse response;

      int error_code = proxy.send( request, &response );
      if( error_code )

      ...

Как по мне, так и первый, так и второй варианты – это страх и ужас, особенно второй.

Видимо, это из-за того, что и сам gSOAP написан в таком же стиле. Когда я пытался найти причину ошибки, мне приходилось заглядывать в такие функции в дебрях gSOAP, в которых директивы условной компиляции были практически через строчку. Как вся эта хрень еще поддерживается и развивается авторами – фиг знает.

А кроссплатформенных альтернатив gSOAP-у под C++, в общем-то, и нет. Была какая-то реализация SOAP-а от Apache (Apache Axis C++) – но там не видно обновлений с 2005-го года. Еще у разработчиков библиотеки POCO есть какой-то собственный POCO Remoting. Но он сильно дороже gSOAP-а, не умеет (как я понимаю) транслировать WSDL в C++, да и вообще его способности поддерживать именно SOAP слегка под вопросом (поскольку там для SOAP-а есть только некий SoapLite).

В связи с этим появляется соблазн взять и склепать собственный wsdl2cpp, который бы из WSDL генерировал заточенные под POCO или Qt нормальные C++ные классы. И продавать бы такой инструмент эдак по $200 за версию + upgrade на следующую версию за $75. Ей Богу, была бы такая альтернатива gSOAP-у – выбрал бы сейчас ее не задумываясь.

14 комментариев:

Skynin комментирует...

И продавать бы такой инструмент эдак по $200 за версию + upgrade на следующую версию за $75
А зачем мучиться если есть агроменное семейство "Java EE технологий". Отдаженное, от разных производителей, разного уровня масштабируемости и - бесплатно?

eao197 комментирует...

Потому что есть случаи, как наш -- большое C++ приложение, в которое нужно встроить поддержку SOAP-а. Делать симбиоз из C++ и Java (C#, Ruby, Python) -- это не меньший геморрой, чем использовать какой-нибудь gSOAP. Тем более, что такая интеграция будет обходиться увеличением как рантайма, так и дистрибутива.

Небольшая статистика. gSOAP 2.7.14, вышедший 20 дней назад, был загружен с SF более 1500 раз. Предшествующая версия 2.7.13 -- более 13000 раз. Это, конечно, не показатели Boost-а, но для очень специфической вещи весьма не плохо. Т.е. спрос есть. И можно было бы им воспользоваться.

Skynin комментирует...

Мыслей опосля и был такой вариант плюс - если встравиваемому приложению нужно работать с SOAP сервисом, а сделать прокси, переводящий в более удобный формат для обработки (по аналогии Opera Mini) нет возможности.

Насчет заработка на таком ПО... я пессимист, а это субъективно :)

eao197 комментирует...

Насчет заработка на таком ПО... я пессимист, а это субъективно :)

В последние годы мне кажется, что зарабатывать на средствах разработки вообще очень тяжело. OpenSource здесь рулит неимоверно. Бесплатные компиляторы, бысплатные библиотеки, бесплатные IDE -- заработать здесь можно, имхо, только на чем-то очень специализированном.

Skynin комментирует...

OpenSource здесь рулит неимоверно. Бесплатные ...
Вот и мне так кажется ;)
В первом постинге я невнятно и скомкал два вопроса - 1. выбор технологии для решения задач (сам бооольшой скептик в отношении нахлобучивания технологий, "а поверх http наше руби приложение на расширенной нашей натив библиотекой VM будет обращаться к эрланг серверу, который дергает сервлеты на jetty а те принимают решение, писать в SQL или в Berkley DB") 2. возможность заработать на инструменте.

только на чем-то очень специализированном.
Оно и стоить тогда должно не х00 у.е.

Проблема заработать на универсальном ПО (то есть для абстрактного заказчика) - сама универсальна:

Пристанище недовольных
С деньгами в AppStore ситуация также печальна. ...

eao197 комментирует...

сам бооольшой скептик в отношении нахлобучивания технологий, "а поверх http наше руби приложение на расширенной нашей натив библиотекой VM будет обращаться к эрланг серверу, который дергает сервлеты на jetty а те принимают решение, писать в SQL или в Berkley DB"

С таким нахлобучиванием регулярно вылазят проблемы на каких-нибудь нестандартных ситуациях. Которые почему-то для тебя становятся штатными... Но это уже совсем офтопик :)

только на чем-то очень специализированном.
Оно и стоить тогда должно не х00 у.е.


Цифра в $200 появилась просто из-за того, что коммерческая лицензия на gSOAP стоит $195 :)
Т.е. как бы смысл был в том, чтобы продавать за те же деньги, но с более удобной ручкой :))

Dmitry Vyukov комментирует...

Re: А кроссплатформенных альтернатив gSOAP-у под C++, в общем-то, и нет. Была какая-то реализация SOAP-а от Apache (Apache Axis C++) – но там не видно обновлений с 2005-го года.

Ошибся всего одной цифрой ;)
http://ws.apache.org/axis2

eao197 комментирует...

2Dmitry Vyukov: а у тебя есть опыт его использования? Например, я не увидел, может ли он из WSDL заглушки клиента/сервера генерировать?

eao197 комментирует...

Разобрался: для генерации кода по WSDL, нужно ставить Axis2/Java -- там есть соответствующий инструмент.

Редиски. Мало того, что это чистый C, так еще и Java нужна для разработки. gSOAP круче! :)

Unknown комментирует...
Этот комментарий был удален автором.
Unknown комментирует...

http://www.prog.org.ru/topic_16542_0.html

"WSDL, SOAP, Web-Services + Qt (обзор, обсуждение, сбор ссылок)"

для Qt есть 2 спец-заточенных инструмента - KdSoap и FEAST. первый бесплатный для GPL проектов, второй платный совсем.

Но я склюняюсь все-же к мысли про gSoap - в принципе для работы с STL-типами в Qt есть методы конвертации, а во вторых - вроде как gSoap можно настроить на собственный набор типов/контейнеров... не разбирался пока как именно правда...

ну и у меня с gSoap втоде все начало взлетать. единственное gSoap не умеет хорошо работать с русскими буквами в именах полей... но пока это терпимо.

eao197 комментирует...

@Denjs:

спасибо за ссылки. Мы у себя gSOAP уже в нескольких проектах применили. Если привыкнуть, то нормально.

А с русскими буквами в WSDL-ях пока не сталкивались :)

Unknown комментирует...

>>А с русскими буквами в WSDL-ях пока не сталкивались :)

в вы вебсервисы от 1Це поюзайте ;)
там и имена полей русские "и вообще"))))

eao197 комментирует...

>вы вебсервисы от 1Це поюзайте ;)

Не-не-не! :)