FirebirdSQL logo

Реализация модуля плагина

Плагины активно взаимодействуют со специальным компонентом Firebird,называемым диспетчером плагинов. В частности, менеджер плагинов должензнать, какие модули плагина были загружены и должен быть уведомлен, еслиоперационная система пытается выгрузить один из этих модулей без явнойкоманды диспетчера плагина (это может произойти прежде всего прииспользовании встроенного сервера (embedded) — когда в программевызывается exit() или основная библиотека Firebird fbclientвыгружается). Основная задача интерфейса IPluginModule — этоуведомление. Прежде всего, нужно решить — как определить, что модульбудет выгружен? Когда динамическая библиотека выгружается по какой-либопричине, выполняется множество зависимых от ОС действий, и некоторые изэтих действий могут использоваться для обнаружения этого факта впрограмме. При написании плагинов, распространяемых вместе с firebird,мы всегда используем вызов деструктора глобальной переменной. Большой«плюс» этого метода заключается в том, что он независим от ОС (хотячто-то вроде функции exit(), возможно, также успешно используется). Ноиспользование деструктора позволяет легко сконцентрировать почти все,что связано с обнаружением выгрузки в одном классе, реализуя в то жевремя интерфейс IPluginModule.

Минимальная реализация выглядит следующим образом:

class PluginModule : public IPluginModuleImpl<PluginModule, CheckStatusWrapper>
{

private:
  IPluginManager* pluginManager;

public:
  PluginModule()
    : pluginManager(NULL)
  { }


  ~PluginModule()
  {
    if (pluginManager)
    {
      pluginManager->unregisterModule(this);
      doClean();
    }
  }

  void registerMe(IPluginManager* m)
  {
    pluginManager = m;
    pluginManager->registerModule(this);
  }

  void doClean()
  {
    pluginManager = NULL;
  }

};

Единственным членом данных является интерфейс диспетчера плагиновIPluginManager. Он передаетсяфункции registerModule() и сохраняется в приватной переменной, в то жевремя модуль регистрируется в диспетчере плагинов методом callModule() ссобственным адресом в качестве единственного параметра. ПеременнаяpluginManager не только сохраняет указатель на интерфейс, ноодновременно служит в качестве флага, что модуль зарегистрирован. Когдавызывается деструктор зарегистрированного модуля, он уведомляетдиспетчер плагинов о неожиданной выгрузке с помощью вызоваunregisterModule(), передающим указатель на себя. Когда диспетчерплагинов будет регулярно выгружать модуль, то в первую очередь вызовметода doClean() меняет состояние модуля на незарегистрированное, и этопозволяет избежать вызова unregisterModule(), когда ОС выполняетфактическую выгрузку.

Реализовав интерфейс плагина IPluginModule, мы встретились с первыминтерфейсом, необходимым для реализации плагинов — IPluginManager. Онбудет активно использоваться позже, остальные члены этого класса вряд липотребуются вам после копирования в вашу программу. Просто не забудьтеобъявить глобальную переменную этого типа и вызвать функцию registerMe()из FB_PLUGIN_ENTRY_POINT.

Основной интерфейс любого плагина

Приступим к реализации самого плагина. Тип основного интерфейса зависитот типа плагина, что очевидно, но все они основаны на общем интерфейсеIPluginBase с подсчётом ссылок, который выполняет общие для всехплагинов (и очень простые) задачи. Каждый плагин имеет некоторый (тоже сподсчётом ссылок) объект, которому принадлежит этот плагин. Чтобывыполнять интеллектуальное управление жизненным циклом плагинов, любойплагин должен иметь возможность хранить информацию о владельце исообщать её диспетчеру плагинов по запросу. Это означает, что каждыйплагин должен реализовывать два тривиальных метода setOwner() иgetOwner(), содержащиеся в интерфейсе IPluginBase. Зависимые от типаплагина методы, безусловно, более интересны — они обсуждаются в частиописания интерфейсов.

Давайте рассмотрим типичную часть реализации любого плагина (здесь яспециально использую несуществующий тип SomePlugin):

class MyPlugin : public ISomePluginImpl<MyPlugin, CheckStatusWrapper>
{
public:
  explicit MyPlugin(IPluginConfig* cnf) throw()
     : config(cnf), refCounter(0), owner(NULL)
  {
    config->addRef();
  }
  ...

Конструктор получает в качестве параметра интерфейс конфигурацииплагина. Если вы собираетесь конфигурировать плагин каким-то образом, торекомендуется сохранить этот интерфейс в вашем плагине и использоватьего позже. Это позволит вам использовать общий стиль конфигурацииFirebird, позволяя пользователям иметь привычную конфигурацию и свести кминимуму написание кода. Конечно, при сохранении какого-либо ссылочногоинтерфейса лучше не забывать добавлять ссылку на него. Также не забудьтеустановить счетчик ссылок в 0 и владельца плагина в NULL.

  ~MyPlugin()
  {
    config->release();
  }

Деструктор освобождает конфигурационный интерфейс. Обратите внимание: мыне меняем счетчик ссылок нашего владельца, потому что он принадлежитнам, а не мы принадлежим ему.

  // IRefCounted implementation
  int release()
  {
    if (--refCounter == 0)
    {
      delete this;
      return 0;
    }
    return 1;
  }


  void addRef()
  {
    ++refCounter;
  }

Абсолютно типичная реализация объекта с подсчётом ссылок.

  // IPluginBase implementation
  void setOwner(IReferenceCounted* o)
  {
    owner = o;
  }

  IReferenceCounted* getOwner()
  {
    return owner;
  }

Как и было обещано, реализация IPluginBase тривиальна.

  // ISomePlugin implementation
  // … here go various methods required for particular plugin type
private:
  IPluginConfig* config;
  FbSampleAtomic refCounter;
  IReferenceCounted* owner;
};

В этом примере формальная часть реализации основного интерфейса плагиназавершена. После добавления специфичных для типа методов (и, возможно,написания кода, чтобы сделать их полезным), интерфейс готов.