OpenWalnut
1.4.0
|
00001 //--------------------------------------------------------------------------- 00002 // 00003 // Project: OpenWalnut ( http://www.openwalnut.org ) 00004 // 00005 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS 00006 // For more information see http://www.openwalnut.org/copying 00007 // 00008 // This file is part of OpenWalnut. 00009 // 00010 // OpenWalnut is free software: you can redistribute it and/or modify 00011 // it under the terms of the GNU Lesser General Public License as published by 00012 // the Free Software Foundation, either version 3 of the License, or 00013 // (at your option) any later version. 00014 // 00015 // OpenWalnut is distributed in the hope that it will be useful, 00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 // GNU Lesser General Public License for more details. 00019 // 00020 // You should have received a copy of the GNU Lesser General Public License 00021 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>. 00022 // 00023 //--------------------------------------------------------------------------- 00024 00025 #include <algorithm> 00026 #include <set> 00027 #include <string> 00028 #include <sstream> 00029 00030 #include <boost/shared_ptr.hpp> 00031 #include <boost/uuid/uuid.hpp> 00032 #include <boost/uuid/uuid_generators.hpp> 00033 #include <boost/uuid/uuid_io.hpp> 00034 00035 #include "WModuleInputConnector.h" 00036 #include "WModuleOutputConnector.h" 00037 #include "WModuleInputData.h" 00038 #include "WModuleOutputData.h" 00039 #include "WModuleConnectorSignals.h" 00040 #include "WModuleContainer.h" 00041 #include "WModuleFactory.h" 00042 #include "WModuleMetaInformation.h" 00043 #include "exceptions/WModuleConnectorInitFailed.h" 00044 #include "exceptions/WModuleConnectorNotFound.h" 00045 #include "exceptions/WModuleUninitialized.h" 00046 #include "exceptions/WModuleRequirementNotMet.h" 00047 #include "../common/WException.h" 00048 #include "../common/exceptions/WNameNotUnique.h" 00049 #include "../common/exceptions/WSignalUnknown.h" 00050 #include "../common/exceptions/WSignalSubscriptionFailed.h" 00051 #include "../common/WLogger.h" 00052 #include "../common/WCondition.h" 00053 #include "../common/WConditionOneShot.h" 00054 #include "../common/WConditionSet.h" 00055 #include "../common/WPathHelper.h" 00056 #include "../common/WProgressCombiner.h" 00057 #include "../common/WPredicateHelper.h" 00058 00059 #include "WModule.h" 00060 00061 WModule::WModule(): 00062 WThreadedRunner(), 00063 WPrototyped(), 00064 m_initialized( new WCondition(), false ), 00065 m_isAssociated( new WCondition(), false ), 00066 m_isUsable( new WCondition(), false ), 00067 m_isReady( new WConditionOneShot(), false ), 00068 m_isReadyOrCrashed( new WConditionSet(), false ), 00069 m_isRunning( new WCondition(), false ), 00070 m_isLoadFinished( new WConditionOneShot(), false ), 00071 m_restoreMode( false ), 00072 m_readyProgress( boost::shared_ptr< WProgress >( new WProgress( "Initializing Module" ) ) ), 00073 m_moduleState(), 00074 m_localPath( WPathHelper::getSharePath() ) 00075 { 00076 // initialize members 00077 m_properties = boost::shared_ptr< WProperties >( new WProperties( "Properties", "Module's properties" ) ); 00078 m_infoProperties = boost::shared_ptr< WProperties >( new WProperties( "Informational Properties", "Module's information properties" ) ); 00079 m_infoProperties->setPurpose( PV_PURPOSE_INFORMATION ); 00080 00081 m_runtimeName = m_properties->addProperty( "Name", "The name of the module defined by the user. This is, by default, the module name but " 00082 "can be changed by the user to provide some kind of simple identification upon many modules.", 00083 std::string( "" ), false ); 00084 00085 m_active = m_properties->addProperty( "active", "Determines whether the module should be activated.", true, true ); 00086 m_active->getCondition()->subscribeSignal( boost::bind( &WModule::activate, this ) ); 00087 00088 // the isReadyOrCrashed condition set needs to be set up here 00089 WConditionSet* cs = static_cast< WConditionSet* >( m_isReadyOrCrashed.getCondition().get() ); // NOLINT 00090 cs->setResetable( true, false ); 00091 cs->add( m_isReady.getCondition() ); 00092 cs->add( m_isCrashed.getCondition() ); 00093 00094 m_container = boost::shared_ptr< WModuleContainer >(); 00095 m_progress = boost::shared_ptr< WProgressCombiner >( new WProgressCombiner() ); 00096 00097 // add a progress indicator which finishes on "ready()" 00098 m_progress->addSubProgress( m_readyProgress ); 00099 00100 // our internal state consist out of two conditions: data changed and the exit flag from WThreadedRunner. 00101 m_moduleState.add( m_shutdownFlag.getCondition() ); 00102 } 00103 00104 WModule::~WModule() 00105 { 00106 // cleanup 00107 } 00108 00109 void WModule::addConnector( boost::shared_ptr< WModuleInputConnector > con ) 00110 { 00111 size_t c = std::count_if( m_inputConnectors.begin(), m_inputConnectors.end(), 00112 WPredicateHelper::Name< boost::shared_ptr< WModuleInputConnector > >( con->getName() ) 00113 ); 00114 // well ... we want it to be unique in both: 00115 c += std::count_if( m_outputConnectors.begin(), m_outputConnectors.end(), 00116 WPredicateHelper::Name< boost::shared_ptr< WModuleOutputConnector > >( con->getName() ) 00117 ); 00118 00119 // if there already is one ... exception 00120 if( c ) 00121 { 00122 throw WNameNotUnique( std::string( "Could not add the connector " + con->getCanonicalName() + " since names must be unique." ) ); 00123 } 00124 00125 m_inputConnectors.push_back( con ); 00126 } 00127 00128 void WModule::addConnector( boost::shared_ptr< WModuleOutputConnector > con ) 00129 { 00130 size_t c = std::count_if( m_inputConnectors.begin(), m_inputConnectors.end(), 00131 WPredicateHelper::Name< boost::shared_ptr< WModuleInputConnector > >( con->getName() ) 00132 ); 00133 // well ... we want it to be unique in both: 00134 c += std::count_if( m_outputConnectors.begin(), m_outputConnectors.end(), 00135 WPredicateHelper::Name< boost::shared_ptr< WModuleOutputConnector > >( con->getName() ) 00136 ); 00137 00138 // if there already is one ... exception 00139 if( c ) 00140 { 00141 throw WNameNotUnique( std::string( "Could not add the connector " + con->getCanonicalName() + " since names must be unique." ) ); 00142 } 00143 00144 m_outputConnectors.push_back( con ); 00145 } 00146 00147 void WModule::disconnect() 00148 { 00149 // remove connections and their signals 00150 for( InputConnectorList::iterator listIter = m_inputConnectors.begin(); 00151 listIter != m_inputConnectors.end(); ++listIter ) 00152 { 00153 ( *listIter )->disconnectAll(); 00154 } 00155 for( OutputConnectorList::iterator listIter = m_outputConnectors.begin(); 00156 listIter != m_outputConnectors.end(); ++listIter ) 00157 { 00158 ( *listIter )->disconnectAll(); 00159 } 00160 } 00161 00162 WCombinerTypes::WDisconnectList WModule::getPossibleDisconnections() 00163 { 00164 WCombinerTypes::WDisconnectList discons; 00165 00166 // iterate inputs 00167 for( InputConnectorList::iterator listIter = m_inputConnectors.begin(); listIter != m_inputConnectors.end(); ++listIter ) 00168 { 00169 // get all connections of the current connector: 00170 WCombinerTypes::WDisconnectGroup g = WCombinerTypes::WDisconnectGroup( ( *listIter )->getName(), 00171 ( *listIter )->getPossibleDisconnections() ); 00172 00173 if( g.second.size() ) 00174 { 00175 discons.push_back( g ); 00176 } 00177 } 00178 00179 // iterate outputs 00180 for( OutputConnectorList::iterator listIter = m_outputConnectors.begin(); listIter != m_outputConnectors.end(); ++listIter ) 00181 { 00182 // get all connections of the current connector: 00183 WCombinerTypes::WDisconnectGroup g = WCombinerTypes::WDisconnectGroup( ( *listIter )->getName(), 00184 ( *listIter )->getPossibleDisconnections() ); 00185 00186 if( g.second.size() ) 00187 { 00188 discons.push_back( g ); 00189 } 00190 } 00191 00192 return discons; 00193 } 00194 00195 void WModule::removeConnectors() 00196 { 00197 m_initialized( false ); 00198 m_isUsable( m_initialized() && m_isAssociated() ); 00199 00200 // remove connections and their signals, this is flat removal. The module container can do deep removal 00201 disconnect(); 00202 00203 // clean up list 00204 // this should delete the connector since nobody else *should* have another shared_ptr to them 00205 m_inputConnectors.clear(); 00206 m_outputConnectors.clear(); 00207 } 00208 00209 void WModule::connectors() 00210 { 00211 } 00212 00213 void WModule::properties() 00214 { 00215 } 00216 00217 void WModule::requirements() 00218 { 00219 } 00220 00221 void WModule::activate() 00222 { 00223 } 00224 00225 std::string WModule::deprecated() const 00226 { 00227 return ""; 00228 } 00229 00230 WModuleMetaInformation::ConstSPtr WModule::getMetaInformation() const 00231 { 00232 return m_meta; 00233 } 00234 00235 void WModule::initialize() 00236 { 00237 // doing it twice is not allowed 00238 if( isInitialized()() ) 00239 { 00240 throw WModuleConnectorInitFailed( std::string( "Could not initialize connectors for Module " ) + getName() + 00241 std::string( ". Reason: already initialized." ) ); 00242 } 00243 00244 // set the module name as default runtime name 00245 m_runtimeName->set( getName() ); 00246 00247 // initialize module meta information 00248 m_meta = WModuleMetaInformation::SPtr( new WModuleMetaInformation( shared_from_this() ) ); 00249 00250 // initialize connectors and properties 00251 requirements(); 00252 connectors(); 00253 properties(); 00254 00255 // now, the module is initialized but not necessarily usable (if not associated with a container) 00256 m_initialized( true ); 00257 m_isUsable( m_initialized() && m_isAssociated() ); 00258 00259 // also set thread name 00260 setThreadName( getName() ); 00261 } 00262 00263 void WModule::cleanup() 00264 { 00265 // currently just removes connectors 00266 removeConnectors(); 00267 } 00268 00269 boost::shared_ptr< WModuleContainer > WModule::getAssociatedContainer() const 00270 { 00271 return m_container; 00272 } 00273 00274 void WModule::setAssociatedContainer( boost::shared_ptr< WModuleContainer > container ) 00275 { 00276 m_container = container; 00277 00278 // true if the pointer is set 00279 m_isAssociated( m_container != boost::shared_ptr< WModuleContainer >() ); 00280 m_isUsable( m_initialized() && m_isAssociated() ); 00281 } 00282 00283 MODULE_TYPE WModule::getType() const 00284 { 00285 return MODULE_ARBITRARY; 00286 } 00287 00288 const WModule::InputConnectorList& WModule::getInputConnectors() const 00289 { 00290 return m_inputConnectors; 00291 } 00292 00293 const WModule::OutputConnectorList& WModule::getOutputConnectors() const 00294 { 00295 return m_outputConnectors; 00296 } 00297 00298 boost::shared_ptr< WModuleInputConnector > WModule::findInputConnector( std::string name ) 00299 { 00300 // simply search 00301 for( InputConnectorList::const_iterator listIter = m_inputConnectors.begin(); 00302 listIter != m_inputConnectors.end(); ++listIter ) 00303 { 00304 // try the canonical name 00305 if( ( name == ( *listIter )->getCanonicalName() ) || ( name == ( *listIter )->getName() ) ) 00306 { 00307 return ( *listIter ); 00308 } 00309 } 00310 00311 return boost::shared_ptr< WModuleInputConnector >(); 00312 } 00313 00314 boost::shared_ptr< WModuleInputConnector > WModule::getInputConnector( std::string name ) 00315 { 00316 boost::shared_ptr< WModuleInputConnector > p = findInputConnector( name ); 00317 00318 if( !p ) 00319 { 00320 throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name + 00321 std::string( "\" does not exist in the module \"" ) + getName() + std::string( "\"." ) ); 00322 } 00323 00324 return p; 00325 } 00326 00327 boost::shared_ptr< WModuleOutputConnector > WModule::findOutputConnector( std::string name ) 00328 { 00329 // simply search 00330 for( OutputConnectorList::const_iterator listIter = m_outputConnectors.begin(); 00331 listIter != m_outputConnectors.end(); ++listIter ) 00332 { 00333 // try the canonical name 00334 if( ( name == ( *listIter )->getCanonicalName() ) || ( name == ( *listIter )->getName() ) ) 00335 { 00336 return ( *listIter ); 00337 } 00338 } 00339 00340 return boost::shared_ptr< WModuleOutputConnector >(); 00341 } 00342 00343 boost::shared_ptr< WModuleOutputConnector > WModule::getOutputConnector( std::string name ) 00344 { 00345 boost::shared_ptr< WModuleOutputConnector > p = findOutputConnector( name ); 00346 00347 if( !p ) 00348 { 00349 throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name + 00350 std::string( "\" does not exist in the module \"" ) + getName() + 00351 std::string( "\"." ) ); 00352 } 00353 00354 return p; 00355 } 00356 00357 boost::shared_ptr< WModuleConnector > WModule::findConnector( std::string name ) 00358 { 00359 // simply search both 00360 boost::shared_ptr< WModuleConnector > p = findInputConnector( name ); 00361 if( p ) // found? 00362 { 00363 return p; 00364 } 00365 00366 // search in output list 00367 return findOutputConnector( name ); 00368 } 00369 00370 boost::shared_ptr< WModuleConnector > WModule::getConnector( std::string name ) 00371 { 00372 boost::shared_ptr< WModuleConnector > p = findConnector( name ); 00373 00374 if( !p ) 00375 { 00376 throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name + 00377 std::string( "\" does not exist in the module \"" ) + getName() + 00378 std::string( "\"." ) ); 00379 } 00380 00381 return p; 00382 } 00383 00384 boost::signals2::connection WModule::subscribeSignal( MODULE_SIGNAL signal, t_ModuleGenericSignalHandlerType notifier ) 00385 { 00386 switch( signal ) 00387 { 00388 case WM_READY: 00389 return signal_ready.connect( notifier ); 00390 default: 00391 std::ostringstream s; 00392 s << "Could not subscribe to unknown signal."; 00393 throw WSignalSubscriptionFailed( s.str() ); 00394 break; 00395 } 00396 } 00397 00398 boost::signals2::connection WModule::subscribeSignal( MODULE_SIGNAL signal, t_ModuleErrorSignalHandlerType notifier ) 00399 { 00400 switch( signal) 00401 { 00402 case WM_ERROR: 00403 return signal_error.connect( notifier ); 00404 default: 00405 std::ostringstream s; 00406 s << "Could not subscribe to unknown signal."; 00407 throw WSignalSubscriptionFailed( s.str() ); 00408 break; 00409 } 00410 } 00411 00412 const t_GenericSignalHandlerType WModule::getSignalHandler( MODULE_CONNECTOR_SIGNAL signal ) 00413 { 00414 switch( signal ) 00415 { 00416 case CONNECTION_ESTABLISHED: 00417 return boost::bind( &WModule::notifyConnectionEstablished, this, _1, _2 ); 00418 case CONNECTION_CLOSED: 00419 return boost::bind( &WModule::notifyConnectionClosed, this, _1, _2 ); 00420 case DATA_CHANGED: 00421 return boost::bind( &WModule::notifyDataChange, this, _1, _2 ); 00422 default: 00423 std::ostringstream s; 00424 s << "Could not subscribe to unknown signal. You need to implement this signal type explicitly in your module."; 00425 throw WSignalUnknown( s.str() ); 00426 break; 00427 } 00428 } 00429 00430 const WBoolFlag& WModule::isInitialized() const 00431 { 00432 return m_initialized; 00433 } 00434 00435 const WBoolFlag& WModule::isAssociated() const 00436 { 00437 return m_isAssociated; 00438 } 00439 00440 const WBoolFlag& WModule::isUseable() const 00441 { 00442 return m_isUsable; 00443 //return isInitialized() && isAssociated(); 00444 } 00445 00446 const WBoolFlag& WModule::isReady() const 00447 { 00448 return m_isReady; 00449 } 00450 00451 const WBoolFlag& WModule::isReadyOrCrashed() const 00452 { 00453 return m_isReadyOrCrashed; 00454 } 00455 00456 const WBoolFlag& WModule::isRunning() const 00457 { 00458 return m_isRunning; 00459 } 00460 00461 void WModule::notifyConnectionEstablished( boost::shared_ptr< WModuleConnector > /*here*/, 00462 boost::shared_ptr< WModuleConnector > /*there*/ ) 00463 { 00464 // By default this callback does nothing. Overwrite it in your module. 00465 } 00466 00467 void WModule::notifyConnectionClosed( boost::shared_ptr< WModuleConnector > /*here*/, 00468 boost::shared_ptr< WModuleConnector > /*there*/ ) 00469 { 00470 // By default this callback does nothing. Overwrite it in your module. 00471 } 00472 00473 void WModule::notifyDataChange( boost::shared_ptr< WModuleConnector > /*input*/, 00474 boost::shared_ptr< WModuleConnector > /*output*/ ) 00475 { 00476 // By default this callback does nothing. Overwrite it in your module. 00477 } 00478 00479 boost::shared_ptr< WProperties > WModule::getProperties() const 00480 { 00481 return m_properties; 00482 } 00483 00484 boost::shared_ptr< WProperties > WModule::getInformationProperties() const 00485 { 00486 return m_infoProperties; 00487 } 00488 00489 boost::shared_ptr< WProgressCombiner > WModule::getRootProgressCombiner() 00490 { 00491 return m_progress; 00492 } 00493 00494 const char** WModule::getXPMIcon() const 00495 { 00496 // return empty 1x1 icon by default. 00497 static const char * o_xpm[] = 00498 { 00499 "1 1 1 1", 00500 " c None", 00501 " " 00502 }; 00503 return o_xpm; 00504 } 00505 00506 void WModule::ready() 00507 { 00508 m_isReady( true ); 00509 m_readyProgress->finish(); 00510 signal_ready( shared_from_this() ); 00511 } 00512 00513 const WRequirement* WModule::checkRequirements() const 00514 { 00515 // simply iterate all requirements and return the first found that is not fulfilled 00516 for( Requirements::const_iterator i = m_requirements.begin(); i != m_requirements.end(); ++i ) 00517 { 00518 if( !( *i )->isComplied() ) 00519 { 00520 return *i; 00521 } 00522 } 00523 00524 return NULL; 00525 } 00526 00527 void WModule::threadMain() 00528 { 00529 WLogger::getLogger()->addLogMessage( "Starting module main method.", "Module (" + getName() + ")", LL_INFO ); 00530 00531 // check requirements 00532 const WRequirement* failedReq = checkRequirements(); 00533 if( failedReq ) 00534 { 00535 throw WModuleRequirementNotMet( failedReq ); 00536 } 00537 00538 // call main thread function 00539 m_isRunning( true ); 00540 moduleMain(); 00541 00542 // NOTE: if there is any exception in the module thread, WThreadedRunner calls onThreadException for us. We can then disconnect the 00543 // module and call our own error notification mechanism. 00544 00545 // remove all pending connections. This is important as connections that still exists after module deletion can cause segfaults when they get 00546 // disconnected in the connector destructor. 00547 disconnect(); 00548 m_isRunning( false ); 00549 } 00550 00551 void WModule::onThreadException( const WException& e ) 00552 { 00553 // use our own error callback which includes the exact module pointer which caused the problem 00554 signal_error( shared_from_this(), e ); 00555 00556 // ensure the module is properly disconnected 00557 disconnect(); 00558 00559 // module is not running anymore. 00560 m_isRunning( false ); 00561 00562 // let WThreadedRunner do the remaining tasks. 00563 handleDeadlyException( e, "Module (" + getName() +")" ); 00564 } 00565 00566 wlog::WStreamedLogger WModule::infoLog() const 00567 { 00568 return wlog::info( getName() ); 00569 } 00570 00571 wlog::WStreamedLogger WModule::errorLog() const 00572 { 00573 return wlog::error( getName() ); 00574 } 00575 00576 wlog::WStreamedLogger WModule::debugLog() const 00577 { 00578 return wlog::debug( getName() ); 00579 } 00580 00581 wlog::WStreamedLogger WModule::warnLog() const 00582 { 00583 return wlog::warn( getName() ); 00584 } 00585 00586 void WModule::setLocalPath( boost::filesystem::path path ) 00587 { 00588 m_localPath = path; 00589 } 00590 00591 boost::filesystem::path WModule::getLocalPath() const 00592 { 00593 return m_localPath; 00594 } 00595 00596 void WModule::setLibPath( boost::filesystem::path path ) 00597 { 00598 m_libPath = path; 00599 } 00600 00601 boost::filesystem::path WModule::getLibPath() const 00602 { 00603 return m_libPath; 00604 } 00605 00606 void WModule::setPackageName( std::string name ) 00607 { 00608 m_packageName = name; 00609 } 00610 00611 std::string WModule::getPackageName() const 00612 { 00613 return m_packageName; 00614 } 00615 00616 bool WModule::isDeprecated() const 00617 { 00618 return !deprecated().empty(); 00619 } 00620 00621 std::string WModule::getDeprecationMessage() const 00622 { 00623 return deprecated(); 00624 } 00625 00626 void WModule::waitRestored() 00627 { 00628 if( m_restoreMode ) 00629 { 00630 // this returns if the flag was set in the past since it uses a OneShot Condition. 00631 m_isLoadFinished.wait(); 00632 // after load has finished, the module is not in restore mode anymore 00633 m_restoreMode = false; 00634 } 00635 } 00636 00637 bool WModule::isRestoreNeeded() const 00638 { 00639 return m_restoreMode; 00640 } 00641 00642 void WModule::setRestoreNeeded( bool restore ) 00643 { 00644 m_restoreMode = restore; 00645 } 00646 00647 void WModule::reportRestoreComplete() 00648 { 00649 m_isLoadFinished.set( true ); 00650 } 00651 00652 const std::string& WModule::getUUID() const 00653 { 00654 return m_uuid; 00655 } 00656 00657 void WModule::setUUID( std::string uuid ) 00658 { 00659 m_uuid = uuid; 00660 00661 // create one if none was specified 00662 if( m_uuid.empty() ) 00663 { 00664 // simple random uuid generator 00665 boost::uuids::random_generator gen; 00666 m_uuid = boost::uuids::to_string( gen() ); 00667 } 00668 } 00669 00670 WModule::SPtr WModule::findByUUID( std::string uuid ) 00671 { 00672 return WModuleFactory::findByUUID( uuid ); 00673 } 00674