Say you have multiple run-time -packages as plugins.

Say you have multiple run-time -packages as plugins.
What is the best way you know, once the plugin is loaded, to deal with adding all the plugins in the package?

In other words:

- PM: Plugin Manager
- P1: Package 1 containing Plugin1..PluginN
- P2: Package 2 containing PluginN+1..M

How do you automate the addition of plugins in a transparent way?

Any good pointers?

Thanks!

A

Comments

  1. How does your plugin, when in process of being loaded/unloaded, provides/removes its specific API entry points to the system ?

    ReplyDelete
  2. That is part of my problem. Right now there is no such thing. My thinking was to do this in a sort of dependency inversion: instead of having the plugin manager "finding" the plugins have the plugins finding the manager and use it to self-register.
    If I were to go with this, however, I am not sure what the best hooking point would be. Keep in mind that there may well be several dozens plugins spread over a few packages.

    However, the question is kept pretty open because I am seeking ideas on how to best do this.

    A

    ReplyDelete
  3. Have a RegisterPlugin procedure that is called for each plugin's class and which stores these classes in a TClassList (or similar). This call can be done from the unit's initialization section. The plugin is then instantiated on demand from that list. For this to work you must inherit all plugins from a common ancestor that has a virtual constructor.
    Or, since you already have a plugin manager, each plugin could be automatically instantiated and that instance registered with the plugin manager. This would remove the requirement of a common ancestor and a virtual constructor on the cost that each plugin is always instantiated.
    Of course there are alternatives: E.g. instead of registering classes you could register plain functions that create the plugins.
    And of course you could work with interfaces instead of instances.

    ReplyDelete
  4. I think there were RegisterClass and UnregisterClass RTL functions to avoid reinventing.

    Personally i'd stick with interfaces for ARC reasons. It easily provides you a counter of instances pointers, so you can easily tell when a plugin is safe to be unloaded. I am reworking quite legacy dll-based app, so some modules use their global vars for per-task initializations and can not be reused without unloading.

    ReplyDelete
  5. If you want to have totally unpredictable dynamic number of plugins, then you would have to go VideoLAN/GIMP way: design some folder(s) and filename mask to be plugins, then to track a flat database ( plugins cache ). On start your app should scan the folder and match it with the cache, removing the obsolete entries and running updated/new plugins so they would (re)introduce themselves, allowing you to update the cache

    ReplyDelete
  6. What about entry points, i still did not automized it. APIs provided are much different, even at the point of being single API implementation/plugin ( like projects list ) or multiple ones ( document export engines to different formats ).

    Generic "conditions matcher" seems to be overengineering for me, so each API declaration unit has its own entrypoint(s) that plugins anchor into. As well as GetXXXXService(): iXXXXXX; function that routes to the plugins loader manager to ask it for GUID to fetch.

    Not elegant, i wish i could in Delphi make a single point function xxx(var impl: Interface), that would accept different i-typesand would allow in runtime check the GUID of the variable, but language does not provide for it.

    ReplyDelete
  7. OK - I think I found the perfect solution.

    I have a "Core" package on which all the other depend. For a variety of reasons, I needed this anyway, so I am not over-engineering for the sake of it.

    In this package I have a class that is basically a plugin list (it's more complicated, but this is the gist) and it has a class method to register the plugin class. From the host application I access this, but when each package loads its various initialization sections kick in registering the plugins.

    It works wonderfully and all I need to do is to load them. Sweet.

    ReplyDelete
  8. If you would not ever unload the already loaded package - then okay. Otherwise I suggest you to arrange your services as ARC interfaces, not PODO class instances.

    ReplyDelete
  9. You may even have something like

    TPluginServiceClass = class abstract
    public
    class function GetImpl: TPluginServiceClass;

    And that function would check if the specific class was already loaded and if not - locate and load the plugin bpl.

    That said, you would still have to decide where would you hold "class names <-> file name" mapping, when the plugin is not loaded yet.

    In the most trivial case, you can have it in the core package.

    in the API description of Core Package

    TPluginServiceClass = class abstract
    public
    class function GetImpl: TPluginServiceClass;
    protected
    class procedure RegisterThisClass;
    function BPLFileName:string; abstract;
    ...
    end;

    procedure TPluginServiceClass.RegisterThisClass;
    begin
    // RegisterClass(Self);
    MyPluginsClassesList.Add(Self);
    end;

    ------

    in the AAA service interface in Core package

    TPluginServiceAAAClass = class (TPluginServiceClass) abstract
    protected
    function BPLFileName:string; override;
    ...
    end;

    ---------

    In the plugin BPL initialization section

    TPluginServiceAAAImpl = class(TPluginServiceAAAClass)
    ....
    end;

    begin
    TPluginServiceAAAImpl.RegisterService;
    end.

    ReplyDelete
  10. But if you want to be able to change/add BPL filenames without recompiling core, then you would need some DBF caching or other indireciton level added

    ReplyDelete
  11. Arioch The you are so far away from an optimal solution, in my view. What I have done is quite simple:

    In Core:

    TPluginDirectory = class( TPersistent )
    private
    function GetPlugIn(Index: Integer): IPlugin;
    function GetPluginCount: Integer;
    public
    constructor Create;
    destructor Destroy; override;

    property PluginCount : Integer read GetPluginCount;
    property PlugIn[ Index : Integer ] : IPlugin read GetPlugIn;
    class procedure RegisterPlugin( Aclass: TBasePluginClass );
    end;

    function PluginDirectory: TPluginDirectory;

    In the plugins:

    initialization
    begin
    TPluginDirectory.RegisterPlugin( TPlugin );
    end;

    In the host app:

    PluginHandle := LoadPackage( APluginName );
    FHandleList.Add( PluginHandle,APluginName );
    if PluginHandle = INVALID_HANDLE_VALUE then
    raise Exception.Create('Plugin not found!');
    PluginCount := PluginDirectory.PluginCount;
    for I := 0 to PluginCount -1 do
    begin
    Plugin := PluginDirectory.PlugIn[ I ];
    if FPluginList.IndexOf( Plugin ) < 0 then
    begin
    FPluginList.Add( Plugin );
    end;
    end;

    There can't possibly be any doubt on what the code does. The IndexOf takes care of making sure a plugin isn't duplicated, then I work off the list of interfaces. Ain't touching the packages any more.

    ReplyDelete

Post a Comment