Main  /  Edit  /  History  /   /  Users Area

Plugin Architecture

Overview

CPPCMS will have a full native support for plugins in the upcoming version 1.2.

Meanwhile, CPPCMS 1.0 already has all the basics in place to support plugins. Templates are already fully pluggable (load a shared object and the template is ready to use). Also there is a new booster::shared_object class which allows to load dynamic libraries easily.

However, depending on the architecture of your application, the exact solution to adopt may vary. In the following sections, we'll explore different use case scenarios. If anything here is not clear, please ask on the mailing list and come back here to appropriately edit this page.

Template plugged via a shared object.

Each template shared object has a global constructor and destructor. Once a shared object is loaded, the constructor is called and it registers itself in cppcms::views::pool singleton and becomes visible for everybody. When it is unloaded it is unregistered via the global destructor and can't be used any more. So it has simple and transparent semantics.

If a plugin is linked with the template code it would load its own template:

libmy_plugin.so
my_application.cpp:

  // my code 
  {
     ...
     render("my_plugin_skin","some_view",some_content);
  }

my_view.cpp <- my_view.tmpl

// this code is auto-generated
 namespace { 
   struct loader { 
     loader() {   cppcms::views::pool::instance().add(my_skin_generator()); }
     ~loader() { cppcms::views::pool::instance().remove(my_skin_generator());}
   } loader_instance;
 }

So once you load the shared object, loader::loader() is executed and registers a view and when you unload the shared object loader::~loader() unregisters it.

Make sure that each template has its own skin name.

Templates with own plugin framework

If you have implemented your own plugin framework, here are two methods to allow plugins to define their own templates for the content they create, to be integrated in the page generated by the main application.

Using streams

The core application defines the layout for the whole page:

<% skin basic_skin %>
<% view basic_view uses content::basic_content %>
<html>
  ...
<body>
  ...

  <!--- aux part-->

  <% foreach p in plugings %>
      <% item %><%= p.rendered_aux_html_content | raw %><% end %>

  <% end %>


  ...
  <!--- Main part-->

  <% foreach p in plugings %>
      <% item %>
        <div id="plugin_<%=p.id %>" >
        <%= p.rendered_main_html_content | raw %>

        </div>        

      <% end %>

  <% end %>

  ...


</body>
<% end view %>
...

<% end skin %>

Each plugin defines the templates for their own content:

<% skin plugin_a %>
<% view main_html uses plugin_a_content::some_content_b %>
<% template render() %>
  <!-- Notice render function -->
  some plugin specific html
  that does something it needs

<% end template %>
<% view aux_html uses plugin_a_content::some_content_a %>
<% template render() %>
  <!-- Notice render function -->
  some plugin specific html
  that does something it needs

<% end template %>

<% end view %>

<% end skin %>

Let's say that our plugin should provide the following API:

class plugin {
public:
   virtual std::string  main_html(cppcms::applicatin *app) = 0;

   virtual std::string aux_html(cppcms::application *app) = 0;
 };

The content of each plugin is rendered as follows:

// main_application.cpp

content::basic_content c;
// prepare c
...
// render plugins
for(int i=0;i<loaded_plugins.size();i++) {
     c.plugins[i].rendered_main_html_content = loaded_plugins[i]->main_html(this);
     c.plugins[i].rendered_aux_html_content = loaded_plugins[i]->aux_html(this);
} 

render("basic_skin","basic_view",c);

On the plugin side, it looks like this:

std::string  main_html(cppcms::applicatin *app)
{
    plugin_a_content::some_content_b c;
    ...
    std::ostringstream buf;
    app->render("plugin_a","main_html",c,buf);
    return buf.str();

}

std::string  aux_html(cppcms::applicatin *app)
{
    plugin_a_content::some_content_a c;
    ...
    std::ostringstream buf;
    app->render("plugin_a","aux_html",c,buf);
    return buf.str();

}

With the command render

Since CPPCMS 0.99.12, there is the command <% render ... %> See Rendering other views.

The core application defines the layout for the whole page:

<% skin basic_skin %>
<% view basic_view uses content::basic_content %>
<html>
  ...
<body>
  ...

  <!--- aux part-->

  <% foreach p in plugings %>
      <% item %><% render p->name(), "aux_html" with p->aux_html_content() %><% end %>

  <% end %>


  ...
  <!--- Main part-->

  <% foreach p in plugings %>
      <% item %>
        <div id="plugin_<%=p->id() %>" >
        <% render p->name(), "main_html" with p->main_html_content() %>
        </div>        

      <% end %>

  <% end %>

  ...


</body>
<% end view %>
...

<% end skin %>

Where p is a pointer to the plugin object.

The plugin's template would be implemented like in the previous section. See above.

Let's say that our plugin should provide the following api:

class plugin {
public:
   virtual void prepare() = 0;

   virtual cppcms::base_content  &main_html_content() = 0;

   virtual cppcms::base_content  &aux_html_content() = 0;

   virtual std::string name() = 0;
};

The content of each plugin is rendered as follows:

basic_content {
  ...
  // pointers to plugins
  std::vector<plugin *> plugins;
}

main_application.cpp:

content::basic_content c;
// prepare c
...
for(p in plugins)
  p->prepare();
render("basic_skin","basic_view",c); 

On the plugin side:

class my_plugin : public plugin {
public:
    virtual void prepare()
    {
      ... setup main_ and aux_...

    }
    virtual std::string name() { return "plugin_a"; }

    virtual cppcms::basic_content &main_html_content() { return main_; }

    virtual cppcms::basic_content &aux_html_content() { return aux_; }


private:

    plugin_a_content::some_content_b aux_;

    plugin_a_content::some_content_a main_;

};



std::string  main_html(cppcms::applicatin *app)
{
    plugin_a_content::some_content_b c;
    ...
    std::ostringstream buf;
    app->render("plugin_a","main_html",c,buf);
    return buf.str();

}


std::string  aux_html(cppcms::applicatin *app)
{
    plugin_a_content::some_content_a c;
    ...
    std::ostringstream buf;
    app->render("plugin_a","aux_html",c,buf);
    return buf.str();

}

Variation

Variation on the above, according to the needs of your application.

Plugin definition:

 plugin {
     void render_main(cppcms::application &);

     void render_aux(cppcms::application &);

 }

Where the plugin is executed like:

 render_main(cppcms::application &app)
 {
      app.render("plugin_a","main_html",my_content);

 }

And the template looks like:

 <% foreach p in plugins %>
  <% item %>
   <% c++ p->render_main(content.app()); %>
  <% end item %>
 <% end foreach %>

Reusing a skin

This section considers the possibility of a plugin to reuse the skin implemented by the core of the application.

Consider that your plugin wants to extend basic_skin::basic_view (i.e. derive from it) and have control over the entire page.

This is much more tricky.

You can't derive from it as cppcms_tmpl_cc does not generate header files. Instead, do the following.

Instead of writing: <% skin basic_skin %>, use a nameless skin: <% skin %>

Then compile the main skin:

Main view cpp:

 cppcms_tmpl_cc -s basic_skin basic_view.tmpl some_other_main_views.tmpl -o basic_skin.cpp
 g++ basic_skin.cpp -o libbasic_skin.so

 cppcms_tmpl_cc -s plugin_a basic_view.tmpl plugin_a.tmpl -o plugin_a.cpp
 g++ plugin_code.cpp plugin_a.cpp  -o libplugin_a.so

Notice that basic_view.tmpl appears twice but once under skin=namespace "basic_skin" and the second time under skin=namespace plugin_a.

This copies c++ code, and requires the plugin to actually use the application's skin code. It is not ideal but it is tolerable, at least until cppcms_tmpl_cc supports generation of .h and .cpp files.


Howto | Top | CppCMS 1.x.x tasks

About

CppCMS is a web development framework for performance demanding applications.

Support This Project

SourceForge.net Logo

Поддержать проект

CppCMS needs You


Navigation

Main Page



Valid CSS | Valid XHTML 1.0