Sophie

Sophie

distrib > Fedora > 18 > i386 > by-pkgid > fffee38c4ecefcfc897550b36364b55a > files > 805

dmlite-docs-0.6.1-1.fc18.i686.rpm

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  <title>DMLite Tutorial - Develop a new plug-in</title>
  <script type="text/javascript" src="google-code-prettify/prettify.js"></script>
  <link href="google-code-prettify/prettify.css" rel="stylesheet" type="text/css"/>
  <link href="style.css" rel="stylesheet" type="text/css"/>
</head>

<body onload="prettyPrint()">
<h1>
  <a href="index.html">DMLite</a>: How to write a new plugin <img alt="LOGO" src="dmlite.png" style="float: right" height="150" /></h1>
  
<p>
The modular design of DMLite makes easy to write new plugins that can be loaded behind any front-end that uses DMLite behind.<br/>
In this tutorial we will develop a very simple plugin that is able to regiter itself and slightly alters the application logic.
</p>
<p>
For testing purposes you can use the <em>"Get a physical location where to read"</em> example. It doesn't matter if you choose
the <a href="backend_c.html#get">C</a>, <a href="backend_cpp.html#get">C++</a> or <a href="backend_python.html#get">Python</a>
implementation.
</p>

<h2>Basic skeleton</h2>

<h3>Implementing a factory</h2>
<p>
As a first step, we will create a plugin that does nothing, except printing all the configuration parameters
it receives, and then ignores them.
</p>
<p>
We create two files called, for instance, <code>myplugin.h</code> and <code>myplugin.cpp</code>.<br/>
As our plugin does nothing, we only need a basic factory that just receives configuration parameters, but does not
create anything. For that, we will create a concrete implementation of
<code><a href="http://dmlite.web.cern.ch/dmlite/trunk/doc/html/classdmlite_1_1_base_factory.html">dmlite::BaseFactory</a></code>
and overload the method
<code><a href="http://dmlite.web.cern.ch/dmlite/trunk/doc/html/classdmlite_1_1_base_factory.html#b37af2e32ff19502114f59cacead885a">configure</a></code>.
</p>

<pre class="prettyprint">
///@file myplugin.h
#ifndef _MY_PLUGIN_H
#define _MY_PLUGIN_H

#include &lt;dmlite/cpp/base.h&gt;

class MyFactory: public dmlite::BaseFactory {
 public:
  MyFactory(void);
  ~MyFactory(void);

  void configure(const std::string& key,
                 const std::string& value) throw (dmlite::DmException);
};

#endif
</pre>

<pre class="prettyprint">
///@file myplugin.cpp
#include &lt;dmlite/cpp/dmlite.h&gt;
#include &lt;iostream&gt;
#include "myplugin.h"

MyFactory::MyFactory(void)
{
  std::cerr &lt;&lt; "MyFactory created" &lt;&lt; std::endl;
}

MyFactory::~MyFactory(void)
{
  std::cerr &lt;&lt; "MyFactory destroyed" &lt;&lt; std::endl;
}

void MyFactory::configure(const std::string& key,
                          const std::string& value) throw (dmlite::DmException)
{
  std::cerr &lt;&lt; "[CONF] " &lt;&lt; key &lt;&lt; " = " &lt;&lt; value &lt;&lt; std::endl;
  throw dmlite::DmException(DMLITE_CFGERR(DMLITE_UNKNOWN_KEY), "Unknown key %s", key.c_str());
}
</pre>

<p>
You can compile now with
</p>
<pre>
# gcc -fPIC -shared myplugin.cpp -o plugin_my.so
</pre>

<h3>Registering the plugin</h3>
<p>
Right now we have a shared .so library with the basic functionality we added before, but
that's not going to help unless we get our plugin into the DMLite stack.
For that, we need to create a "register" function, and create a symbol that will act as an
entry point for the DMLite loader.
</p>
<p>
For that, we just have to add at the end of our <code>myplugin.cpp</code> file the following code:
</p>

<pre class="prettyprint">
// Register method
static void registerMyPlugin(dmlite::PluginManager* pm) throw (dmlite::DmException)
{
  pm->registerConfigureFactory(new MyFactory());
}

// This is what DMLite will look for
dmlite::PluginIdCard plugin_my = {
  PLUGIN_ID_HEADER,
  registerMyPlugin
};
</pre>

<p>
And we compile as before.
</p>

<p>
Now, to test it we will create a new configuration file that loads our plugin, and then
the defaults <code>/etc/dmlite.conf.d/*.conf</code>.
</p>

<pre class="prettyprint">
# myplugin.conf
LoadPlugin plugin_my     ./plugin_my.so
LoadPlugin plugin_config /usr/lib64/dmlite/plugin_config.so


Include /etc/dmlite.conf.d/*.conf
</pre>

<p>
And we run any of the test mentioned before, using our configuration file (the test executable
must be in your <code>PATH</code> if you don't put the full path).
</p>

<pre>
$ dmlite-init myplugin.conf /
MyFactory created
[CONF] DpmHost = localhost
[CONF] TokenPassword = test
[CONF] TokenId = ip
[CONF] TokenLife = 1000
[CONF] TokenPassword = test
[CONF] TokenId = ip
[CONF] TokenLife = 1000
[CONF] MySqlHost = localhost
[CONF] MySqlUsername = dpmdbuser
[CONF] MySqlPassword = changethis
[CONF] Include = /etc/dmlite.conf.d/*.conf
Could not get the final location for /
Reason: [#00.000021] Directories do not have replicas
MyFactory destroyed
</pre>

<h2>Add a PoolManager decorator</h2>
<p>
The previous example is not able to actually create any concrete implementation of any interface, so its functionallity
would be limited to interact with the PluginManager and with the configuration parameters. It could be useful to implement
a configuration plugin (as the Include directive is implemented), but doesn't add functionality to the front-end.
</p>
<p>
Now, we are going to extend the plugin to be able to create a decorator for the PoolManager. Our implementation will do nothing,
but delegate every call to the next plugin in the stack.
</p>
<p>
First, we need to inherit from
<code><a href="http://dmlite.web.cern.ch/dmlite/trunk/doc/html/classdmlite_1_1_pool_manager_factory.html">dmlite::PoolManagerFactory</a></code>
and overload the method <code>createPoolManager</code>, and then create a new implementation of
<code><a href="http://dmlite.web.cern.ch/dmlite/trunk/doc/html/classdmlite_1_1_pool_manager.html">dmlite::PoolManager</a></code>,
inheriting from <code><a href="http://dmlite.web.cern.ch/dmlite/trunk/doc/html/classdmlite_1_1_dummy_pool_manager.html">dmlite::DummyPoolManager</a></code>,
which is a convenient base class for decorator plugins, since it has a default implementation for all methods that just delegates.
</p>

<pre class="prettyprint">
///@file myplugin.h
#ifndef _MY_PLUGIN_H
#define _MY_PLUGIN_H

#include &lt;dmlite/cpp/poolmanager.h&gt;
#include &lt;dmlite/cpp/dummy/DummyPool.h&gt;

class MyFactory: public dmlite::PoolManagerFactory {
 public:
  MyFactory(dmlite::PoolManagerFactory* next);
  ~MyFactory(void);

  void configure(const std::string& key,
                 const std::string& value) throw (dmlite::DmException);

  dmlite::PoolManager* createPoolManager(dmlite::PluginManager* pm) throw (dmlite::DmException);

 private:
  dmlite::PoolManagerFactory* next;
};

class MyPoolManager: public dmlite::DummyPoolManager {
 public:
  MyPoolManager(dmlite::PoolManager* decorated) throw (dmlite::DmException);
  ~MyPoolManager();

  // Dummy does not implement this, so we need to do it ourselves
  std::string getImplId() const throw();
};

#endif
</pre>

<p>
Then, we implement the methods:
</p>

<pre class="prettyprint">
///@file myplugin.cpp
#include &lt;dmlite/cpp/dmlite.h&gt;
#include &lt;iostream&gt;
#include "myplugin.h"

MyFactory::MyFactory(dmlite::PoolManagerFactory* next): next(next)
{
}

MyFactory::~MyFactory(void)
{
}

void MyFactory::configure(const std::string& key, const std::string& value) throw (dmlite::DmException)
{
}

dmlite::PoolManager* MyFactory::createPoolManager(dmlite::PluginManager* pm) throw (dmlite::DmException)
{
  // We got a pointer to the next factory in 'next', so we use the static protected method
  // to create a PoolManager from that factory.
  // We can not call directly next->createPoolManager(pm) because of the protection.
  // Then, we pass the PoolManager implementation to our own so it can be wrapped.
  return new MyPoolManager(PoolManagerFactory::createPoolManager(next, pm));
}

MyPoolManager::MyPoolManager(dmlite::PoolManager* next) throw (dmlite::DmException):
  dmlite::DummyPoolManager(next)
{
  std::cout &lt;&lt; "Wrapping " &lt;&lt; next->getImplId() &lt;&lt; std::endl;
}

MyPoolManager::~MyPoolManager()
{
}

std::string MyPoolManager::getImplId() const throw ()
{
  return "MyPoolManager";
}

// Register method
static void registerMyPlugin(dmlite::PluginManager* pm) throw (dmlite::DmException)
{
  // What's happening here:
  // Since we are not yet registered, we are getting the previous top of the stack
  // and storing it in our factory so we can call it later.
  // Then, we register our factory.
  pm->registerPoolManagerFactory(new MyFactory(pm->getPoolManagerFactory()));
}

// This is what DMLite will look for
dmlite::PluginIdCard plugin_my = {
  PLUGIN_ID_HEADER,
  registerMyPlugin
};
</pre>

<p>
We have to modify the configuration file too, since now we need to be loaded last, not first:
our implementation rely on a previous <code>PoolManager</code> concrete implementation being there
to be wrapped.
</p>

<pre class="prettyprint">
# myplugin.conf
LoadPlugin plugin_config /usr/lib64/dmlite/plugin_config.so

Include /etc/dmlite.conf.d/*.conf

LoadPlugin plugin_my ./plugin_my.so
</pre>

<p>
After compiling and running, we should get something like this:
</p>

<pre>
$ dmlite-init myplugin.conf /dpm/cern.ch/home/dteam/file.html
Wrapping mysql_pool_manager
Chunk 0: sl6vm.cern.ch:/dpm/cern.ch/home/dteam/dmlite03_test/test529.html (0-63214)
	token: UEkJNEvVepSVHYe8vVEkjikUnE0=@1348231193@0
</pre>

<p>
You can see that the constructor of <code>MyPoolManager</code> is actually being code, but from the
rest of the behaviour we see no difference because we are just delegating.<br/>
But this is a good starting point: our plugin is already in the stack, and everything
it doesn't implement is being delegated 
</p>

<h2>Override the 'whereToRead' method</h2>
<p>
Now we are going to actually decorate one method: whereToRead. Since doing something actually useful if beyond the
scope of this tutorial, we are going to take advantage of the program printing the chunks to put something there that
we can see.
</p>
<p>
For that, we only need to add whereToRead to our class declaration, and then implement the method delegating ourselves
to the next plugin, and pushing some additional data.
</p>

<pre class="prettyprint">
class MyPoolManager: public dmlite::DummyPoolManager {
 public:
  MyPoolManager(dmlite::PoolManager* decorated) throw (dmlite::DmException);
  ~MyPoolManager();

  std::string getImplId() const throw();

  dmlite::Location whereToRead(const std::string& path) throw (dmlite::DmException);
};
</pre>

<p>
Now, the implementation:
</p>

<pre class="prettyprint">
dmlite::Location MyPoolManager::whereToRead(const std::string& path) throw (dmlite::DmException)
{
  dmlite::Location where = DummyPoolManager::whereToRead(path);
  dmlite::Chunk myChunk;
  myChunk.host = "myhost.mydomain.com";
  myChunk.path = path;
  myChunk.offset = 0;
  myChunk.size   = 0;
  myChunk["arbitrary"] = std::string("data");
  where.push_back(myChunk);
  return where;
}
</pre>

<p>
After compiling and executing as before, we would get something like:
</p>

<pre>
# ../dmlite-init myplugin.conf /dpm/cern.ch/home/dteam/file.html
Wrapping mysql_pool_manager
Chunk 0: sl6vm.cern.ch:/dpm/cern.ch/home/dteam/dmlite03_test/test529.html (0-63214)
	token: J9dNueNkEpvmE7rr96BTn85h8oY=@1348231832@0
Chunk 1: myhost.mydomain.com:/dpm/cern.ch/home/dteam/file.html (0-0)
	arbitrary: data
</pre>

<p>
So you can see how our PoolManager took over the request, pushing whatever data it needed to.
</p>

<h2>Summary</h2>
<p>
After following this tutorial, implementing new plugins that override additional methods should be relatively easy,
since the steps are the same in all cases. The only thing that may change is the Factory and the Interface types
to implement.<br/>
It could happen that your plugin doesn't need to delegate anything (i.e. a new database backend). In that case,
you can inherit directly from the right interface, and not from the Dummy plugin, but in that case no default implementation is
given, so using Dummy as a base is always useful when available, since it will give you a base where you can add methods one by one.
</p>

</body>
</html>