пятница, 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 комментариев:

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

    ОтветитьУдалить
  10. Этот комментарий был удален автором.

    ОтветитьУдалить
  11. 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 не умеет хорошо работать с русскими буквами в именах полей... но пока это терпимо.

    ОтветитьУдалить
  12. @Denjs:

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

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

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

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

    ОтветитьУдалить
  14. >вы вебсервисы от 1Це поюзайте ;)

    Не-не-не! :)

    ОтветитьУдалить