@@ -400,6 +400,7 @@ set(CPPCMS_SOURCES | |||
src/copy_filter.cpp | |||
src/send_timeout.cpp | |||
src/http_content_filter.cpp | |||
src/plugin.cpp | |||
src/capi/session.cpp | |||
) | |||
@@ -644,6 +645,12 @@ if(NOT DISABLE_SHARED) | |||
set_target_properties(${SKIN} PROPERTIES COMPILE_DEFINITIONS DLL_EXPORT) | |||
endif() | |||
endforeach() | |||
add_library(plugin SHARED tests/test_plugin_so.cpp) | |||
target_link_libraries(plugin ${CPPCMS_LIB}) | |||
if(IS_WINDOWS) | |||
set_target_properties(plugin PROPERTIES COMPILE_DEFINITIONS DLL_EXPORT) | |||
endif() | |||
@@ -707,6 +714,7 @@ set(ALL_TESTS | |||
response_test | |||
file_buffer_test | |||
filter_test | |||
plugin_test | |||
) | |||
if(NOT DISABLE_PREFORK_CACHE AND NOT IS_WINDOWS) | |||
@@ -823,6 +831,7 @@ add_test(file_buffer_test file_buffer_test) | |||
if(NOT DISABLE_SHARED) | |||
add_test(tc_test_shared tc_test "--shared") | |||
add_test(tc_test_separate tc_test "--separate") | |||
add_test(plugin_test plugin_test) | |||
endif() | |||
add_test(status_test | |||
@@ -0,0 +1,125 @@ | |||
/////////////////////////////////////////////////////////////////////////////// | |||
// | |||
// Copyright (C) 2008-2016 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com> | |||
// | |||
// See accompanying file COPYING.TXT file for licensing details. | |||
// | |||
/////////////////////////////////////////////////////////////////////////////// | |||
#ifndef CPPCMS_PLUGIN_H | |||
#define CPPCMS_PLUGIN_H | |||
#include <cppcms/defs.h> | |||
#include <cppcms/cppcms_error.h> | |||
#include <booster/callback.h> | |||
#include <booster/hold_ptr.h> | |||
#include <string> | |||
namespace cppcms { | |||
namespace plugin { | |||
class CPPCMS_API manager { | |||
public: | |||
/// | |||
/// Get the instance of the manager | |||
/// | |||
static manager &instance(); | |||
typedef booster::intrusive_ptr<booster::refcounted> refcounted_ptr; | |||
/// | |||
/// Functions registered as plugin entry points | |||
/// | |||
typedef refcounted_ptr (*entry_point_type)(); | |||
/// | |||
/// Get plugin entry by plugin name, entry point name and type | |||
/// | |||
/// For example | |||
/// \code | |||
/// booster::callback<cppcms::application *(cppcms::service &)> cb = cppcms::plugin::manager::entry<cppcms::application *(cppcms::service &)>("foo","application"); | |||
/// cppcms::application *app =cb(service()); | |||
/// attach(app,"/plugins/foo(/.*)",1); // attach new application | |||
/// \endcode | |||
/// | |||
/// Or | |||
/// | |||
/// \code | |||
/// cppcms::application *app = cppcms::plugin::manager::entry<cppcms::application *(cppcms::service &)>("myapi","app::generator")(service()); | |||
/// attach(app,"/plugins/foo(/.*)",1); | |||
/// \endcode | |||
/// | |||
/// \ver{v1_2} | |||
template<typename Signature> | |||
static | |||
booster::callback<Signature> | |||
entry(std::string const &name) | |||
{ | |||
typedef booster::callback<Signature> callback_type; | |||
typedef typename callback_type::callable_type callable_type; | |||
typedef typename callback_type::pointer_type pointer_type; | |||
entry_point_type plugin_call = instance().get_entry(name); | |||
if(!plugin_call) | |||
throw cppcms_error("Could not find plugin `" + name + "'"); | |||
refcounted_ptr call = plugin_call(); | |||
if(!call) | |||
throw cppcms_error("Failed to create callback from `" + name + "'"); | |||
callable_type *real_call = dynamic_cast<callable_type *>(call.get()); | |||
if(!real_call) { | |||
throw booster::bad_cast(); | |||
} | |||
pointer_type ptr(real_call); | |||
callback_type result(ptr); | |||
return result; | |||
} | |||
/// | |||
/// Get entry point that creates a base of booster::callback::callable_type | |||
/// | |||
entry_point_type get_entry(std::string const &name); | |||
/// | |||
/// Addes entry to the plugin manager - thread safe function | |||
/// | |||
void add_entry(char const *name,entry_point_type entry); | |||
/// | |||
/// Removes entry from the plugin manager - thread safe function | |||
/// | |||
void remove_entry(entry_point_type entry); | |||
private: | |||
manager(); | |||
~manager(); | |||
manager(manager const &); | |||
void operator=(manager const &); | |||
struct _data; | |||
booster::hold_ptr<_data> d; | |||
}; | |||
#define CPPCMS_NAMED_PLUGIN_ENTRY(type,call,name) \ | |||
namespace { \ | |||
struct stpg_##__LINE__ { \ | |||
static booster::intrusive_ptr<booster::refcounted> entry() \ | |||
{ \ | |||
typedef booster::callback<type> ct; \ | |||
ct cb = &call; \ | |||
booster::refcounted *tmp = cb.get_pointer().get(); \ | |||
booster::intrusive_ptr<booster::refcounted> ptr(tmp); \ | |||
return ptr; \ | |||
} \ | |||
stpg_##__LINE__() { \ | |||
cppcms::plugin::manager::instance().add_entry(name,&entry); \ | |||
} \ | |||
~stpg_##__LINE__() { \ | |||
cppcms::plugin::manager::instance().remove_entry(&entry); \ | |||
} \ | |||
} instance_of_stpg_##__LINE__; \ | |||
} | |||
#define CPPCMS_PLUGIN_ENTRY(type,call) CPPCMS_NAMED_PLUGIN_ENTRY(type,call,#call) | |||
} // plugin | |||
} // cppcms | |||
#endif |
@@ -0,0 +1,76 @@ | |||
/////////////////////////////////////////////////////////////////////////////// | |||
// | |||
// Copyright (C) 2008-2016 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com> | |||
// | |||
// See accompanying file COPYING.TXT file for licensing details. | |||
// | |||
/////////////////////////////////////////////////////////////////////////////// | |||
#define CPPCMS_SOURCE | |||
#include <cppcms/plugin.h> | |||
#include <map> | |||
#include <booster/thread.h> | |||
namespace cppcms { | |||
namespace plugin { | |||
namespace { | |||
struct init { | |||
init() { manager::instance(); } | |||
} init_inst; | |||
} | |||
struct manager::_data { | |||
std::map<std::string,manager::entry_point_type> mapping; | |||
booster::mutex lock; | |||
}; | |||
manager &manager::instance() | |||
{ | |||
static manager m; | |||
return m; | |||
} | |||
manager::manager() : d(new manager::_data()) | |||
{ | |||
} | |||
manager::~manager() | |||
{ | |||
} | |||
void manager::add_entry(char const *name,manager::entry_point_type e) | |||
{ | |||
try { | |||
booster::unique_lock<booster::mutex> guard(d->lock); | |||
std::string key=name; | |||
d->mapping.insert(std::make_pair(key,e)); | |||
} | |||
catch(...){} | |||
} | |||
void manager::remove_entry(manager::entry_point_type e) | |||
{ | |||
try { | |||
booster::unique_lock<booster::mutex> guard(d->lock); | |||
for(std::map<std::string,manager::entry_point_type>::iterator p=d->mapping.begin();p!=d->mapping.end();++p) { | |||
if(p->second==e) { | |||
d->mapping.erase(p); | |||
return; | |||
} | |||
} | |||
} | |||
catch(...){} | |||
} | |||
manager::entry_point_type manager::get_entry(std::string const &name) | |||
{ | |||
booster::unique_lock<booster::mutex> guard(d->lock); | |||
std::map<std::string,manager::entry_point_type>::const_iterator p=d->mapping.find(name); | |||
if(p==d->mapping.end()) | |||
return 0; | |||
return p->second; | |||
} | |||
} // plugin | |||
} // cppcms |
@@ -0,0 +1,37 @@ | |||
#include <cppcms/plugin.h> | |||
#include <booster/shared_object.h> | |||
#include <iostream> | |||
#include "test.h" | |||
int main() | |||
{ | |||
try { | |||
booster::shared_object obj(booster::shared_object::name("plugin")); | |||
{ | |||
booster::callback<std::string(std::string const &)> cb; | |||
cb = cppcms::plugin::manager::entry<std::string(std::string const &)>("foo::lower"); | |||
TEST(cb("Hello World")=="hello world"); | |||
try { | |||
cppcms::plugin::manager::entry<std::string(std::string &)>("foo::lower"); | |||
TEST(!"Never Get There"); | |||
} | |||
catch(booster::bad_cast const &) {} | |||
catch(...) { throw std::runtime_error("Something else thrown"); } | |||
} | |||
obj.close(); | |||
try { | |||
cppcms::plugin::manager::entry<std::string(std::string const &)>("foo::lower"); | |||
std::cerr << "Must Not get there!" <<std::endl; | |||
return 1; | |||
} | |||
catch(cppcms::cppcms_error const &) {} | |||
catch(...) { throw std::runtime_error("Something else thrown"); } | |||
} | |||
catch(std::exception const &e) { | |||
std::cerr << "Error:" << e.what() << std::endl; | |||
return 1; | |||
} | |||
std::cout << "Ok" << std::endl; | |||
return 0; | |||
} |
@@ -0,0 +1,14 @@ | |||
#include <cppcms/plugin.h> | |||
#include <ctype.h> | |||
struct foo { | |||
static std::string lower(std::string f) | |||
{ | |||
for(size_t i=0;i<f.size();i++) { | |||
f[i]=tolower(f[i]); | |||
} | |||
return f; | |||
} | |||
}; | |||
CPPCMS_PLUGIN_ENTRY(std::string(std::string const &),foo::lower) |