Browse Source

Added new plugin API and test

master
Artyom Beilis 8 years ago
parent
commit
76474a2a26
5 changed files with 261 additions and 0 deletions
  1. +9
    -0
      CMakeLists.txt
  2. +125
    -0
      cppcms/plugin.h
  3. +76
    -0
      src/plugin.cpp
  4. +37
    -0
      tests/plugin_test.cpp
  5. +14
    -0
      tests/test_plugin_so.cpp

+ 9
- 0
CMakeLists.txt View File

@@ -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


+ 125
- 0
cppcms/plugin.h View File

@@ -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

+ 76
- 0
src/plugin.cpp View File

@@ -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

+ 37
- 0
tests/plugin_test.cpp View File

@@ -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;
}

+ 14
- 0
tests/test_plugin_so.cpp View File

@@ -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)

Loading…
Cancel
Save