Attention: This document is unfinished!
In the course of previous discussions and workshops (link) the CTK group agreed on providing a general plugin/component system. This system should allow for loose coupling between plugins and should provide well designed means of exchanging functionality and data. Further, it should not a priori impose restrictions on the plugins, such that it will be easy to embed existing plugin-infrastructures from existing toolkits/partners.
Purpose of this Document
This document serves as a software design document for the proposed CTK Plugin Framework. CTK members are welcome to add their comments directly on this page or post to email@example.com.
Scope of this Document
This document describes the architecture of the proposed CTK Plugin Framework and explains the design decisions taken. It also points out features which may be important in the future.
The intended audience are CTK developers, and to a certain degree CTK users which want to gain more insight into the framework.
- Plugin meta-data
- A well-defined plugin lifecycle and context
- Integrated service discovery and registration
The plugin framework is developed as a CTK Library (code).
Documentation created from the code can be found here:
- Doxygen generated documentation for the public API
- Doxygen generated documentation for public and internal classes
Bleeding edge development concerning the plugin framework happens here. Newly developed features will be merged into the main CTK git repository when they are ready.
The following points should be considered when designing a plugin system for CTK (please comment and/or add points!):
- The system must not impose functional restrictions on plugins.
- Plugins should communicate via well-defined means (services, interfaces, etc.).
- Dependencies between plugins must be handled.
- Plugins should be loadable at runtime
Before writing any new library/framework, it is beneficial to closely look at already existing solutions. One well established modular system for Java applications is specified by the OSGi group. Other, more complex solutions like CORBA look interesting at first, but impose too much overhead and add a lot of complexity for the target audience of CTK (in my opinion). On the other hand, Qt Creator uses an elegant, yet simple approach to achieve extensibility.
Other solutions developed independently by members of the CTK consortium handle extensibility very well in the context of their respective applications. However, they usually require the implementation of a specialized interface which may not be suitable in a general context (please add comments or corrections!).
Therefore, general plugin systems (frameworks) like OSGi and to a certain degree the Qt Creator extension system seem most suitable as guidelines. They add meta data to plugins (Java archives or shared libraries) by using embedded text files (for example a .pluginspec file), which may provide the following information about a plugin:
Further, communication between plugins is done by declaring interfaces or providing services rather than using implementation classes directly. The OSGi specifications provide a central service registry which can be used by all plugins to register or discover services. Qt Creator uses a common QObject pool to make implementations for certain interfaces available.
Due to the more general approach of the OSGi module system and its proven reliability and extensibility in large scale and distributed applications (Apache Sling, Eclipse, GlassFish, JBoss, NetBeans, etc.) the design of the proposed CTK plugin framework closely follows those specifications where it makes sense. Hence the system will benefit from ten years of experience gained in the OSGi community and it will be relatively easy for OSGi developers to understand the API of the CTK plugin framework.
The plugin system must also be designed to be able to handle a large amount of plugins. Loading a lot of (potentially) large shared libraries which may execute arbitrary code at load time can impact the initial start of an application drastically. Loading plugins on demand (lazy loading) reduces the start time. However, the system then needs to be able to access the plugin meta-data without loading the plugin itself.
The architecture of the proposed plugin framework can be broken up in two main components, the Plugin System itself and the Service Registry. However, those two components are related to each other and their combination at the API level results in a comprehensive and flexible system.
The CTK Core already depends on the QtCore library, hence the CTK plugin framework will be based on the Qt Plugin System. The Qt API allows for loading and unloading of plugins at runtime, however, this capability needs to be augmented to enable transparent lazy loading and resolving of dependencies. The meta-data for a plugin could either be distributed as a textfile together with the plugin or be compiled into it. The proposed plugin framework favors the second solution because it avoids the problem of distributing multiple files by using the Qt Resource System. This system makes it easy to embed arbitrary data in a shared library and extract it via a filesystem like API. However, the Qt resources can only be accessed if the plugin is loaded into memory, hence the meta-data should be cached to avoid application load time issues.
Additionally, the plugin framework should directly support the use of services via a central registry.
The Qt Mobility project recently released a Service Framework API as a Qt solution. This service framework allows for declarative services and loads the service implementations on demand. To also enable dynamic (non-persistent) services, the Qt Mobility service framework should be used together with a service registry similar to the one described in the OSGi Core Specifications. Using the Qt API will also enable future out-of-process services (a release for an out-of-process service framework is scheduled for H2 2010 by Nokia).
Memory Management and Binary Compatibility
Due to the direct use of Qt classes in the proposed plugin framework, the Qt architecture and coding style should be used. In detail, this means making use of private implementations to ensure binary compatibility once the API is stable. Further, memory management should be done by using the Qt object hierarchy, smart pointers, or implicit sharing. In the case of using the Qt object hierarchy or raw pointers (if the classes don't inherit from QObject), the class member documentation must clearly state if the caller is responsible for any allocated memory.
The proposed CTK plugin framework is designed quite analogous to the OSGi specifications. For application developers, the main entry point is the PluginFrameworkFactory. Plugin developers need to understand the Plugin* classes as well as the ServiceRegistry and its related classes.
The PluginFrameworkFactory is the main entry point for CTK application developers who want to make use of the plugin framework. It can be used to create a PluginFramework instance which derives from the Plugin class (see next section). In this sense, the framework itself is a plugin, known as the System Plugin.
The PluginActivator and PluginContext
The PluginActivator is a Qt interface which must be implemented by each plugin. It is used to customize the starting and stopping of the plugin and to get access to the PluginContext.
The PluginContext is the execution context for a plugin within the framework. The context allows a plugin to
- Subscribe to events published by the plugin framework
- Register service objects
- Discover and retrieve service objects
- Install new plugins
- Get a list of installed plugins
- Get the Plugin object for a plugin
Having such a well defined interface to the framework API for each plugin allows the framework adapting its actions depending on the calling context. In the future this would also allow the implementation of a security model restricting the access to certain framework objects, depending on the access rights of the calling plugin.
To be written.
The Plugin Meta-Data Cache
As described in Architectural Strategies / Plugin System the meta data for a plugin needs to be cached when the data is embedded in the plugin itself. The proposed plugin framework uses a SQLite database via the Qt API to store a modification time stamp for each plugin. When a plugin is installed the first time, it will be loaded and the modification time and its meta data will be added to the database. Additionally, all Qt resources which are accessible under a plugin specific prefix (the plugin symbolic name) are extracted and inserted into the database. Subsequent installations of the same plugin will only load the shared library if the modification time stamp differs.
The meta data can be later on be accessed by using the "getHeaders()" and "getResource*()" methods of the Plugin class.