|
- #define CPPCMS_SOURCE
- #include "views_pool.h"
- #include "json.h"
- #include "config.h"
- #include "cppcms_error.h"
- #ifdef CPPCMS_USE_EXTERNAL_BOOST
- # include <boost/thread.hpp>
- # include <boost/format.hpp>
- #else // Internal Boost
- # include <cppcms_boost/thread.hpp>
- # include <cppcms_boost/format.hpp>
- namespace boost = cppcms_boost;
- #endif
-
- #ifdef CPPCMS_WIN32
- #include <windows.h>
- #include <process.h>
- #else
- #include <dlfcn.h>
- #endif
-
- #include <sys/types.h>
- #include <sys/stat.h>
-
-
-
- namespace cppcms {
- namespace impl {
- #ifdef CPPCMS_WIN32
- class shared_object : public util::noncopyable {
- public:
- static std::string name(std::string file,std::string path);
-
- shared_object(std::string file_name,bool reloadable)
- {
- if(reloadable) {
- #ifdef CPPCMS_WIN_NATIVE
- int pid=_getpid();
- #else
- int pid=getpid();
- #endif
- file_name_ = (boost::format("%1%.tmp-%2%.dll",std::locale::classic()) % file_name % pid).str();
- if(!CopyFile(file_name.c_str(),file_name_.c_str(),1)) {
- throw cppcms_error("Failed to copy file "+file_name+" to "+file_name_);
- }
- remove_=true;
- }
- else {
- file_name_ = file_name;
- remove_=false;
- }
- handler_ = LoadLibrary(file_name_.c_str());
- if(!handler_) {
- if(remove_)
- DeleteFile(file_name_.c_str());
- throw cppcms_error("Failed to load library "+file_name);
- }
- }
-
- ~shared_object()
- {
- FreeLibrary(handler_);
- if(remove_) {
- DeleteFile(file_name_.c_str());
- }
- }
-
- FARPROC symbol(std::string const &name) const
- {
- return GetProcAddress(handler_,name.c_str());
- }
- private:
- HMODULE handler_;
- std::string file_name_;
- bool remove_;
- };
- #else
- class shared_object : public util::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);
- if(!handler_) {
- throw cppcms_error("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
-
- std::string shared_object::name(std::string file,std::string path)
- {
- #ifdef CPPCMS_LIBRARY_PREFIX
- return path+"/"+CPPCMS_LIBRARY_PREFIX+file+CPPCMS_LIBRARY_SUFFIX;
- #else
- return path+"/"+file+CPPCMS_LIBRARY_SUFFIX;
- #endif
- }
-
-
- } // impl
-
- #if !defined(HAVE_STAT) && defined(HAVE__STAT)
- #define stat _stat
- #endif
-
- struct views_pool::skin {
- public:
- skin()
- {
- }
-
- skin(std::string const &name,std::vector<std::string> const &search_path,bool reloadable) :
- reloadable_(true),
- skin_name_(name)
- {
- for(unsigned i=0;i<search_path.size();i++) {
- struct stat st;
- std::string file_name = impl::shared_object::name(name,search_path[i]);
- if(::stat(file_name.c_str(),&st) < 0)
- continue;
- file_name_ = file_name;
- time_stamp_ = st.st_mtime;
-
- shared_object_.reset(new impl::shared_object(file_name,reloadable));
- return;
- }
- throw cppcms_error("Can't load skin " + name);
- }
-
- void copy_mapping(skin const &other)
- {
- mapping_ = other.mapping_;
- }
-
-
- skin(std::string skin_name,std::map<std::string,view_factory_type> const &views) :
- reloadable_(false),
- skin_name_(skin_name)
- {
- mapping_=views;
- }
-
- bool is_updated() const
- {
- if(!reloadable_)
- return true;
- struct stat st;
- if(::stat(file_name_.c_str(),&st) < 0)
- return true;
- return time_stamp_ >= st.st_mtime;
- }
-
- void render(std::string template_name,std::ostream &output,base_content &content) const
- {
- mapping_type::const_iterator p = mapping_.find(template_name);
- if(p==mapping_.end())
- throw cppcms_error("Can't find template "+template_name + " in skin "+skin_name_);
- std::auto_ptr<base_view> a_view = p->second(output,&content);
- a_view->render();
- }
-
- private:
- bool reloadable_;
- std::string skin_name_;
- std::string file_name_;
- time_t time_stamp_;
- mapping_type mapping_;
- boost::shared_ptr<impl::shared_object> shared_object_;
-
- };
-
- struct views_pool::data {
- bool dynamic_reload;
- typedef std::map<std::string,skin> skins_type;
- skins_type skins;
- boost::shared_mutex lock_;
- std::string default_skin;
- std::vector<std::string> search_path;
- };
-
- views_pool::views_pool() :
- d(new data())
- {
- }
-
- std::string views_pool::default_skin() const
- {
- return d->default_skin;
- }
-
- views_pool::views_pool(json::value const &settings) :
- d(new data())
- {
- d->skins=static_instance().d->skins;
- std::vector<std::string> paths=settings.get("views.paths",std::vector<std::string>());
- d->search_path=paths;
- std::vector<std::string> skins=settings.get("views.skins",std::vector<std::string>());
- d->dynamic_reload= settings.get("views.auto_reload",false);
- d->default_skin = settings.get<std::string>("views.default_skin","");
- if(d->default_skin.empty() && d->skins.size()==1)
- d->default_skin=d->skins.begin()->first;
- if(paths.empty() || skins.empty()) {
- return;
- }
- for(unsigned i=0;i<skins.size();i++) {
- std::string name=skins[i];
- if(d->skins.find(name)!=d->skins.end())
- throw cppcms_error("Two skins with same name provided:" + name);
- d->skins[name]=skin(name,paths,d->dynamic_reload);
- }
- if(d->default_skin.empty())
- d->default_skin=skins[0];
- }
-
- void views_pool::render(std::string skin_name,std::string template_name,std::ostream &out,base_content &content)
- {
- if(d->dynamic_reload) {
- for(;;){
- { // Check if update
- boost::shared_lock<boost::shared_mutex> lock(d->lock_);
- data::skins_type::const_iterator p=d->skins.find(skin_name);
- if(p==d->skins.end())
- throw cppcms_error("There is no such skin:" + skin_name);
- if(p->second.is_updated()) {
- p->second.render(template_name,out,content);
- return;
- }
- }
- { // Reload
- boost::unique_lock<boost::shared_mutex> lock(d->lock_);
- data::skins_type::iterator p=d->skins.find(skin_name);
- if(p==d->skins.end())
- throw cppcms_error("There is no such skin:" + skin_name);
- if(!p->second.is_updated()) {
- d->skins.erase(p);
- skin new_skin = skin(skin_name,d->search_path,true);
- new_skin.copy_mapping(d->skins[skin_name]);
- d->skins[skin_name] = new_skin;
- }
- }
- }
- }
- else { // No need to reload
- data::skins_type::const_iterator p=d->skins.find(skin_name);
- if(p==d->skins.end())
- throw cppcms_error("There is no such skin:" + skin_name);
- p->second.render(template_name,out,content);
- }
- }
-
- views_pool::~views_pool()
- {
- }
-
- void views_pool::add_view(std::string name,mapping_type const &mapping)
- {
- data::skins_type::iterator p=d->skins.find(name);
- if(p!=d->skins.end())
- throw cppcms_error("Skin " + name + "can't be loaded twice");
- d->skins[name]=skin(name,mapping);
- }
-
- void views_pool::remove_view(std::string name)
- {
- d->skins.erase(name);
- }
-
-
- namespace { // Make sure that static views pool is loaded
- struct loader {
- loader()
- {
- views_pool::static_instance();
- }
- };
- } // anon
-
-
- views_pool &views_pool::static_instance()
- {
- static views_pool pool;
- return pool;
- }
-
-
- } // cppcms
|