diff --git a/CMake/FindLibXMLxx.cmake b/CMake/FindLibXMLxx.cmake index ca3888b7f1077d3ea4ae2257ebb3a97b474e738f..ce73e576e7781471091d525798543b137113e69c 100644 --- a/CMake/FindLibXMLxx.cmake +++ b/CMake/FindLibXMLxx.cmake @@ -30,4 +30,12 @@ if(NOT LIBXMLXX_FOUND) include(FindPkgConfig) pkg_search_module(LIBXMLXX REQUIRED libxml++-2.8 libxml++-2.7 libxml++-2.6 libxml++-2.5) mark_as_advanced(LIBXMLXX_INCLUDE_DIRS LIBXMLXX_LIBRARIES) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(LIBXMLXX DEFAULT_MSG + LIBXMLXX_LIBRARIES LIBXMLXX_INCLUDE_DIRS) + + set(LIBXMLXX_LIBRARY "${LIBXMLXX_LIBRARIES}" CACHE LIST "LibXML++ libraries") + set(LIBXMLXX_INCLUDE_DIR "${LIBXMLXX_INCLUDE_DIRS}" CACHE LIST "LibXML++ include dirs") + endif(NOT LIBXMLXX_FOUND) diff --git a/LCS/MessageBus/CMakeLists.txt b/LCS/MessageBus/CMakeLists.txt index 2a8d283d6ece24acb3cf4fb01c30b40e291ed247..41113fb9e24557eb4f044f12f7be3e99f53068a2 100644 --- a/LCS/MessageBus/CMakeLists.txt +++ b/LCS/MessageBus/CMakeLists.txt @@ -4,6 +4,7 @@ lofar_package(MessageBus 1.0 DEPENDS Common pyparameterset) include(LofarFindPackage) lofar_find_package(QPID) +lofar_find_package(LibXMLxx) lofar_find_package(UnitTest++) add_subdirectory(include/MessageBus) diff --git a/LCS/MessageBus/include/MessageBus/Message.h b/LCS/MessageBus/include/MessageBus/Message.h index b6ada16d93f8221187e4e895756a11db348fc873..3cf9cc0439dc1958ef5011caeba312ce2a8b0641 100644 --- a/LCS/MessageBus/include/MessageBus/Message.h +++ b/LCS/MessageBus/include/MessageBus/Message.h @@ -30,6 +30,10 @@ #include <MessageBus/NoQpidFallback.h> #endif +#ifdef HAVE_LIBXMLXX +#include <libxml++/parsers/domparser.h> +#endif + #include <string> #include <ostream> @@ -91,13 +95,17 @@ public: void set(const std::string &value) { itsContent->setXMLvalue(itsKey, value); } std::string get() const { return itsContent->getXMLvalue(itsKey); } - // C++ operator overloading - void operator=(const std::string &value) { set(value); } + // C++ operator overloading (syntactic sugar) + Property &operator=(const std::string &value) { set(value); return *this; } operator std::string () const { return get(); } - bool operator==(const Property &other) const { return (std::string)*this == (std::string)other; } - bool operator==(const std::string &other) const { return (std::string)*this == other; } - bool operator==(const char *other) const { return (std::string)*this == std::string(other); } + bool operator==(const Property &other) const { return this->get() == other.get(); } + bool operator==(const std::string &other) const { return this->get() == other; } + bool operator==(const char *other) const { return this->get() == other; } + + bool operator!=(const Property &other) const { return this->get() != other.get(); } + bool operator!=(const std::string &other) const { return this->get() != other; } + bool operator!=(const char *other) const { return this->get() != other; } private: Property(): itsContent(0), itsKey("") {} @@ -142,11 +150,26 @@ public: // Set a value in the XML content. void setXMLvalue(const std::string& key, const std::string& data); +protected: + // Import an XML subdocument under the given key. + void insertXML(const std::string& key, const std::string& xml); + +#ifdef HAVE_LIBXMLXX + // Locates and returns a node given by its XPATH key ("/a/b/c") + xmlpp::Element *getXMLnode(const std::string &name) const; +#endif + private: + void initContent(const std::string&); void addProperties(); // -- datamembers -- +#ifdef HAVE_LIBXMLXX + xmlpp::DomParser itsParser; // NOTE: non-copyable + xmlpp::Document *itsDocument; +#else std::string itsContent; +#endif }; inline std::ostream &operator<<(std::ostream &os, const MessageContent &msg) @@ -175,17 +198,14 @@ public: qpid::messaging::Message& qpidMsg() { return (itsQpidMsg); } const qpid::messaging::Message& qpidMsg() const { return (itsQpidMsg); } - // Return the content - MessageContent content() const { return MessageContent(itsQpidMsg); } - // Return the raw message content std::string rawContent() const { return itsQpidMsg.getContent(); } // Return a short (one line) description of the message - std::string short_desc() const { return content().short_desc(); } + std::string short_desc() const { MessageContent content(itsQpidMsg); return content.short_desc(); } // function for printing - std::ostream& print (std::ostream& os) const { return content().print(os); } + std::ostream& print (std::ostream& os) const { MessageContent content(itsQpidMsg); return content.print(os); } private: // -- datamembers -- diff --git a/LCS/MessageBus/src/Message.cc b/LCS/MessageBus/src/Message.cc index a0490fd974684bbf463f85e3205c70aa2b14d99c..c9f282098bcf0b4032be8fcf922ad41a133e295a 100644 --- a/LCS/MessageBus/src/Message.cc +++ b/LCS/MessageBus/src/Message.cc @@ -33,6 +33,13 @@ #include <qpid/types/Uuid.h> #endif +#ifdef HAVE_LIBXMLXX +#include <libxml++/parsers/domparser.h> +#include <libxml++/nodes/textnode.h> + +using namespace xmlpp; +#endif + #include <time.h> @@ -86,6 +93,7 @@ static string _uuid() { MessageContent::MessageContent() { + initContent(LOFAR_MSG_TEMPLATE); addProperties(); } @@ -96,9 +104,8 @@ MessageContent::MessageContent(const std::string &from, const std::string &protocolVersion, const std::string &momid, const std::string &sasid) -: - itsContent(LOFAR_MSG_TEMPLATE) { + initContent(LOFAR_MSG_TEMPLATE); addProperties(); this->system = LOFAR::system; @@ -116,9 +123,8 @@ MessageContent::MessageContent(const std::string &from, } MessageContent::MessageContent(const qpid::messaging::Message &qpidMsg) -: - itsContent(qpidMsg.getContent()) { + initContent(qpidMsg.getContent()); addProperties(); } @@ -126,6 +132,16 @@ MessageContent::~MessageContent() { } +void MessageContent::initContent(const std::string &content) +{ +#ifdef HAVE_LIBXMLXX + itsParser.parse_memory(content); + itsDocument = itsParser.get_document(); +#else + itsContent = content; +#endif +} + void MessageContent::addProperties() { system .attach(this, "message/header/system"); @@ -151,7 +167,13 @@ void MessageContent::addProperties() qpid::messaging::Message MessageContent::qpidMsg() const { qpid::messaging::Message qpidMsg; +#ifdef HAVE_LIBXMLXX + std::string content = itsDocument->write_to_string_formatted(); + qpidMsg.setContent(content); +#else qpidMsg.setContent(itsContent); +#endif + qpidMsg.setContentType("text/plain"); qpidMsg.setDurable(true); @@ -175,10 +197,8 @@ std::string MessageContent::short_desc() const std::ostream& MessageContent::print (std::ostream& os) const { - os << "system : " << system << endl; - os << "systemversion : " << headerVersion << endl; - os << "protocolName : " << protocol << endl; - os << "protocolVersion: " << protocolVersion << endl; + os << "system : " << system << " " << headerVersion << endl; + os << "protocol : " << protocol << " " << protocolVersion << endl; os << "summary : " << summary << endl; os << "timestamp : " << timestamp << endl; os << "source (name) : " << name << endl; @@ -192,65 +212,134 @@ std::ostream& MessageContent::print (std::ostream& os) const string MessageContent::getXMLvalue(const string& key) const { - // get copy of content - vector<string> labels = split(key, '/'); - - // loop over subkeys - string::size_type offset = 0; - string::size_type begin = string::npos; - string::size_type end = string::npos; - string startTag; - for (size_t i = 0; i < labels.size(); ++i) { - // define tags to find - startTag = string("<"+labels[i]+">"); - // search begin tag - begin = itsContent.find(startTag, offset); - if (begin == string::npos) { - return ("???"); - } - offset = begin; - } - // search end tag - string stopTag ("</"+labels[labels.size()-1]+">"); - begin+=startTag.size(); - end = itsContent.find(stopTag, begin); - if (end == string::npos) { - return ("???"); - } - return (itsContent.substr(begin, end - begin)); +#ifdef HAVE_LIBXMLXX + Element *e = getXMLnode(key); + if (!e) return "???"; + + TextNode *t = e->get_child_text(); + if (!t) return ""; + + return t->get_content(); +#else + // get copy of content + vector<string> labels = split(key, '/'); + + // loop over subkeys + string::size_type offset = 0; + string::size_type begin = string::npos; + string::size_type end = string::npos; + string startTag; + for (size_t i = 0; i < labels.size(); ++i) { + // define tags to find + startTag = string("<"+labels[i]+">"); + // search begin tag + begin = itsContent.find(startTag, offset); + if (begin == string::npos) { + return ("???"); + } + offset = begin; + } + // search end tag + string stopTag ("</"+labels[labels.size()-1]+">"); + begin+=startTag.size(); + end = itsContent.find(stopTag, begin); + if (end == string::npos) { + return ("???"); + } + return (itsContent.substr(begin, end - begin)); +#endif } void MessageContent::setXMLvalue(const string& key, const string &data) { - // get copy of content - vector<string> labels = split(key, '/'); - - // loop over subkeys - string::size_type offset = 0; - string::size_type begin = string::npos; - string::size_type end = string::npos; - string startTag; - for (size_t i = 0; i < labels.size(); ++i) { - // define tags to find - startTag = string("<"+labels[i]+">"); - // search begin tag - begin = itsContent.find(startTag, offset); - if (begin == string::npos) { - return; - } - offset = begin; - } - // search end tag - string stopTag ("</"+labels[labels.size()-1]+">"); - begin+=startTag.size(); - end = itsContent.find(stopTag, begin); - if (end == string::npos) { - return; - } - - itsContent.replace(begin, end - begin, data); +#ifdef HAVE_LIBXMLXX + Element *e = getXMLnode(key); + if (!e) return; + + e->set_child_text(data); +#else + // get copy of content + vector<string> labels = split(key, '/'); + + // loop over subkeys + string::size_type offset = 0; + string::size_type begin = string::npos; + string::size_type end = string::npos; + string startTag; + for (size_t i = 0; i < labels.size(); ++i) { + // define tags to find + startTag = string("<"+labels[i]+">"); + // search begin tag + begin = itsContent.find(startTag, offset); + if (begin == string::npos) { + return; + } + offset = begin; + } + // search end tag + string stopTag ("</"+labels[labels.size()-1]+">"); + begin+=startTag.size(); + end = itsContent.find(stopTag, begin); + if (end == string::npos) { + return; + } + + itsContent.replace(begin, end - begin, data); +#endif +} + +void MessageContent::insertXML(const string &key, const string &xml) +{ +#ifdef HAVE_LIBXMLXX + // Find insert spot + Element *e = getXMLnode(key); + if (!e) return; + + // Parse provided XML + DomParser parser; + parser.parse_memory(xml); + + Document *document = parser.get_document(); + if (!document) return; + + Element *root = document->get_root_node(); + if (!root) return; + + // Insert the XML into our document + e->import_node(root); +#else + setXMLvalue(key, xml); +#endif } +#ifdef HAVE_LIBXMLXX +Element *MessageContent::getXMLnode(const string &name) const +{ + Element *root = itsDocument->get_root_node(); + + if (!root) { + // Document is broken + return NULL; + } + + // assume key is an XPath relative to root, see http://www.w3schools.com/xpath/xpath_syntax.asp + NodeSet nodeset = root->find("/"+name); + if (nodeset.empty()) { + // Element not found + return NULL; + } + + Element *e = dynamic_cast<Element*>(nodeset[0]); + + if (!e) { + // Key points to a special element + return NULL; + } + + return e; +} +#endif + Message::Message(const MessageContent &content) : itsQpidMsg(content.qpidMsg()) diff --git a/LCS/MessageBus/test/CMakeLists.txt b/LCS/MessageBus/test/CMakeLists.txt index c433204fb7b7322a074783f95959ece05e08b157..d85a2d03c6733ff03e6bb88e9ac37c374805cc98 100644 --- a/LCS/MessageBus/test/CMakeLists.txt +++ b/LCS/MessageBus/test/CMakeLists.txt @@ -6,11 +6,11 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/MessageFuncs.sh.in ${CMAKE_BINARY_DIR}/bin/MessageFuncs.sh @ONLY) -if(HAVE_QPID) - if(HAVE_UNITTEST++) - lofar_add_test(tMessage tMessage.cc) - endif(HAVE_UNITTEST++) +if(HAVE_UNITTEST++) + lofar_add_test(tMessage tMessage.cc) +endif(HAVE_UNITTEST++) +if(HAVE_QPID) lofar_add_test(tMsgBus tMsgBus.cc) lofar_add_test(tPyMsgBus) diff --git a/LCS/MessageBus/test/tMessage.cc b/LCS/MessageBus/test/tMessage.cc index d06b2d36da9bb6841d7d98a5a8d53dc0a6612d75..98ec3b345da5cf70f5141a34c8ca6c99dec7629c 100644 --- a/LCS/MessageBus/test/tMessage.cc +++ b/LCS/MessageBus/test/tMessage.cc @@ -26,6 +26,8 @@ #include <UnitTest++.h> #include <MessageBus/Message.h> +#include <iostream> + using namespace LOFAR; using namespace std; @@ -45,6 +47,9 @@ SUITE(MessageContent) { CHECK_EQUAL("1.2", msg.protocolVersion.get()); CHECK_EQUAL("MOMID", msg.momid.get()); CHECK_EQUAL("SASID", msg.sasid.get()); + + std::cout << msg << std::endl; + std::cout << msg.qpidMsg().getContent() << std::endl; } TEST(existingmsg) { @@ -61,6 +66,14 @@ SUITE(MessageContent) { CHECK_EQUAL(orig.momid, copy.momid); CHECK_EQUAL(orig.sasid, copy.sasid); } + + TEST(modifymsg) { + MessageContent orig("NAME", "USER", "SUMMARY", "PROTOCOL", "1.2", "MOMID", "SASID"); + + orig.protocolVersion = "1.3"; + + CHECK_EQUAL("1.3", orig.protocolVersion.get()); + } } int main() { diff --git a/lofar_config.h.cmake b/lofar_config.h.cmake index 46dbc719dbc0acee012f0fdc603331e08256d6e7..6ff3b27b94375aef27646ac65500b94fd0e3de61 100644 --- a/lofar_config.h.cmake +++ b/lofar_config.h.cmake @@ -130,7 +130,7 @@ #cmakedefine HAVE_LIBSSH2 1 /* Define if libxml++ is installed */ -#cmakedefine HAVE_LIBXML_CPP 1 +#cmakedefine HAVE_LIBXMLXX 1 /* Define if LOG4CPLUS is installed */ #cmakedefine HAVE_LOG4CPLUS 1