OpenWalnut  1.4.0
WModule.cpp
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