Loadable filter support for FilterFactory using dlopen/dlsym. Only
authorAdam Dickmeiss <adam@indexdata.dk>
Sat, 10 Dec 2005 09:59:10 +0000 (09:59 +0000)
committerAdam Dickmeiss <adam@indexdata.dk>
Sat, 10 Dec 2005 09:59:10 +0000 (09:59 +0000)
works for Linux and similar systems. Added a test it in
test_filter_factory. Also extended test_filter_factory to call the
loadable filters (to ensure they are _right_).

src/Makefile.am
src/filter.hpp
src/filter_dl.cpp [new file with mode: 0644]
src/filter_factory.cpp
src/filter_factory.hpp
src/test_filter_factory.cpp
src/tstdl.cpp [new file with mode: 0644]

index d3924c1..197c06a 100644 (file)
@@ -1,4 +1,4 @@
-## $Id: Makefile.am,v 1.37 2005-12-02 12:21:07 adam Exp $
+## $Id: Makefile.am,v 1.38 2005-12-10 09:59:10 adam Exp $
 
 MAINTAINERCLEANFILES = Makefile.in config.in config.hpp
 
@@ -9,7 +9,7 @@ EXTRA_DIST=Jamfile.in
 # Rules for the library..
 
 lib_LTLIBRARIES = libyp2.la
-libyp2_la_LDFLAGS = -version-info 0:0:0
+libyp2_la_LDFLAGS = -version-info 0:0:0 -export-dynamic
 
 libyp2_la_SOURCES = \
        session.cpp session.hpp \
@@ -33,10 +33,19 @@ libyp2_la_SOURCES = \
 LDADD= libyp2.la $(YAZPPLALIB) $(XSLT_LIBS)
 
 bin_PROGRAMS =
-noinst_PROGRAMS = ex_filter_frontend_net ex_router_flexml
+noinst_PROGRAMS = ex_filter_frontend_net ex_router_flexml tstdl
 
 ex_filter_frontend_net_SOURCES = ex_filter_frontend_net.cpp
 ex_router_flexml_SOURCES =  ex_router_flexml.cpp
+tstdl_SOURCES = tstdl.cpp
+
+# Rules for dl programs
+pkglib_LTLIBRARIES = yp2_filter_dl.la
+
+yp2_filter_dl_la_SOURCES = filter_dl.cpp
+yp2_filter_dl_la_LDFLAGS = -rpath $(pkglibdir) -module -avoid-version
+yp2_filter_dl_la_LIBADD = libyp2.la
+
 # Rules for test programs..
 
 check_PROGRAMS = \
@@ -67,6 +76,7 @@ test_boost_threads_SOURCES=test_boost_threads.cpp
 test_boost_time_SOURCES=test_boost_time.cpp
 test_thread_pool_observer_SOURCES = test_thread_pool_observer.cpp
 test_filter_factory_SOURCES = test_filter_factory.cpp
+test_filter_factory_LDFLAGS = -export-dynamic
 test_filter_frontend_net_SOURCES = test_filter_frontend_net.cpp
 test_filter_log_SOURCES = test_filter_log.cpp
 test_filter_z3950_client_SOURCES = test_filter_z3950_client.cpp
index 29f5216..c2855bd 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: filter.hpp,v 1.9 2005-11-03 14:45:15 adam Exp $
+/* $Id: filter.hpp,v 1.10 2005-12-10 09:59:10 adam Exp $
    Copyright (c) 2005, Index Data.
 
 %LICENSE%
@@ -23,12 +23,7 @@ namespace yp2 {
             ///sends Package off to next Filter, returns altered Package
             virtual void process(Package & package) const = 0;
 
-            virtual void configure(const xmlNode * ptr = 0) { };
-        };
-
-        struct Creator {
-            const char* type;
-            yp2::filter::Base* (*creator)();
+            virtual void configure(const xmlNode * ptr) { };
         };
 
         class FilterException : public std::runtime_error {
@@ -39,9 +34,13 @@ namespace yp2 {
         };
         
     }
-  
 }
 
+struct yp2_filter_struct {
+    int ver;
+    yp2::filter::Base* (*creator)();
+};
+
 #endif
 /*
  * Local variables:
diff --git a/src/filter_dl.cpp b/src/filter_dl.cpp
new file mode 100644 (file)
index 0000000..7ce559b
--- /dev/null
@@ -0,0 +1,46 @@
+/* $Id: filter_dl.cpp,v 1.1 2005-12-10 09:59:10 adam Exp $
+   Copyright (c) 2005, Index Data.
+
+%LICENSE%
+ */
+
+#include "config.hpp"
+
+#include "filter.hpp"
+#include "router.hpp"
+#include "package.hpp"
+
+namespace yp2 {
+    namespace filter {
+        class Filter_dl: public yp2::filter::Base {
+        public:
+            void process(yp2::Package & package) const;
+        };
+    }
+}
+
+void yp2::filter::Filter_dl::process(yp2::Package & package) const
+{
+    package.data() = 42;
+}
+
+static yp2::filter::Base* filter_creator()
+{
+    return new yp2::filter::Filter_dl;
+}
+
+extern "C" {
+    struct yp2_filter_struct yp2_filter_dl = {
+        0,
+        filter_creator
+    };
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * c-file-style: "stroustrup"
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
index 6cbca63..a5406bd 100644 (file)
@@ -1,11 +1,16 @@
-/* $Id: filter_factory.cpp,v 1.1 2005-11-10 23:10:42 adam Exp $
+/* $Id: filter_factory.cpp,v 1.2 2005-12-10 09:59:10 adam Exp $
    Copyright (c) 2005, Index Data.
 
 %LICENSE%
  */
 
+#include "config.hpp"
+
 #include "filter_factory.hpp"
 
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
 #include <stdexcept>
 #include <iostream>
 #include <string>
@@ -13,6 +18,7 @@
 
 namespace yp2 {
     class FilterFactory::Rep {
+        typedef std::map<std::string, CreateFilterCallback> CallbackMap;
     public:
         friend class FilterFactory;
         CallbackMap m_fcm;
@@ -47,7 +53,7 @@ yp2::FilterFactory::~FilterFactory()
 bool yp2::FilterFactory::add_creator(std::string fi,
                                      CreateFilterCallback cfc)
 {
-    return m_p->m_fcm.insert(CallbackMap::value_type(fi, cfc)).second;
+    return m_p->m_fcm.insert(Rep::CallbackMap::value_type(fi, cfc)).second;
 }
 
 
@@ -58,7 +64,7 @@ bool yp2::FilterFactory::drop_creator(std::string fi)
 
 yp2::filter::Base* yp2::FilterFactory::create(std::string fi)
 {
-    CallbackMap::const_iterator it = m_p->m_fcm.find(fi);
+    Rep::CallbackMap::const_iterator it = m_p->m_fcm.find(fi);
     
     if (it == m_p->m_fcm.end()){
         std::string msg = "filter type '" + fi + "' not found";
@@ -68,6 +74,33 @@ yp2::filter::Base* yp2::FilterFactory::create(std::string fi)
     return (it->second());
 }
 
+#if HAVE_DLFCN_H
+bool yp2::FilterFactory::add_creator_dyn(const std::string &fi,
+                                         const std::string &path)
+{
+    std::string full_path = path + "/yp2_filter_" + fi + ".so";
+    void *dl_handle = dlopen(full_path.c_str(), RTLD_GLOBAL|RTLD_NOW);
+    if (!dl_handle)
+    {
+        const char *dl = dlerror();
+        std::cout << "dlopen " << full_path << " failed. dlerror=" << dl << 
+            std::endl;
+        return false;
+    }
+
+    std::string full_name = "yp2_filter_" + fi;
+    
+    void *dlsym_ptr = dlsym(dl_handle, full_name.c_str());
+    if (!dlsym_ptr)
+    {
+        std::cout << "dlsym " << full_name << " failed\n";
+        return false;
+    }
+    struct yp2_filter_struct *s = (struct yp2_filter_struct *) dlsym_ptr;
+    return add_creator(fi, s->creator);
+}
+#endif
+
 /*
  * Local variables:
  * c-basic-offset: 4
index 320f86f..c8b2251 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: filter_factory.hpp,v 1.6 2005-11-10 23:10:42 adam Exp $
+/* $Id: filter_factory.hpp,v 1.7 2005-12-10 09:59:10 adam Exp $
    Copyright (c) 2005, Index Data.
 
 %LICENSE%
@@ -28,7 +28,6 @@ namespace yp2 {
     class FilterFactory : public boost::noncopyable
     {
         typedef yp2::filter::Base* (*CreateFilterCallback)();
-        typedef std::map<std::string, CreateFilterCallback> CallbackMap;
 
         class Rep;
     public:
@@ -38,14 +37,12 @@ namespace yp2 {
         ~FilterFactory();
 
         bool add_creator(std::string fi, CreateFilterCallback cfc);
-        /// true if unregistration ok
         
         bool drop_creator(std::string fi);
         
-        /// factory create method
-        
         yp2::filter::Base* create(std::string fi);
-        
+
+        bool add_creator_dyn(const std::string &fi, const std::string &path);
     private:
         boost::scoped_ptr<Rep> m_p;
     };
index 7dfcaae..b9190e3 100644 (file)
@@ -1,16 +1,16 @@
-/* $Id: test_filter_factory.cpp,v 1.6 2005-12-02 12:21:07 adam Exp $
+/* $Id: test_filter_factory.cpp,v 1.7 2005-12-10 09:59:10 adam Exp $
    Copyright (c) 2005, Index Data.
 
 %LICENSE%
 
 */
 
-
 #include <iostream>
 #include <stdexcept>
 
 #include "config.hpp"
 #include "filter.hpp"
+#include "package.hpp"
 #include "filter_factory.hpp"
 
 
 
 using namespace boost::unit_test;
 
+// XFilter
 class XFilter: public yp2::filter::Base {
 public:
-    void process(yp2::Package & package) const {};
+    void process(yp2::Package & package) const;
 };
 
+void XFilter::process(yp2::Package & package) const
+{
+    package.data() = 1;
+}
 
-yp2::filter::Base* xfilter_creator(){
+static yp2::filter::Base* xfilter_creator(){
     return new XFilter;
 }
 
+// YFilter ...
 class YFilter: public yp2::filter::Base {
 public:
-    void process(yp2::Package & package) const {};
+    void process(yp2::Package & package) const;
 };
 
-yp2::filter::Base* yfilter_creator(){
-    return new YFilter;
+void YFilter::process(yp2::Package & package) const
+{
+    package.data() = 2;
 }
 
+static yp2::filter::Base* yfilter_creator(){
+    return new YFilter;
+}
 
 BOOST_AUTO_UNIT_TEST( test_filter_factory_1 )
 {
@@ -51,54 +61,57 @@ BOOST_AUTO_UNIT_TEST( test_filter_factory_1 )
         const std::string xfid = "XFilter";
         const std::string yfid = "YFilter";
         
-        //std::cout << "Xfilter name: " << xfid << std::endl;
-        //std::cout << "Yfilter name: " << yfid << std::endl;
-
-        BOOST_CHECK_EQUAL(ffactory.add_creator(xfid, xfilter_creator),
-                          true);
-        BOOST_CHECK_EQUAL(ffactory.drop_creator(xfid),
-                          true);
-        BOOST_CHECK_EQUAL(ffactory.add_creator(xfid, xfilter_creator),
-                          true);
-        BOOST_CHECK_EQUAL(ffactory.add_creator(yfid, yfilter_creator),
-                          true);
+        BOOST_CHECK(ffactory.add_creator(xfid, xfilter_creator));
+        BOOST_CHECK(ffactory.drop_creator(xfid));
+        BOOST_CHECK(ffactory.add_creator(xfid, xfilter_creator));
+        BOOST_CHECK(ffactory.add_creator(yfid, yfilter_creator));
         
         yp2::filter::Base* xfilter = 0;
         xfilter = ffactory.create(xfid);
         yp2::filter::Base* yfilter = 0;
         yfilter = ffactory.create(yfid);
 
-        //BOOST_CHECK_EQUAL(sizeof(xf), sizeof(*xfilter));
-        //BOOST_CHECK_EQUAL(sizeof(yf), sizeof(*yfilter));
-
         BOOST_CHECK(0 != xfilter);
         BOOST_CHECK(0 != yfilter);
+
+        yp2::Package pack;
+        xfilter->process(pack);
+        BOOST_CHECK_EQUAL(pack.data(), 1);
+
+        yfilter->process(pack);
+        BOOST_CHECK_EQUAL(pack.data(), 2);
     }
     catch ( ... ) {
         throw;
         BOOST_CHECK (false);
     }
-        
-    std::exit(0);
 }
 
-// get function - right val in assignment
-//std::string name() const {
-//return m_name;
-//  return "Base";
-//}
-
-// set function - left val in assignment
-//std::string & name() {
-//    return m_name;
-//}
+#if HAVE_DLFCN_H
+BOOST_AUTO_UNIT_TEST( test_filter_factory_2 )
+{
+    try {        
+        yp2::FilterFactory  ffactory;
+        
+        const std::string id = "dl";
+        
+        BOOST_CHECK(ffactory.add_creator_dyn(id, ".libs"));
+        
+        yp2::filter::Base* filter = 0;
+        filter = ffactory.create(id);
 
-// set function - can be chained
-//Base & name(const std::string & name){
-//  m_name = name;
-//  return *this;
-//}
+        BOOST_CHECK(0 != filter);
 
+        yp2::Package pack;
+        filter->process(pack);
+        BOOST_CHECK_EQUAL(pack.data(), 42); // magic from filter_dl ..
+    }
+    catch ( ... ) {
+        throw;
+        BOOST_CHECK (false);
+    }
+}
+#endif
 
 /*
  * Local variables:
diff --git a/src/tstdl.cpp b/src/tstdl.cpp
new file mode 100644 (file)
index 0000000..4f3eb66
--- /dev/null
@@ -0,0 +1,49 @@
+/* $Id: tstdl.cpp,v 1.1 2005-12-10 09:59:10 adam Exp $
+   Copyright (c) 2005, Index Data.
+
+%LICENSE%
+ */
+
+#include "config.hpp"
+
+#include <stdlib.h>
+#include <iostream>
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+using namespace std;
+
+int main(int argc, char **argv)
+{
+#if HAVE_DLFCN_H
+    if (argc != 3)
+    {
+       cerr << "bad args" << endl << "Usage: tstdl filename symbol" << endl;
+       exit(1);
+    }
+    void *mod = dlopen(argv[1][0] ? argv[1] : 0, RTLD_NOW|RTLD_LOCAL);
+    if (!mod)
+    {
+       cerr << "dlopen failed for file " << argv[1] << 
+           "dlerror=" << dlerror() << endl;
+       exit(1);
+    }
+    void *sym = dlsym(mod, argv[2]);
+    printf("sym=%p\n", sym);
+    dlclose(mod);
+    exit(0);
+#else
+    cerr << "dl lib not enabled or not supported" << endl;
+    exit(1);
+#endif
+
+}
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * c-file-style: "stroustrup"
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */