00001 /********************************************************************* 00002 * Software License Agreement (BSD License) 00003 * 00004 * Copyright (c) 2013, Institute for Artificial Intelligence, 00005 * Universität Bremen. 00006 * All rights reserved. 00007 * 00008 * Redistribution and use in source and binary forms, with or without 00009 * modification, are permitted provided that the following conditions 00010 * are met: 00011 * 00012 * * Redistributions of source code must retain the above copyright 00013 * notice, this list of conditions and the following disclaimer. 00014 * * Redistributions in binary form must reproduce the above 00015 * copyright notice, this list of conditions and the following 00016 * disclaimer in the documentation and/or other materials provided 00017 * with the distribution. 00018 * * Neither the name of the Institute for Artificial Intelligence, 00019 * Universität Bremen, nor the names of its contributors may be 00020 * used to endorse or promote products derived from this software 00021 * without specific prior written permission. 00022 * 00023 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00024 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00025 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00026 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00027 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00028 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00029 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00030 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00031 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00032 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00033 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00034 * POSSIBILITY OF SUCH DAMAGE. 00035 *********************************************************************/ 00036 00040 #include <PluginSystem.h> 00041 00042 00043 namespace beliefstate { 00044 PluginSystem::PluginSystem(int argc, char** argv) { 00045 m_argc = argc; 00046 m_argv = argv; 00047 00048 this->setMessagePrefixLabel("plugins"); 00049 } 00050 00051 PluginSystem::~PluginSystem() { 00052 m_lstLoadedPlugins.reverse(); 00053 00054 // Trigger kill signals 00055 for(PluginInstance* icCurrent : m_lstLoadedPlugins) { 00056 icCurrent->setRunning(false); 00057 } 00058 00059 // Join all threads 00060 for(PluginInstance* icCurrent : m_lstLoadedPlugins) { 00061 icCurrent->waitForJoin(); 00062 } 00063 00064 // Delete all structures 00065 for(PluginInstance* icCurrent : m_lstLoadedPlugins) { 00066 icCurrent->unload(); 00067 delete icCurrent; 00068 } 00069 00070 m_lstLoadedPlugins.clear(); 00071 } 00072 00073 string PluginSystem::pluginNameFromPath(string strPath) { 00074 // Remove path 00075 const size_t last_slash_idx = strPath.find_last_of("\\/"); 00076 if(std::string::npos != last_slash_idx) { 00077 strPath.erase(0, last_slash_idx + 1); 00078 } 00079 00080 // Remove extension 00081 const size_t period_idx = strPath.rfind('.'); 00082 if(std::string::npos != period_idx) { 00083 strPath.erase(period_idx); 00084 } 00085 00086 // Remove plugin prefix 00087 std::string strPrefix = "libbs_plugin_"; 00088 00089 if(strPath.substr(0, strPrefix.size()) == strPrefix) { 00090 strPath = strPath.substr(strPrefix.size()); 00091 } 00092 00093 return strPath; 00094 } 00095 00096 bool PluginSystem::pluginLoaded(std::string strPluginName) { 00097 for(PluginInstance* icCurrent : m_lstLoadedPlugins) { 00098 if(icCurrent->name() == strPluginName) { 00099 return true; 00100 } 00101 } 00102 00103 return false; 00104 } 00105 00106 void PluginSystem::setLoadDevelopmentPlugins(bool bLoadDevelopmentPlugins) { 00107 m_bLoadDevelopmentPlugins = bLoadDevelopmentPlugins; 00108 } 00109 00110 bool PluginSystem::loadDevelopmentPlugins() { 00111 return m_bLoadDevelopmentPlugins; 00112 } 00113 00114 bool PluginSystem::pluginFailedToLoadBefore(std::string strName) { 00115 for(std::string strPluginName : m_lstLoadFailedPlugins) { 00116 if(strPluginName == strName) { 00117 return true; 00118 } 00119 } 00120 00121 return false; 00122 } 00123 00124 Result PluginSystem::loadPluginLibrary(std::string strFilepath, bool bIsNameOnly) { 00125 PluginInstance* icLoad = NULL; 00126 std::string strPrefix = "libbs_plugin_"; 00127 std::string strSuffix = ".so"; 00128 00129 if(bIsNameOnly) { 00130 strFilepath = strPrefix + strFilepath + strSuffix; 00131 } 00132 00133 std::list<std::string> lstSearchPaths = m_lstPluginSearchPaths; 00134 lstSearchPaths.push_front("./"); // Add local path as search path 00135 00136 Result resLoad = defaultResult(); 00137 resLoad.bSuccess = false; 00138 00139 if(this->pluginLoaded(this->pluginNameFromPath(strFilepath))) { 00140 this->info("Plugin '" + this->pluginNameFromPath(strFilepath) + "' already loaded."); 00141 resLoad.bSuccess = true; 00142 } else { 00143 if(!this->pluginFailedToLoadBefore(strFilepath)) { 00144 for(string strSP : lstSearchPaths) { 00145 string strSearchFilepath = strSP + (strSP[strSP.size() - 1] != '/' && strFilepath[0] != '/' && strSP.size() > 0 ? "/" : "") + strFilepath; 00146 00147 icLoad = new PluginInstance(); 00148 resLoad = icLoad->loadPluginLibrary(strSearchFilepath); 00149 resLoad.piPlugin = icLoad; 00150 00151 if(resLoad.bSuccess) { 00152 // Check if this is a development plugin and if we're supposed to load it. 00153 if((icLoad->developmentPlugin() && m_bLoadDevelopmentPlugins) || !icLoad->developmentPlugin()) { 00154 if(icLoad->developmentPlugin()) { 00155 this->info("This is a development plugin: '" + strFilepath + "'"); 00156 } 00157 00158 // Check and meet dependencies 00159 std::list<std::string> lstDeps = icLoad->dependencies(); 00160 for(string strDep : lstDeps) { 00161 if(this->pluginLoaded(strDep) == false) { 00162 Result resLoadDep = this->loadPluginLibrary(strPrefix + strDep + strSuffix); 00163 00164 if(resLoadDep.bSuccess == false) { 00165 this->fail("Unable to meet dependency of '" + strSearchFilepath + "': '" + strDep + "'"); 00166 00167 resLoad.bSuccess = false; 00168 resLoad.riResultIdentifier = RI_PLUGIN_DEPENDENCY_NOT_MET; 00169 resLoad.strErrorMessage = strDep; 00170 00171 break; 00172 } 00173 } 00174 } 00175 } else { 00176 this->info("Not loading development plugin: '" + strFilepath + "'"); 00177 00178 resLoad.bSuccess = false; 00179 resLoad.riResultIdentifier = RI_PLUGIN_DEVELOPMENT_NOT_LOADING; 00180 } 00181 00182 if(resLoad.bSuccess) { 00183 // Initialize the plugin 00184 Result rsResult = icLoad->init(m_argc, m_argv); 00185 00186 if(rsResult.bSuccess) { 00187 m_lstLoadedPlugins.push_back(icLoad); 00188 } else { 00189 resLoad.bSuccess = false; 00190 resLoad.riResultIdentifier = RI_PLUGIN_LOADING_FAILED; 00191 } 00192 00193 break; 00194 } 00195 } else { 00196 resLoad.bSuccess = false; 00197 resLoad.riResultIdentifier = RI_PLUGIN_LOADING_FAILED; 00198 } 00199 00200 if(resLoad.bSuccess == false) { 00201 icLoad->unload(); 00202 m_lstLoadFailedPlugins.push_back(strFilepath); 00203 00204 delete icLoad; 00205 } else { 00206 break; 00207 } 00208 } 00209 } else { 00210 this->warn("This plugin failed to load before. Skipping it."); 00211 } 00212 } 00213 00214 if(resLoad.bSuccess == false) { 00215 this->fail("Failed to load plugin '" + strFilepath + "'"); 00216 } else { 00217 resLoad.piPlugin = icLoad; 00218 } 00219 00220 return resLoad; 00221 } 00222 00223 void PluginSystem::queueUnloadPluginInstance(PluginInstance* icUnload) { 00224 m_lstUnloadPlugins.push_back(icUnload); 00225 } 00226 00227 int PluginSystem::spreadEvent(Event evEvent) { 00228 int nReceivers = 0; 00229 00230 for(PluginInstance* piPlugin : m_lstLoadedPlugins) { 00231 if(piPlugin->subscribedToEvent(evEvent.strEventName)) { 00232 piPlugin->consumeEvent(evEvent); 00233 nReceivers++; 00234 } 00235 } 00236 00237 return nReceivers; 00238 } 00239 00240 int PluginSystem::spreadServiceEvent(ServiceEvent seServiceEvent) { 00241 list<Event> lstResultEvents; 00242 int nReceivers = 0; 00243 00244 for(PluginInstance* piPlugin : m_lstLoadedPlugins) { 00245 if(piPlugin->offersService(seServiceEvent.strServiceName) || 00246 seServiceEvent.siServiceIdentifier == SI_RESPONSE) { 00247 Event evResult = piPlugin->consumeServiceEvent(seServiceEvent); 00248 nReceivers++; 00249 00250 if(seServiceEvent.smResultModifier != SM_IGNORE_RESULTS) { 00251 evResult.nOriginID = piPlugin->pluginID(); 00252 lstResultEvents.push_back(evResult); 00253 00254 if(seServiceEvent.smResultModifier == SM_FIRST_RESULT) { 00255 break; 00256 } else { 00257 // Aggregate results 00258 } 00259 } 00260 } 00261 } 00262 00263 PluginInstance* piRequester = this->pluginInstanceByID(seServiceEvent.nRequesterID); 00264 if(piRequester && seServiceEvent.smResultModifier != SM_IGNORE_RESULTS) { 00265 ServiceEvent seResponses = seServiceEvent; 00266 seResponses.lstResultEvents = lstResultEvents; 00267 seResponses.siServiceIdentifier = SI_RESPONSE; 00268 00269 piRequester->consumeServiceEvent(seResponses); 00270 } 00271 00272 return nReceivers; 00273 } 00274 00275 Result PluginSystem::cycle() { 00276 Result resCycle = defaultResult(); 00277 00278 for(PluginInstance* icCurrent : m_lstLoadedPlugins) { 00279 Result resCurrent = icCurrent->cycle(); 00280 00281 for(StatusMessage smCurrent : resCurrent.lstStatusMessages) { 00282 resCycle.lstStatusMessages.push_back(smCurrent); 00283 } 00284 00285 if(resCurrent.bSuccess == false) { 00286 // NOTE(winkler): This might also be a good place to implement 00287 // a recovery mechanism in case a plugin actually fails during 00288 // its cycle. Reload plugins and notify all "depending" 00289 // plugins (in order of dependency) to "recover". 00290 this->queueUnloadPluginInstance(icCurrent); 00291 } else { 00292 for(Event evtCurrent : resCurrent.lstEvents) { 00293 resCycle.lstEvents.push_back(evtCurrent); 00294 } 00295 00296 for(ServiceEvent seCurrent : resCurrent.lstServiceEvents) { 00297 resCycle.lstServiceEvents.push_back(seCurrent); 00298 } 00299 } 00300 } 00301 00302 for(PluginInstance* icCurrent : m_lstUnloadPlugins) { 00303 icCurrent->unload(); 00304 m_lstLoadedPlugins.remove(icCurrent); 00305 delete icCurrent; 00306 } 00307 00308 return resCycle; 00309 } 00310 00311 void PluginSystem::addPluginSearchPath(std::string strPath) { 00312 // Make sure it is in there only once. 00313 m_lstPluginSearchPaths.remove(strPath); 00314 m_lstPluginSearchPaths.push_back(strPath); 00315 } 00316 00317 PluginInstance* PluginSystem::pluginInstanceByID(int nID) { 00318 for(PluginInstance* icCurrent : m_lstLoadedPlugins) { 00319 if(icCurrent->pluginID() == nID) { 00320 return icCurrent; 00321 } 00322 } 00323 00324 return NULL; 00325 } 00326 }