/
zOMGdev
/
cppcms
Обзор
Документация
Войти
/
zOMGdev
/
cppcms
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v2.0.0.beta2
src/views_pool.cpp
432 строки
11 KB
Artyom Beilis
Fixed windows category
18 авг 2020, 09:11
18 авг 2020, 09:11
22d482f
Код
Авторство
О чём код?
/////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com> // // See accompanying file COPYING.TXT file for licensing details. // /////////////////////////////////////////////////////////////////////////////// #define CPPCMS_SOURCE #include <cppcms/views_pool.h> #include <cppcms/json.h> #include <cppcms/config.h> #include <cppcms/cppcms_error.h> #include <booster/locale/format.h> #include <booster/shared_ptr.h> #include <booster/thread.h> #include <booster/system_error.h> #include <booster/log.h> #ifdef CPPCMS_WIN_NATIVE #include <booster/nowide/convert.h> #include <windows.h> #include <process.h> #else #include <dlfcn.h> #endif #include <errno.h> #include <sys/types.h> #include <sys/stat.h> namespace cppcms { namespace views { // class generator struct generator::data {}; generator::generator() { } generator::~generator() { } void generator::name(std::string const &n) { name_ = n; } std::string generator::name() const { return name_; } void generator::add_factory(std::string const &n,generator::view_factory_type *factory) { views_[n]=factory; } std::unique_ptr<base_view> generator::create( std::string const &view_name, std::ostream &output, base_content *content) const { std::unique_ptr<base_view> result; views_type::const_iterator p = views_.find(view_name); if(p==views_.end()) return result; result = p->second(output,content); return result; } std::vector<std::string> generator::enumerate() const { std::vector<std::string> all; all.reserve(views_.size()); for(views_type::const_iterator p=views_.begin(),e=views_.end();p!=e;++p) { all.push_back(p->first); } return all; } // class pool struct pool::data { booster::recursive_shared_mutex lock; typedef std::map<std::string, generator const *> skin_generators_type; typedef std::map<std::string,skin_generators_type> generators_type; generators_type generators; }; void pool::add(generator const &g) { generator const *ptr = &g; std::string name = ptr->name(); booster::unique_lock<booster::recursive_shared_mutex> guard(d->lock); data::generators_type::iterator s = d->generators.find(name); if (s == d->generators.end()) { d->generators[name] = data::skin_generators_type(); s = d->generators.find(name); } std::vector<std::string> views = ptr->enumerate(); for(std::vector<std::string>::iterator v=views.begin(), e=views.end();v!=e;++v) { data::skin_generators_type& skin = s->second; skin[*v] = ptr; } } void pool::remove(generator const &g) { generator const *ptr = &g; std::string name = ptr->name(); booster::unique_lock<booster::recursive_shared_mutex> guard(d->lock); data::generators_type::iterator s = d->generators.find(name); if (s == d->generators.end()) return; data::skin_generators_type& skin = s->second; std::vector<std::string> views = ptr->enumerate(); for(std::vector<std::string>::iterator v=views.begin(), e=views.end();v!=e;++v) { if (skin[*v] == ptr) skin.erase(*v); } if (skin.empty()) d->generators.erase(name); } base_view *pool::create_view(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content) { data::generators_type::iterator s=d->generators.find(skin); if(s==d->generators.end()) throw cppcms_error("cppcms::views::pool: no such skin:" + skin); data::skin_generators_type const& reg_skin = s->second; data::skin_generators_type::const_iterator t=reg_skin.find(template_name); if(t==reg_skin.end()) throw cppcms_error("cppcms::view::pool: no suck view:" + template_name + " is registered for skin: " + skin); std::unique_ptr<base_view> v; v = t->second->create(template_name,out,&content); if(!v.get()) throw cppcms_error("cppcms::views::pool: no such view " + template_name + " in the skin " + skin); return v.release(); } void pool::lock() { d->lock.shared_lock(); } void pool::unlock() { d->lock.unlock(); } struct view_lock::_data {}; view_lock::view_lock(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content) { pool &p = pool::instance(); p.lock(); try { view_.reset(p.create_view(skin,template_name,out,content)); } catch(...) { p.unlock(); throw; } } view_lock::~view_lock() { view_.reset(); pool::instance().unlock(); } base_view &view_lock::view() { return *view_; } void pool::render(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content) { booster::shared_lock<booster::recursive_shared_mutex> guard(d->lock); { booster::hold_ptr<base_view> v(create_view(skin,template_name,out,content)); v->render(); } } std::vector<std::string> pool::enumerate() { booster::shared_lock<booster::recursive_shared_mutex> guard(d->lock); std::vector<std::string> all; all.reserve(d->generators.size()); for(data::generators_type::iterator p=d->generators.begin(),e=d->generators.end();p!=e;++p) { all.push_back(p->first); } return all; } pool::pool() : d(new data) {} pool::~pool() {} pool &pool::instance() { static pool instance_; return instance_; } namespace { struct initializer { initializer() { pool::instance(); } } initializer_instance; } namespace impl { #ifdef CPPCMS_WIN_NATIVE class shared_object : public booster::noncopyable { public: static std::string name(std::string file,std::string path); shared_object(std::string u8file_name,bool reloadable) { std::wstring file_name = booster::nowide::convert(u8file_name); if(reloadable) { int pid=_getpid(); file_name_ = (booster::locale::wformat(L"{1}.tmp-{2}.dll") % file_name % pid).str(std::locale::classic()); if(!CopyFileW(file_name.c_str(),file_name_.c_str(),1)) { booster::system::error_code e(GetLastError(),booster::system::system_category()); throw booster::system::system_error(e,"Failed to copy file "+u8file_name+" to " +booster::nowide::convert(file_name_)); } remove_=true; } else { file_name_ = file_name; remove_=false; } handler_ = LoadLibraryW(file_name_.c_str()); if(!handler_) { if(remove_) DeleteFileW(file_name_.c_str()); booster::system::error_code e(GetLastError(),booster::system::system_category()); throw booster::system::system_error(e,"Failed to load library "+u8file_name); } } ~shared_object() { FreeLibrary(handler_); if(remove_) { DeleteFileW(file_name_.c_str()); } } FARPROC symbol(std::string const &name) const { return GetProcAddress(handler_,name.c_str()); } private: HMODULE handler_; std::wstring file_name_; bool remove_; }; #else class shared_object : public booster::noncopyable { public: static std::string name(std::string file,std::string path); shared_object(std::string file_name,bool /*unused*/) { handler_ = dlopen(file_name.c_str(),RTLD_LAZY | RTLD_GLOBAL); if(!handler_) { booster::system::error_code e(errno,booster::system::system_category()); throw booster::system::system_error(e,"Failed to load library "+file_name); } } ~shared_object() { dlclose(handler_); } void *symbol(std::string const &name) const { return dlsym(handler_,name.c_str()); } private: void *handler_; }; #endif time_t get_mtime(std::string const &file_name) { #ifdef CPPCMS_WIN_NATIVE struct _stat st; if(_wstat(booster::nowide::convert(file_name).c_str(),&st) < 0) return 0; #else struct stat st; if(stat(file_name.c_str(),&st) < 0) return 0; #endif return st.st_mtime; } struct skin { std::string file_name; booster::shared_ptr<impl::shared_object> so; time_t mtime; }; std::string default_library_pattern() { #ifdef CPPCMS_LIBRARY_PREFIX return CPPCMS_LIBRARY_PREFIX "{1}" CPPCMS_LIBRARY_SUFFIX; #else return "{1}" CPPCMS_LIBRARY_SUFFIX; #endif } } // impl struct manager::data { bool auto_reload; std::string default_skin; std::vector<impl::skin> skins; booster::recursive_shared_mutex lock; data() : auto_reload(false) { } }; manager::manager(json::value const &settings) : d(new data()) { // configure all d->auto_reload = settings.get("views.auto_reload",false); std::string pattern = settings.get("views.shared_object_pattern",impl::default_library_pattern()); std::vector<std::string> paths=settings.get("views.paths",std::vector<std::string>()); std::vector<std::string> skins=settings.get("views.skins",std::vector<std::string>()); if(!skins.empty() && paths.empty()) { throw cppcms_error("When views.skins provided at least one search path should be given in views.paths"); } d->default_skin = settings.get<std::string>("views.default_skin",""); if(d->default_skin.empty()) { if(!skins.empty()) d->default_skin = skins.front(); else { std::vector<std::string> static_skins = pool::instance().enumerate(); if(static_skins.size()==1) d->default_skin = static_skins[0]; } } // configuration done now load all for(unsigned i=0;i<skins.size();i++) { std::string name=skins[i]; unsigned j; for(j=0;j<paths.size();j++) { impl::skin new_skin; new_skin.file_name = paths[j] + "/" + (booster::locale::format(pattern) % name).str(); new_skin.mtime = impl::get_mtime(new_skin.file_name); if(new_skin.mtime!=0) { new_skin.so.reset(new impl::shared_object(new_skin.file_name,d->auto_reload)); d->skins.push_back(new_skin); break; } } if(j == paths.size()) { throw cppcms_error("Failed to load skin:" + name+ ", no shared object/dll found"); } } } manager::~manager() { } std::string manager::default_skin() { return d->default_skin; } void manager::render(std::string const &skin_name,std::string const &template_name,std::ostream &out,base_content &content) { if(skin_name.empty() && d->default_skin.empty()) { throw cppcms_error("No default skin was detected, please define one in views.default_skin"); } if(d->auto_reload) { { // Check if update bool reload_required = false; booster::shared_lock<booster::recursive_shared_mutex> guard(d->lock); for(size_t i=0;i<d->skins.size();i++) { time_t mtime = impl::get_mtime(d->skins[i].file_name); if(mtime != d->skins[i].mtime) { reload_required = true; break; } } if(!reload_required) { pool::instance().render(skin_name,template_name,out,content); return; } } // reload all if needed booster::unique_lock<booster::recursive_shared_mutex> lock(d->lock); for(size_t i=0;i<d->skins.size();i++) { impl::skin ¤t = d->skins[i]; time_t mtime = impl::get_mtime(current.file_name); if(mtime == current.mtime) { continue; } BOOSTER_DEBUG("cppcms") << "Reloading shared object/dll " << current.file_name; current.so.reset(); current.mtime = mtime; current.so.reset(new impl::shared_object(current.file_name,true)); } pool::instance().render(skin_name,template_name,out,content); return; } pool::instance().render(skin_name,template_name,out,content); } } // views } // cppcms