TKN Logo SelfSoft Logo

Total Knowledge

SelfSoft, Inc.

CPPSERV

[CPPSERV] [Documentation] [Download] [Contact] [Bug tracker] [News] [RSS Feed] [gitweb]

App.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (C) 2004-2006 by Ilya A. Volynets-Evenbakh                  *
00003  *   ilya@total-knowledge.com                                              *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU General Public License as published by  *
00007  *   the Free Software Foundation; either version 2 of the License, or     *
00008  *   (at your option) any later version.                                   *
00009  *                                                                         *
00010  *   This program is distributed in the hope that it will be useful,       *
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00013  *   GNU General Public License for more details.                          *
00014  *                                                                         *
00015  *   You should have received a copy of the GNU General Public License     *
00016  *   along with this program; if not, write to the                         *
00017  *   Free Software Foundation, Inc.,                                       *
00018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00019  ***************************************************************************/
00020 
00021 #include <App.h>
00022 #include <requesthandler.h>
00023 #include <dlfcn.h>
00024 #include <fcntl.h>
00025 #include <sptk3/CBase64.h>
00026 #include <servlet/IllegalArgumentException.h>
00027 #include <serverconfig.h>
00028 
00029 namespace container
00030 {
00038 ServletConfigImpl* App::addServlet(const std::string& path, const std::string& name, const std::string& dso, bool hidden)
00039 {
00040         void* dsoHandle=dlopen(dso.c_str(),RTLD_LAZY);
00041         if(!dsoHandle) {
00042                 std::cerr<<dlerror()<<std::endl;
00043                 return 0;
00044         }
00045         servletcreatefunc_t createFunc=(servletcreatefunc_t)dlsym(dsoHandle,(name + "_createServlet").c_str());
00046         if(!createFunc) {
00047                 std::cerr<<"Could not locate servlet "<<name<<" in the library "<<dso<<": "<<dlerror()<<std::endl;
00048                 return 0;
00049         }
00050         ServletConfigImpl* config=new ServletConfigImpl(this, name);
00051         ServletDesc* desc=new ServletDesc(createFunc(), config, dsoHandle, path);
00052         addServlet(path, desc);
00053         if(!hidden)
00054                 RequestHandler::addServlet(path,getServletContainer(path));
00055         return config;
00056 }
00057 
00058 bool App::addServlet(const std::string& path,ServletDesc* d)
00059 {
00060         maptop[path] = d;
00061         return true;
00062 }
00063 
00064 App::ServletDesc* App::getDesc(const std::string& path)
00065 {
00066         return maptop.getServletDesc(path);
00067 }
00068 
00069 void App::delServlet(const std::string& path)
00070 {
00071         maptop.removeDesc(path);
00072 }
00073 
00074 ServletContainer* App::getServletContainer(const std::string& path)
00075 {
00076         ServletDesc* desc = getDesc(path);
00077         if(!desc)
00078                 return 0;
00079         return &(desc->cont);
00080 }
00081 
00082 void* App::getDso(const std::string& path)
00083 {
00084         ServletDesc* desc = getDesc(path);
00085         if(!desc)
00086                 return 0;
00087         return desc->h;
00088 }
00089 
00090 void App::splitServPath(const std::string& path, std::string& dir, std::string& name)
00091 {
00092         std::string::size_type slash = path.rfind('/');
00093         if(slash == std::string::npos) {
00094                 dir = "/";
00095                 name = path;
00096         } else {
00097                 dir.assign(path.substr(0, slash));
00098                 name.assign(path.substr(slash + 1));
00099         }
00100 }
00101 
00102 void App::initServlet(const std::string& path)
00103 {
00104         ServletContainer* cont=getServletContainer(path);
00105         cont->init();
00106 }
00107 
00108 void App::destroyServlet(const std::string& path)
00109 {
00110         ServletContainer* cont=getServletContainer(path);
00111         cont->destroy();
00112 }
00113 
00120 void App::unloadServlet(const std::string& path)
00121 {
00122         ServletContainer* cont=getServletContainer(path);
00123         servlet::ServletConfig* conf=cont->getConfig();
00124         ServletDesc* d=getDesc(path);
00125         void* dsoHandle=d->h;
00126         delServlet(path);
00127         delete d; // this will delete ServletContainer, which will in turn delete servlet object
00128         delete conf;
00129         dlclose(dsoHandle);
00130 }
00131 
00132 class RHServletRemover
00133 {
00134 public:
00135         void operator()(const std::string& path)
00136         {
00137                 RequestHandler::removeServlet(path);
00138         }
00139 };
00140 
00141 template<class T>
00142  class SMFunctor
00143 {
00144 private:
00145         T* inst;
00146         typedef void (T::*F)(const std::string&);
00147         F f;
00148 public:
00149         SMFunctor(T* inst, F f): inst(inst), f(f){}
00150         void operator()(const std::string& path)
00151         {
00152                 (inst->*f)(path);
00153         }
00154 };
00155 
00156 App::~App()
00157 {
00158         cleaner->stop();
00159         delete cleaner;
00160         killAllSessions();
00161         std::string empty;
00162         destroyServlets(); // Calls destroy() on all servlets, before unloading them
00163         RHServletRemover rh;
00164         SMFunctor<App> unl(this, &App::unloadServlet);
00165         // This will block till all requests being processed by this servlet
00166         // are done. Alternatively we may want to kill all processes that are
00167         // in this servlet. Maybe there should be some timeout
00168         maptop.forEachServletPath(rh, empty);
00169         maptop.forEachServletPath(unl, empty);
00170 }
00171 
00172 void App::initServlets()
00173 {
00174         std::string empty;
00175         SMFunctor<App> init(this, &App::initServlet);
00176         maptop.forEachServletPath(init, empty);
00177 }
00178 
00179 void App::destroyServlets()
00180 {
00181         std::string empty;
00182         SMFunctor<App> destroy(this, &App::destroyServlet);
00183         maptop.forEachServletPath(destroy, empty);
00184 }
00185 
00186 
00187 App::App(ServerConfig& cfg, time_t sessionTimeout, const std::string& name, const std::string& fileSaveDir, const std::string& uribase)
00188         : servlet::ServletContext()
00189         , cfg(cfg)
00190         , sessionCookieName(makeSName(name))
00191         , sessionTimeout(sessionTimeout)
00192         , fileSaveTemplate(fileSaveDir+'/'+"cppserv.tmpXXXXXX")
00193         , name(name)
00194         , m_uribase(uribase)
00195 {
00196         cleaner=new SessionCleaner(this,sessionTimeout/2+1); // +1, so that it doesn't become 0...
00197         cleaner->run();
00198 }
00199 
00208 container::HttpSessionImpl* App::getSession(const std::string& sid, bool create)
00209 {
00210         sessionLock.lock();
00211         if(sid.empty()) {
00212                 container::HttpSessionImpl *s = 0;
00213                 if(create) s = newSession();
00214                 sessionLock.unlock();
00215                 return s;
00216         }
00217         sessionlist_t::iterator s=sessions.find(sid);
00218         //FIXME: should we perhaps return an invalid session in this case?
00219         if(s==sessions.end()||!s->second->validP()){
00220                 HttpSessionImpl* s = 0;
00221                 if(create) s = newSession();
00222                 //s->invalidate();
00223                 sessionLock.unlock();
00224                 return s;
00225         }
00226         s->second->notNew();
00227         sessionLock.unlock();
00228         return s->second;
00229 }
00230 
00231 
00236 container::HttpSessionImpl* App::newSession()
00237 {
00238         std::string sid=container::util::getRandomString(10);
00239         HttpSessionImpl* s=new HttpSessionImpl(*this,sid,sessionTimeout);
00240         sessions[sid]=s;
00241         return s;
00242 }
00243 
00244 
00250 void App::killSession(const std::string& sid)
00251 {
00252         sessionlist_t::iterator s=sessions.find(sid);
00253         if(s==sessions.end()){
00254                 return;
00255         }
00256         delete s->second;
00257         sessions.erase(sid);
00258 }
00259 
00260 
00264 void App::cleanSessions()
00265 {
00266         sessionLock.lock();
00267         sessionlist_t::iterator cur=sessions.begin();
00268         while(cur!=sessions.end()){
00269                 if(!cur->second->validP()){
00270                         killSession(cur->first);
00271                         // killSession modifies the map, so we have
00272                         // to restart our search from scratch.
00273                         // Another option would be to leave this alone,
00274                         // In hopes that we'll get to every expired session
00275                         // sooner or later - Harder on RAM, easier on CPU
00276                         cur=sessions.begin();
00277                 } else {
00278                         cur++;
00279                 }
00280         }
00281         sessionLock.unlock();
00282 }
00287 App::SessionCleaner::SessionCleaner(App* ctx,int freq)
00288         : sptk::CThread("",true)
00289         , running(true)
00290         , freq(freq)
00291         , ctx(ctx)
00292 {
00293         if(pipe(trigger)==-1) {
00294                 std::cerr<<"Error setting up session cleaner trigger: "<<strerror(errno)<<std::endl;
00295                 ::exit(1);
00296         }
00297         if(fcntl(trigger[0],F_SETFL,O_NONBLOCK)<0) {
00298                 std::cerr<<"Error setting trigger reader to non-blocking mode: "
00299                         <<strerror(errno)<<std::endl;
00300                 exit(1);
00301         }
00302 }
00306 App::SessionCleaner::~SessionCleaner()
00307 {
00308         close(trigger[0]);
00309         close(trigger[1]);
00310 }
00311 void App::SessionCleaner::threadFunction()
00312 {
00313         while(running) {
00314                 ctx->cleanSessions();
00315                 struct timeval timeout={freq,0};
00316                 fd_set rfd;
00317                 FD_ZERO(&rfd);
00318                 FD_SET(trigger[0],&rfd);
00319                 int r=select(trigger[0]+1,&rfd,0,0,&timeout);
00320                 if(r){
00321                         char buf[4];
00322                         while(::read(trigger[0],buf,sizeof(buf))>0);
00323                 }
00324         }
00325 }
00332 void App::SessionCleaner::stop()
00333 {
00334         running=false;
00335         this->wakeUp();
00336         this->terminate();
00337 }
00338 
00344 bool App::isSessionValid(const std::string& sid)
00345 {
00346         sessionlist_t::iterator s=sessions.find(sid);
00347         return s!=sessions.end()&&s->second->validP();
00348 }
00349 
00350 
00354 void App::addInitParam(const std::string& name, const std::string& value)
00355 {
00356         m_params[name]=value;
00357 }
00358 
00363 std::string App::getServletContextName() const
00364 {
00365         return name;
00366 }
00367 
00368 void App::log(const std::string& msg) const
00369 {
00370         std::cerr<<msg<<std::endl;
00371 }
00372 
00373 void App::log(const std::string& message, const std::exception& e) const 
00374 {
00375         log(message);
00376         std::cerr<<e.what()<<std::endl;
00377 }
00378 
00379 
00384 void App::SessionCleaner::wakeUp()
00385 {
00386         char c='1';
00387         ::write(trigger[1],&c,1);
00388 }
00389 
00390 
00397 void App::killAllSessions()
00398 {
00399         sessionLock.lock();
00400         sessionlist_t::iterator cur=sessions.begin();
00401         while(cur!=sessions.end()){
00402                 delete cur->second;
00403                 cur++;
00404         }
00405         sessions.clear();
00406         sessionLock.unlock();
00407 }
00408 
00409 std::string App::makeSName(const std::string& name)
00410 {
00411         sptk::CBuffer in(name.c_str());
00412         sptk::CBuffer out(name.length()+5);
00413         sptk::CBase64::encode(out,in);
00414         std::string ret="CPPSERV";
00415         ret+=out.data();
00416         std::transform(ret.begin(),ret.end(),ret.begin(),container::util::TrEq());
00417         return ret;
00418 }
00419 
00420 
00421 servlet::ServletContext* App::getContext(const std::string& uripath)
00422 {
00423         std::string uribase = getUriBase();
00424         if(uripath.find(uribase) != 0)
00425                 return 0;
00426         std::string contextName = uripath.substr(uribase.length());
00427         return cfg.getApp(contextName);
00428 }
00429 int App::getMajorVersion()
00430 {
00431         return VERSION_MAJOR;
00432 }
00433 int App::getMinorVersion()
00434 {
00435         return VERSION_MINOR;
00436 }
00437 std::string App::getMimeType(const std::string& /*file*/)
00438 {
00439         //FIXME: should we do something here? Talk to web server?
00440         //FIXME: Call file?
00441         return "";
00442 }
00443 std::auto_ptr< std::set<std::string> > App::getResourcePaths(const std::string& path)
00444 {
00445         std::auto_ptr< std::set<std::string> > ret(new std::set<std::string>);
00446         // For now we can only return list of available Servlets
00447         // Which means our implementation is not really complete
00448         servletmap_t* sp = maptop.getPath(path, false);
00449         if(!sp)
00450                 return ret;
00451         std::insert_iterator<std::set<std::string> > ii(*ret, ret->begin());
00452         std::transform(sp->getSubpaths().begin(), sp->getSubpaths().end(), ii, __gnu_cxx::select1st<std::pair<std::string, ServletMap<ServletDesc>*> >());
00453         std::transform(sp->getServlets().begin(), sp->getServlets().end(), ii, __gnu_cxx::select1st<std::pair<std::string, ServletDesc*> >());
00454         return ret;
00455 }
00456 std::string App::getResource(const std::string& /*path*/)
00457 {
00458         //throw servlet::ServletException("Method unimplemented");
00459         return "";
00460 }
00461 std::istream& App::getResourceAsStream(const std::string& /*path*/)
00462 {
00463         throw servlet::ServletException("Method unimplemented");
00464 }
00465 servlet::RequestDispatcher* App::getRequestDispatcher(const std::string& path)
00466 {
00467         if(path[0]!='/')
00468                 throw servlet::IllegalArgumentException();
00469         std::string name(path.substr(1));
00470         return getServletContainer(name);
00471 }
00472 servlet::RequestDispatcher* App::getNamedDispatcher(const std::string& name)
00473 {
00474         return getServletContainer(name);
00475 }
00476 std::string App::getRealPath(const std::string& /*path*/)
00477 {
00478         return "";
00479 }
00480 std::string App::getServerInfo()
00481 {
00482         return "CPPSERV/" CPPSERV_VERSION;
00483 }
00484 std::string App::getInitParameter(const std::string& name)
00485 {
00486         param_t::const_iterator iRet = m_params.find(name);
00487         if(iRet == m_params.end())
00488             return "";
00489         else
00490             return iRet->second;
00491 }
00492 std::auto_ptr< std::vector<std::string> > App::getInitParameterNames()
00493 {
00494         return util::getMapKeyNames(m_params);
00495 }
00496 boost::shared_ptr<void> App::getAttribute(const std::string& name)
00497 {
00498         attr_t::const_iterator iRet = m_attrs.find(name);
00499         if(iRet == m_attrs.end())
00500             return boost::shared_ptr<void>();
00501         else
00502             return iRet->second;
00503 }
00504 std::auto_ptr< std::vector<std::string> > App::getAttributeNames()
00505 {
00506         return util::getMapKeyNames(m_attrs);
00507 }
00508 void App::setAttribute(const std::string& name, boost::shared_ptr<void> object)
00509 {
00510         m_attrs[name] = object;
00511 }
00512 void App::removeAttribute(const std::string& name)
00513 {
00514         attr_t::iterator it=m_attrs.find(name);
00515         if(it!=m_attrs.end())
00516             m_attrs.erase(it);
00517 }
00518 bool App::hasAttribute(const std::string& name) const
00519 {
00520         attr_t::const_iterator it=m_attrs.find(name);
00521         return it!=m_attrs.end();
00522 }
00523 
00524 }

SourceForge.net Logo

Authoright © Total Knowledge: 2001-2004