From 8336dd20b4aacab1daa26c2be87792d20ccf74fc Mon Sep 17 00:00:00 2001
From: ilor <kailoran@gmail.com>
Date: Tue, 7 Jun 2011 13:21:23 +0200
Subject: [PATCH] Plugin infrastructure in corpus2 readers: plugin loading code
 in pwrnlp/plugin, chanegs all around to accomodate

---
 libcorpus2/io/reader.cpp   | 88 ++++++++++++++++++++++++++++++--------
 libcorpus2/io/reader.h     | 22 +++-------
 libpwrutils/CMakeLists.txt |  1 +
 libpwrutils/plugin.cpp     | 78 +++++++++++++++++++++++++++++++++
 libpwrutils/plugin.h       | 30 +++++++++++++
 poliqarp/CMakeLists.txt    | 10 ++---
 poliqarp/pqreader.cpp      | 11 +++++
 poliqarp/pqreader.h        |  5 +++
 swig/CMakeLists.txt        |  3 --
 9 files changed, 208 insertions(+), 40 deletions(-)
 create mode 100644 libpwrutils/plugin.cpp
 create mode 100644 libpwrutils/plugin.h

diff --git a/libcorpus2/io/reader.cpp b/libcorpus2/io/reader.cpp
index 46a158a..b3b12ac 100644
--- a/libcorpus2/io/reader.cpp
+++ b/libcorpus2/io/reader.cpp
@@ -19,9 +19,32 @@ or FITNESS FOR A PARTICULAR PURPOSE.
 #include <boost/algorithm/string.hpp>
 #include <libcorpus2/ann/annotatedsentence.h>
 #include <sstream>
+#include <libpwrutils/plugin.h>
+#include <libcorpus2/util/settings.h>
 
 namespace Corpus2 {
 
+namespace detail {
+/**
+ * Declaration of the TokenWriter factory as a singleton Loki object
+ * factory. The factory instance can be accessed as
+ * TokenLayerFactory::Instance(). It is assumed that all derived classes
+ * have the same constructor signature.
+ */
+typedef Loki::SingletonHolder<
+	TokenReaderFactory,
+	Loki::CreateUsingNew, // default, needed to change the item below
+	Loki::LongevityLifetime::DieAsSmallObjectChild // per libloki docs
+>
+TokenReaderFactorySingleton;
+
+TokenReaderFactory& token_reader_factory()
+{
+	return TokenReaderFactorySingleton::Instance();
+}
+} /* ned ns detail */
+
+
 TokenReader::TokenReader(const Tagset& tagset)
 	: tagset_(tagset), tag_parse_mode_(Tagset::ParseDefault),
 	use_annotated_sentences_(false)
@@ -76,6 +99,17 @@ boost::shared_ptr<Sentence> TokenReader::make_sentence() const
 	}
 }
 
+namespace {
+std::string guess_plugin_name(const std::string& reader_class_id, int idx)
+{
+	switch (idx) {
+		case 0: return reader_class_id + "reader";
+		case 1: return reader_class_id;
+		default: return "";
+	}
+}
+}
+
 boost::shared_ptr<TokenReader> TokenReader::create_path_reader(
 	const std::string& class_id_params,
 	const Tagset& tagset,
@@ -86,13 +120,23 @@ boost::shared_ptr<TokenReader> TokenReader::create_path_reader(
 							boost::is_any_of(std::string(",")));
 	std::string class_id = boost::copy_range<std::string>(params[0]);
 	params.erase(params.begin(), params.begin() + 1);
-	try {
-		return boost::shared_ptr<TokenReader>(
-		detail::TokenReaderFactorySingleton::Instance().path_factory.CreateObject(
-			class_id, tagset, path, params));
-	} catch (detail::TokenReaderFactoryException&) {
-		throw Corpus2Error("Reader class not found: " + class_id);
+	int plugin_name_idx = 0;
+	while (plugin_name_idx >=0) {
+		try {
+			return boost::shared_ptr<TokenReader>(
+				detail::TokenReaderFactorySingleton::Instance().
+				path_factory.CreateObject(class_id, tagset, path, params));
+		} catch (detail::TokenReaderFactoryException&) {
+			std::string next_plugin = guess_plugin_name(class_id, plugin_name_idx);
+			if (!next_plugin.empty()) {
+				PwrNlp::Plugin::load("corpus2", next_plugin, !Path::Instance().get_verbose());
+				plugin_name_idx++;
+			} else {
+				plugin_name_idx = -1;
+			}
+		}
 	}
+	throw Corpus2Error("Reader class not found: " + class_id);
 }
 
 boost::shared_ptr<TokenReader> TokenReader::create_stream_reader(
@@ -105,19 +149,29 @@ boost::shared_ptr<TokenReader> TokenReader::create_stream_reader(
 							boost::is_any_of(std::string(",")));
 	std::string class_id = boost::copy_range<std::string>(params[0]);
 	params.erase(params.begin(), params.begin() + 1);
-	try {
-		return boost::shared_ptr<TokenReader>(
-		detail::TokenReaderFactorySingleton::Instance().stream_factory.CreateObject(
-			class_id, tagset, stream, params));
-	} catch (detail::TokenReaderFactoryException& e) {
-		std::vector<std::string> ids;
-		ids = detail::TokenReaderFactorySingleton::Instance().path_factory.RegisteredIds();
-		if (std::find(ids.begin(), ids.end(), class_id) == ids.end()) {
-			throw Corpus2Error("Reader class not found: " + class_id);
-		} else {
-			throw Corpus2Error("This reader does not support stream mode: " + class_id);
+	int plugin_name_idx = 0;
+	while (plugin_name_idx >=0) {
+		try {
+			return boost::shared_ptr<TokenReader>(
+			detail::TokenReaderFactorySingleton::Instance()
+			.stream_factory.CreateObject(class_id, tagset, stream, params));
+		} catch (detail::TokenReaderFactoryException&) {
+			std::string next_plugin = guess_plugin_name(class_id, plugin_name_idx);
+			if (!next_plugin.empty()) {
+				PwrNlp::Plugin::load("corpus2", next_plugin, !Path::Instance().get_verbose());
+				plugin_name_idx++;
+			} else {
+				plugin_name_idx = -1;
+			}
 		}
 	}
+	std::vector<std::string> ids;
+	ids = detail::TokenReaderFactorySingleton::Instance().path_factory.RegisteredIds();
+	if (std::find(ids.begin(), ids.end(), class_id) == ids.end()) {
+		throw Corpus2Error("Reader class not found: " + class_id);
+	} else {
+		throw Corpus2Error("This reader does not support stream mode: " + class_id);
+	}
 }
 
 std::vector<std::string> TokenReader::available_reader_types()
diff --git a/libcorpus2/io/reader.h b/libcorpus2/io/reader.h
index 2c2ddec..e9d01f6 100644
--- a/libcorpus2/io/reader.h
+++ b/libcorpus2/io/reader.h
@@ -203,17 +203,9 @@ struct TokenReaderFactory
 };
 
 /**
- * Declaration of the TokenWriter factory as a singleton Loki object
- * factory. The factory instance can be accessed as
- * TokenLayerFactory::Instance(). It is assumed that all derived classes
- * have the same constructor signature.
+ * Factory singleton accesor
  */
-typedef Loki::SingletonHolder<
-	TokenReaderFactory,
-	Loki::CreateUsingNew, // default, needed to change the item below
-	Loki::LongevityLifetime::DieAsSmallObjectChild // per libloki docs
->
-TokenReaderFactorySingleton;
+TokenReaderFactory& token_reader_factory();
 
 /**
  * Templated TokenReader creation function, stream variant
@@ -262,12 +254,12 @@ template <typename T>
 bool TokenReader::register_reader(const std::string& class_id,
 		const std::string& help)
 {
-	bool ret = detail::TokenReaderFactorySingleton::Instance().path_factory.Register(
+	bool ret = detail::token_reader_factory().path_factory.Register(
 			class_id, detail::path_reader_creator<T>);
-	bool ret2 = detail::TokenReaderFactorySingleton::Instance().stream_factory.Register(
+	bool ret2 = detail::token_reader_factory().stream_factory.Register(
 			class_id, detail::stream_reader_creator<T>);
 	if (ret || ret2) {
-		detail::TokenReaderFactorySingleton::Instance().help[class_id] = help;
+		detail::token_reader_factory().help[class_id] = help;
 	}
 	return ret;
 }
@@ -276,10 +268,10 @@ template <typename T>
 bool TokenReader::register_path_reader(const std::string& class_id,
 		const std::string& help)
 {
-	bool ret = detail::TokenReaderFactorySingleton::Instance().path_factory.Register(
+	bool ret = detail::token_reader_factory().path_factory.Register(
 			class_id, detail::path_reader_creator<T>);
 	if (ret) {
-		detail::TokenReaderFactorySingleton::Instance().help[class_id] = help;
+		detail::token_reader_factory().help[class_id] = help;
 	}
 	return ret;
 }
diff --git a/libpwrutils/CMakeLists.txt b/libpwrutils/CMakeLists.txt
index 56f110e..69a9912 100644
--- a/libpwrutils/CMakeLists.txt
+++ b/libpwrutils/CMakeLists.txt
@@ -29,6 +29,7 @@ SET(libpwrutils_STAT_SRC
 	exception.cpp
 	whitespace.cpp
 	pathsearch.cpp
+	plugin.cpp
 	plural.cpp
 	util.cpp
 )
diff --git a/libpwrutils/plugin.cpp b/libpwrutils/plugin.cpp
new file mode 100644
index 0000000..157aac4
--- /dev/null
+++ b/libpwrutils/plugin.cpp
@@ -0,0 +1,78 @@
+#include <libpwrutils/plugin.h>
+#include <dlfcn.h>
+#include <iostream>
+
+namespace PwrNlp {
+namespace Plugin {
+
+std::string make_soname(const std::string &scope, const std::string &name)
+{
+	if (name.size() > 1 && name.find('/') != name.npos) {
+		return name;
+	} else {
+		return "lib" + scope + "_" + name + ".so";
+	}
+}
+
+bool load(const std::string &scope, const std::string &name, bool quiet)
+{
+	std::string soname = make_soname(scope, name);
+	// std::cerr << "PLUGIN LOAD " << scope << " " << name << " " << soname << "\n";
+	// first check if the plugin was already loaded
+	void* handle = dlopen(soname.c_str(), RTLD_NOW | RTLD_NOLOAD);
+	if (handle != NULL) {
+		if (!quiet) {
+			std::cerr << "Warning: " << scope << " plugin '" << name
+				<< "'' already loaded\n";
+		}
+		return false;
+	}
+	// actually load the library
+	dlerror();
+	handle = dlopen(soname.c_str(), RTLD_NOW);
+	if (handle == NULL) {
+		if (!quiet) {
+			const char* dle = dlerror();
+			std::cerr << "Error: dlopen error while loading " << scope
+				<< " plugin '" << name << "' (" << soname << "): ";
+			if (dle != NULL) {
+				std::cerr << dle << "\n";
+			}
+		}
+		return false;
+	}
+	// run plugin init function if it exiests
+	typedef void (*init_func_t)();
+	init_func_t init_func = reinterpret_cast<init_func_t>(
+			dlsym(handle, "pwrnlp_plugin_init"));
+	if (init_func) {
+		init_func();
+	}
+	if (!quiet) {
+		std::cerr << "Loaded " << scope << " plugin '" << name << "'\n";
+	}
+	return true;
+}
+
+bool load_check(const std::string &scope, const std::string &name, bool quiet,
+		boost::function<size_t (void)> counter, const std::string &what)
+{
+	size_t before = counter();
+	if (load(scope, name, quiet)) {
+		size_t after = counter();
+		if (after <= before) {
+			if (!quiet) {
+				std::cerr << "Warning: " << scope << " plugin '"
+					<< name << "'' loaded, but"
+					<< what << " count did not increase\n";
+			}
+			return false;
+		}
+		return true;
+	} else {
+		return false;
+	}
+}
+
+} /* end ns Plugin */
+} /* end ns PwrNlp */
diff --git a/libpwrutils/plugin.h b/libpwrutils/plugin.h
new file mode 100644
index 0000000..4259530
--- /dev/null
+++ b/libpwrutils/plugin.h
@@ -0,0 +1,30 @@
+#ifndef LIBPWRNLP_PLUGIN_H
+#define LIBPWRNLP_PLUGIN_H
+
+#include <boost/function.hpp>
+
+namespace PwrNlp {
+namespace Plugin {
+
+/**
+ * Convert a plugin name to a shared library name that is expected to
+ * contain the plugin.
+ */
+std::string make_soname(const std::string& scope, const std::string& name);
+
+/**
+ * Load a plugin
+ */
+bool load(const std::string& scope, const std::string& name, bool quiet);
+
+/**
+ * Load a plugin, checking if a counter increases after the load,
+ * and outputting a disgnostic message if it does not
+ */
+bool load_check(const std::string& scope, const std::string& name, bool quiet,
+		boost::function<size_t(void)> counter, const std::string& what);
+
+} /* end ns Plugin */
+} /* end ns PwrNlp */
+
+#endif // LIBPWRNLP_PLUGIN_H
diff --git a/poliqarp/CMakeLists.txt b/poliqarp/CMakeLists.txt
index b1a51a2..4fb5827 100644
--- a/poliqarp/CMakeLists.txt
+++ b/poliqarp/CMakeLists.txt
@@ -7,16 +7,16 @@ include_directories(${PoliqarpLibrary_SOURCE_DIR})
 include_directories(${PoliqarpLibrary_BINARY_DIR}/sakura)
 include_directories(${PoliqarpLibrary_BINARY_DIR})
 
-add_library(corpus2poliqarp SHARED pqclient.cpp pqreader.cpp)
-set_target_properties(corpus2poliqarp PROPERTIES
+add_library(corpus2_poliqarpreader SHARED pqclient.cpp pqreader.cpp)
+set_target_properties(corpus2_poliqarpreader PROPERTIES
 	VERSION "${c2pq_ver_major}.${c2pq_ver_minor}"
 	SOVERSION ${c2pq_ver_major})
-target_link_libraries(corpus2poliqarp poliqarpc2 corpus2)
+target_link_libraries(corpus2_poliqarpreader poliqarpc2 corpus2)
 
 add_executable(c2pqtest c2pqtest.cpp)
-target_link_libraries(c2pqtest poliqarpc2 corpus2poliqarp corpus2 pwrutils )
+target_link_libraries(c2pqtest poliqarpc2 corpus2_poliqarpreader corpus2 pwrutils )
 
 if(UNIX)
-	install(TARGETS corpus2poliqarp LIBRARY DESTINATION lib)
+	install(TARGETS corpus2_poliqarpreader LIBRARY DESTINATION lib)
 	install(TARGETS c2pqtest RUNTIME DESTINATION bin)
 endif(UNIX)
diff --git a/poliqarp/pqreader.cpp b/poliqarp/pqreader.cpp
index 400e9b5..6334b4f 100644
--- a/poliqarp/pqreader.cpp
+++ b/poliqarp/pqreader.cpp
@@ -1,6 +1,17 @@
 #include "pqreader.h"
 #include "pqclient.h"
 
+/*
+extern "C" {
+void pwrnlp_plugin_init()
+{
+	std::cerr << "PQINIT\n";
+	Corpus2::TokenReader::register_path_reader<Corpus2::PoliqarpReader>(
+		"poliqarp","token,chunk,sentence");
+}
+}
+*/
+
 namespace Corpus2 {
 
 bool PoliqarpReader::registered = TokenReader::register_path_reader<PoliqarpReader>(
diff --git a/poliqarp/pqreader.h b/poliqarp/pqreader.h
index 35acf22..a8fb40b 100644
--- a/poliqarp/pqreader.h
+++ b/poliqarp/pqreader.h
@@ -5,6 +5,11 @@
 #include <deque>
 #include <boost/scoped_ptr.hpp>
 
+/*
+extern "C" {
+void pwrnlp_plugin_init();
+}
+*/
 
 namespace Corpus2 {
 
diff --git a/swig/CMakeLists.txt b/swig/CMakeLists.txt
index bf78211..1221f86 100644
--- a/swig/CMakeLists.txt
+++ b/swig/CMakeLists.txt
@@ -3,9 +3,6 @@
 PROJECT(corpus2SwigWrap)
 
 set(LIBS "corpus2" "pwrutils")
-if (CORPUS2_BUILD_POLIQARP)
-	set(LIBS ${LIBS} "corpus2poliqarp")
-endif (CORPUS2_BUILD_POLIQARP)
 
 include_directories (${corpus2_SOURCE_DIR})
 include_directories (${pwrutils_SOURCE_DIR})
-- 
GitLab