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