Feb
02 HowTo: XCF ActiveMemory
In my diploma (respectively master) thesis, I'm using XCF, a C++ middleware developed at the Bielefeld University. This communication framework is quite good and versatile. It offers publisher/subscriber data streams, XML-RPC and content driven event notification through the so called "ActiveMemory". The ActiveMemory is a XML database, that routes all communication between nodes based on the content of the data streams. This makes it easy to publish or subscribe data and react on the occurrence of particular content.
Unfortunately, the documentation of the ActiveMemory is a bit sparse, which makes getting started a bit complicated. In the extended part of this posting (just click on its headline) I'm going to explain how to use the ActiveMemory quite easily and comfortable.
I assume that there already is a valid XCF installation on the target Linux system and you have some basic XCF programming skills (after going through the tutorials you have these). The HowTo is divided into three parts:
- Setting up an ActiveMemory
- Accessing and inserting data into the ActiveMemory
- Subscribing on content
Setting up an ActiveMemory
To run an ActiveMemory, you need a running dispatcher (included in the XCF framework). It should be running after simply executing "dispatcher" from the XCF bin-directory.
Before starting the memory server, you also need to run "spread". This requires a valid configuration file in your home-directory, called "spread.conf". This file keeps all nodes, that should be able to access the ActiveMemory:
Spread_Segment 127.0.0.255:4803 {
localhost 127.0.0.1
leonardo 127.0.1.1
}
The code above show my local spread.conf, where leonardo is the host name of my system (you might want to adapt it to the hos name of your machine). To let more nodes access the ActiveMemory, just add thier host name and IP between the brackets, one node per line. Finally start "spread".
Now create a directory where the database of the ActiveMemory can place it's content. For first experiments it is sufficient, to create a directory in the /tmp path of your machine, e.g. call "mkdir /tmp/dump/". After creating the directory, you can start the memory server "memory_server" with this path and the desired name of the ActiveMemory as arguments. For example call "memory_server /tmp/dump MemoryName".
Now you should have successfully set up an ActiveMemory.
You can also write two simple bash-scripts, one that starts these three programs and another, that kills them and cleans up:
"am-start.sh": dispatcher & sleep 2 spread & sleep 3 mkdir /tmp/dump memory_server /tmp/dump/ MemoryName
"am-stop.sh": killall dispatcher killall spread killall memory_server rm /tmp/4803
Accessing and inserting data into the ActiveMemory
To get access to your ActiveMemory instance, you need a MemoryInterface::pointer:
memory::interface::MemoryInterface::pointer mi;
mi = MemoryInterface::getInstance("xcf:MemoryName");
Now you should have an open connection to your ActiveMemory. With the insert method you can insert XML data:
// Creating some dummy data:
Location root("<data></data>", "/data");
Location foo("<foo>", "/foo");
foo["value"] = 123;
root.add(foo);
// inserting the data:
mi->insert(root.getDocumentText());
See documentation for more access commands.
Subscribing on content
For convenient access Alex found a nice encapsulation concept. He created a class that encapsulates the binding of a callback function using boost and the callback function itself. Here is the code of the header file "amcalladapter.h":
#ifndef AMCALLADAPTER_H
#define AMCALLADAPTER_H
#include <xcf/xcf.hpp>
#include <xmltio/xmltio.hpp>
#include <Memory/Interface.hpp>
#include <boost/bind.hpp>
#include <string>
#include <iostream>
class AMCallAdapter {
public:
AMCallAdapter(std::string memory, std::string xpath);
virtual void callbackFunc(memory::interface::Event e);
protected:
memory::interface::MemoryInterface::pointer m_mi;
std::string m_memory;
std::string m_xpath;
};
#endif
Additionally you need the source file "amcalladapter.cpp":
#include "amcalladapter.h"
using namespace std;
using namespace memory::interface;
using namespace boost;
using namespace xmltio;
AMCallAdapter::AMCallAdapter(string memory, string xpath){
m_memory = memory;
m_xpath = xpath;
m_mi = MemoryInterface::getInstance("xcf:" + memory);
Condition con(Event::INSERT|Event::REPLACE, xpath);
TriggeredAction action(bind(&AMCallAdapter::callbackFunc, this, _1));
m_mi->add(con,action);
}
void AMCallAdapter::callbackFunc(Event e){
TIODocument doc = e.getDocument();
Location root = doc.getRootLocation();
cerr << "WARNING: You are using a useless class!" << endl <<
"Memory: " << m_memory << endl <<
"XPath: " << m_xpath << endl <<
"Document: " << root.getDocumentText() << endl <<
"You have to derive a new class from CallAdapter" <<
"and overwrite the callbackFunc!" << endl;
}
In the instantiation of the Condition you can specify on which type of event you want the callbackFunc to be subscribed. In the example the types insert and replace are linked (according to boolean terms) so that the callbackFunc gets called on insert and replace events.
Now you can simply derive a new class from this stub and reimplement the callbackFunc to your needs. Every time, a xml document with content, the AMCallAdapter class takes out a subscription on, the callbackFunc gets called. When using other frameworks, like Qt, that implements event handling, this could be used to emit signals. But be careful with signal-slot connections between threads. These need to get queued.
Now you have a basic and very convenient method at hand to use an ActiveMemory.