tufao
1.3.0
An asynchronous web framework for C++ built on top of Qt
|
The ClassHandler class Define the interface to implement when creating a plugin. More...
Public Member Functions | |
virtual void | deinit ()=0 |
Clean up resource; called automatically before plugin is unloaded. | |
virtual void | init ()=0 |
Initialize an instance for use; alled automatically when plugin is registered. | |
virtual ClassHandlerPluginInfo | getPluginInfo () const =0 |
Get information about the plugin. | |
Static Public Attributes | |
static const QString | HttpResponseStatusKey |
static const QString | JsonResponseKey |
The ClassHandler class Define the interface to implement when creating a plugin.
This interface is used by the QPluginLoader to load plugins dynamically.
This class is intended to be used in a very specific way. If it is used in that way, and linked into a library, then Tufao can automatically dispatch incoming requests to this class, and more specifically, to specific methods defined in this class. This allows the developer to just write a class, and not need to worry about crazy regular expressions, and then logic to determine what should be done (action to perform) with the traditional AbstractHttpServerRequestHandler. As the AbstractHttpServerRequestHandler can only have one dispatch-able method, it would have been up to the developer to determine what logic to execute based on the request, or to have a large number of classes & a large number of regular expressions to map to those classes.
This is achieved by breaking the URL paths up into a parameter list, and then invoking a method on the subclass and passing the parameters to that method. So a URL request like: /forum/read/forumName/someForumName/threadName/someThreadName/page/6
would call a method Forum::read(QString forumName, QString threadName, int pageNumber) as read("someForumName", "someThreadName", 6);. (please read below for a more accurate description). This would take a user to a forum named someForumName, to a topic named someThreadName, to the 6th page of responses.
This format is intended to follow the general ReST tenants of resource location, and map them to a method implemented on a ClassHandler. In this example, some resource named forum has a sub-resource named forumName, and that sub-resource further has a sub-resource named threadName, and finally, a further sub-resource of page. Each of the first two resources are found by a string, and the last resource is found by an int.
To use this class, there are a few conventions you must follow, and the class needs to be compiled into a plugin. First I'll talk about the implementation, and then I'll talk about the packaging.
All subclasses must be Q_OBJECT's. Further, two additional macro's must be used: Q_PLUGIN_METADATA(IID "tufao.Test/1.0") and Q_INTERFACES(Tufao::ClassHandler). The second macro must be exact, and is what Qt uses to know the class implements the ClassHandler interface. The first macro defines metadata about the plugin, specifically the IID of the plugin. The string can be anything meaningful, but is typically the now-common reverse resource locator used by so many languages. So if you were with the Stellarium project, the IID would be something like org.stellarium.PluginName/1.0 where PluginName would be name of this specific plugin, and 1.0 would be the plugin version.
In the URL, the first path component is the object name (technically, if a context is being used, the context will be the first path component - see ClassHandlerManager - but that is transparent to implementing this class). In your constructor, you should call setObjectName("forum");
where the parameter is what you want matched in the URL path. For clarity, using the name of the class itself is a good idea. As it is case-sensitive, you may want to use the lower case version. Once this is done, incoming requests that match the object name will be dispatched (if they match a method; see below).
The next path component is the name of a method in the class. The method name must match exactly, and is case- sensitive. The method should be a public slot
, and the signature of the method is very important. The method must accept at least two parameters, and they must be Tufao::HttpServerRequest & request, Tufao::HttpServerResponse & response
. The parameter names must be that exactly, as the dispatch uses parameter names of the methods to find the correct method to dispatch to. Further, the parameter types are used to convert the strings of the URL path to the data types the method requires.
As these two parameters must be on every method, the simplest method could have would have these two parameters. This allows you access to the request if you need it, and the response to write out the information back to the requester.
In the above example, the actual declaration of the method would be: void read(Tufao::HttpServerRequest & request, Tufao::HttpServerResponse & response, QString forumName, QString threadName, int pageNumber)
. The parameter names are used to match the path components in the URL. This means that a URL will always have an even number of path components after the class name; half for the name of the parameters, and the other half for the values. If a value cannot be converted to the type of the parameter, the request is not handled, and a message logged. So the request would end up passing to the next registered AbstractHttpServerRequestHandler registered with the HttpRequestRouter.
If a context has been assigned to the ClassHandlerManager that is responsible for this plugin, it is also important to check to see if the request has a context. This is done simply with !request.context().isEmpty()
. If the context is not empty, yo may need to set it into the output, so that any URL's refernced (either in HTML or in Ajax) can prepend the context to there paths. If you are the sole user of your plugin, and you know what context you are going to use, you can avoid this and directly use the context in your HTML.
Finally, as the user has no control over instance lifetime (creation is done at application start for static plugins and load-time for dynamic ones, and objects are destroyed at application termination for static plugins and when the plugin is unloaded for dynamic ones), there are void init()
and void deinit()
methods that are called before an instance is dispatched to, and before shutdown. This allows for the creation or obtaining of resources that should not be attempted until the QCoreApplication is running, and that should be freed before the QCoreApplication is terminated.
This class is also the superclass for plugins. One you subclass, you compile that code into either a dynamic or a static plugin. Dynamic plugins are a bit more robust in this case, and you can find an example project in the examples directory, named sample_plugin, that has a project with an executable as well as a dynamic plugin. You can use the cmake files for your own project.
If using a static plugins, you can load as many plugins as you like, but, they will all be connected to the same context (if used; see ClassHandlerManager). This is because there is not way to determine what the IID of the plugin is when loaded statically.
If using a dynamic plugin, the plugin must be installed into a path where it will be found. This includes all of the following:
Each of these paths is searched for a plugins subdirectory. Each entry in that directory that is a dynamic library is loaded if it implements the ClassHandler interface.