OpenWalnut  1.4.0
WModule.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <algorithm>
26 #include <set>
27 #include <string>
28 #include <sstream>
29 
30 #include <boost/shared_ptr.hpp>
31 #include <boost/uuid/uuid.hpp>
32 #include <boost/uuid/uuid_generators.hpp>
33 #include <boost/uuid/uuid_io.hpp>
34 
35 #include "WModuleInputConnector.h"
36 #include "WModuleOutputConnector.h"
37 #include "WModuleInputData.h"
38 #include "WModuleOutputData.h"
39 #include "WModuleConnectorSignals.h"
40 #include "WModuleContainer.h"
41 #include "WModuleFactory.h"
42 #include "WModuleMetaInformation.h"
43 #include "exceptions/WModuleConnectorInitFailed.h"
44 #include "exceptions/WModuleConnectorNotFound.h"
45 #include "exceptions/WModuleUninitialized.h"
46 #include "exceptions/WModuleRequirementNotMet.h"
47 #include "../common/WException.h"
48 #include "../common/exceptions/WNameNotUnique.h"
49 #include "../common/exceptions/WSignalUnknown.h"
50 #include "../common/exceptions/WSignalSubscriptionFailed.h"
51 #include "../common/WLogger.h"
52 #include "../common/WCondition.h"
53 #include "../common/WConditionOneShot.h"
54 #include "../common/WConditionSet.h"
55 #include "../common/WPathHelper.h"
56 #include "../common/WProgressCombiner.h"
57 #include "../common/WPredicateHelper.h"
58 
59 #include "WModule.h"
60 
63  WPrototyped(),
64  m_initialized( new WCondition(), false ),
65  m_isAssociated( new WCondition(), false ),
66  m_isUsable( new WCondition(), false ),
67  m_isReady( new WConditionOneShot(), false ),
68  m_isReadyOrCrashed( new WConditionSet(), false ),
69  m_isRunning( new WCondition(), false ),
70  m_isLoadFinished( new WConditionOneShot(), false ),
71  m_restoreMode( false ),
72  m_readyProgress( boost::shared_ptr< WProgress >( new WProgress( "Initializing Module" ) ) ),
73  m_moduleState(),
74  m_localPath( WPathHelper::getSharePath() )
75 {
76  // initialize members
77  m_properties = boost::shared_ptr< WProperties >( new WProperties( "Properties", "Module's properties" ) );
78  m_infoProperties = boost::shared_ptr< WProperties >( new WProperties( "Informational Properties", "Module's information properties" ) );
79  m_infoProperties->setPurpose( PV_PURPOSE_INFORMATION );
80 
81  m_runtimeName = m_properties->addProperty( "Name", "The name of the module defined by the user. This is, by default, the module name but "
82  "can be changed by the user to provide some kind of simple identification upon many modules.",
83  std::string( "" ), false );
84 
85  m_active = m_properties->addProperty( "active", "Determines whether the module should be activated.", true, true );
86  m_active->getCondition()->subscribeSignal( boost::bind( &WModule::activate, this ) );
87 
88  // the isReadyOrCrashed condition set needs to be set up here
89  WConditionSet* cs = static_cast< WConditionSet* >( m_isReadyOrCrashed.getCondition().get() ); // NOLINT
90  cs->setResetable( true, false );
91  cs->add( m_isReady.getCondition() );
92  cs->add( m_isCrashed.getCondition() );
93 
94  m_container = boost::shared_ptr< WModuleContainer >();
95  m_progress = boost::shared_ptr< WProgressCombiner >( new WProgressCombiner() );
96 
97  // add a progress indicator which finishes on "ready()"
98  m_progress->addSubProgress( m_readyProgress );
99 
100  // our internal state consist out of two conditions: data changed and the exit flag from WThreadedRunner.
102 }
103 
105 {
106  // cleanup
107 }
108 
109 void WModule::addConnector( boost::shared_ptr< WModuleInputConnector > con )
110 {
111  size_t c = std::count_if( m_inputConnectors.begin(), m_inputConnectors.end(),
113  );
114  // well ... we want it to be unique in both:
115  c += std::count_if( m_outputConnectors.begin(), m_outputConnectors.end(),
117  );
118 
119  // if there already is one ... exception
120  if( c )
121  {
122  throw WNameNotUnique( std::string( "Could not add the connector " + con->getCanonicalName() + " since names must be unique." ) );
123  }
124 
125  m_inputConnectors.push_back( con );
126 }
127 
128 void WModule::addConnector( boost::shared_ptr< WModuleOutputConnector > con )
129 {
130  size_t c = std::count_if( m_inputConnectors.begin(), m_inputConnectors.end(),
132  );
133  // well ... we want it to be unique in both:
134  c += std::count_if( m_outputConnectors.begin(), m_outputConnectors.end(),
136  );
137 
138  // if there already is one ... exception
139  if( c )
140  {
141  throw WNameNotUnique( std::string( "Could not add the connector " + con->getCanonicalName() + " since names must be unique." ) );
142  }
143 
144  m_outputConnectors.push_back( con );
145 }
146 
148 {
149  // remove connections and their signals
150  for( InputConnectorList::iterator listIter = m_inputConnectors.begin();
151  listIter != m_inputConnectors.end(); ++listIter )
152  {
153  ( *listIter )->disconnectAll();
154  }
155  for( OutputConnectorList::iterator listIter = m_outputConnectors.begin();
156  listIter != m_outputConnectors.end(); ++listIter )
157  {
158  ( *listIter )->disconnectAll();
159  }
160 }
161 
162 WCombinerTypes::WDisconnectList WModule::getPossibleDisconnections()
163 {
164  WCombinerTypes::WDisconnectList discons;
165 
166  // iterate inputs
167  for( InputConnectorList::iterator listIter = m_inputConnectors.begin(); listIter != m_inputConnectors.end(); ++listIter )
168  {
169  // get all connections of the current connector:
170  WCombinerTypes::WDisconnectGroup g = WCombinerTypes::WDisconnectGroup( ( *listIter )->getName(),
171  ( *listIter )->getPossibleDisconnections() );
172 
173  if( g.second.size() )
174  {
175  discons.push_back( g );
176  }
177  }
178 
179  // iterate outputs
180  for( OutputConnectorList::iterator listIter = m_outputConnectors.begin(); listIter != m_outputConnectors.end(); ++listIter )
181  {
182  // get all connections of the current connector:
183  WCombinerTypes::WDisconnectGroup g = WCombinerTypes::WDisconnectGroup( ( *listIter )->getName(),
184  ( *listIter )->getPossibleDisconnections() );
185 
186  if( g.second.size() )
187  {
188  discons.push_back( g );
189  }
190  }
191 
192  return discons;
193 }
194 
196 {
197  m_initialized( false );
199 
200  // remove connections and their signals, this is flat removal. The module container can do deep removal
201  disconnect();
202 
203  // clean up list
204  // this should delete the connector since nobody else *should* have another shared_ptr to them
205  m_inputConnectors.clear();
206  m_outputConnectors.clear();
207 }
208 
210 {
211 }
212 
214 {
215 }
216 
218 {
219 }
220 
222 {
223 }
224 
225 std::string WModule::deprecated() const
226 {
227  return "";
228 }
229 
231 {
232  return m_meta;
233 }
234 
236 {
237  // doing it twice is not allowed
238  if( isInitialized()() )
239  {
240  throw WModuleConnectorInitFailed( std::string( "Could not initialize connectors for Module " ) + getName() +
241  std::string( ". Reason: already initialized." ) );
242  }
243 
244  // set the module name as default runtime name
245  m_runtimeName->set( getName() );
246 
247  // initialize module meta information
248  m_meta = WModuleMetaInformation::SPtr( new WModuleMetaInformation( shared_from_this() ) );
249 
250  // initialize connectors and properties
251  requirements();
252  connectors();
253  properties();
254 
255  // now, the module is initialized but not necessarily usable (if not associated with a container)
256  m_initialized( true );
258 
259  // also set thread name
260  setThreadName( getName() );
261 }
262 
264 {
265  // currently just removes connectors
267 }
268 
269 boost::shared_ptr< WModuleContainer > WModule::getAssociatedContainer() const
270 {
271  return m_container;
272 }
273 
274 void WModule::setAssociatedContainer( boost::shared_ptr< WModuleContainer > container )
275 {
276  m_container = container;
277 
278  // true if the pointer is set
279  m_isAssociated( m_container != boost::shared_ptr< WModuleContainer >() );
281 }
282 
283 MODULE_TYPE WModule::getType() const
284 {
285  return MODULE_ARBITRARY;
286 }
287 
289 {
290  return m_inputConnectors;
291 }
292 
294 {
295  return m_outputConnectors;
296 }
297 
298 boost::shared_ptr< WModuleInputConnector > WModule::findInputConnector( std::string name )
299 {
300  // simply search
301  for( InputConnectorList::const_iterator listIter = m_inputConnectors.begin();
302  listIter != m_inputConnectors.end(); ++listIter )
303  {
304  // try the canonical name
305  if( ( name == ( *listIter )->getCanonicalName() ) || ( name == ( *listIter )->getName() ) )
306  {
307  return ( *listIter );
308  }
309  }
310 
311  return boost::shared_ptr< WModuleInputConnector >();
312 }
313 
314 boost::shared_ptr< WModuleInputConnector > WModule::getInputConnector( std::string name )
315 {
316  boost::shared_ptr< WModuleInputConnector > p = findInputConnector( name );
317 
318  if( !p )
319  {
320  throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name +
321  std::string( "\" does not exist in the module \"" ) + getName() + std::string( "\"." ) );
322  }
323 
324  return p;
325 }
326 
327 boost::shared_ptr< WModuleOutputConnector > WModule::findOutputConnector( std::string name )
328 {
329  // simply search
330  for( OutputConnectorList::const_iterator listIter = m_outputConnectors.begin();
331  listIter != m_outputConnectors.end(); ++listIter )
332  {
333  // try the canonical name
334  if( ( name == ( *listIter )->getCanonicalName() ) || ( name == ( *listIter )->getName() ) )
335  {
336  return ( *listIter );
337  }
338  }
339 
340  return boost::shared_ptr< WModuleOutputConnector >();
341 }
342 
343 boost::shared_ptr< WModuleOutputConnector > WModule::getOutputConnector( std::string name )
344 {
345  boost::shared_ptr< WModuleOutputConnector > p = findOutputConnector( name );
346 
347  if( !p )
348  {
349  throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name +
350  std::string( "\" does not exist in the module \"" ) + getName() +
351  std::string( "\"." ) );
352  }
353 
354  return p;
355 }
356 
357 boost::shared_ptr< WModuleConnector > WModule::findConnector( std::string name )
358 {
359  // simply search both
360  boost::shared_ptr< WModuleConnector > p = findInputConnector( name );
361  if( p ) // found?
362  {
363  return p;
364  }
365 
366  // search in output list
367  return findOutputConnector( name );
368 }
369 
370 boost::shared_ptr< WModuleConnector > WModule::getConnector( std::string name )
371 {
372  boost::shared_ptr< WModuleConnector > p = findConnector( name );
373 
374  if( !p )
375  {
376  throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name +
377  std::string( "\" does not exist in the module \"" ) + getName() +
378  std::string( "\"." ) );
379  }
380 
381  return p;
382 }
383 
384 boost::signals2::connection WModule::subscribeSignal( MODULE_SIGNAL signal, t_ModuleGenericSignalHandlerType notifier )
385 {
386  switch( signal )
387  {
388  case WM_READY:
389  return signal_ready.connect( notifier );
390  default:
391  std::ostringstream s;
392  s << "Could not subscribe to unknown signal.";
393  throw WSignalSubscriptionFailed( s.str() );
394  break;
395  }
396 }
397 
398 boost::signals2::connection WModule::subscribeSignal( MODULE_SIGNAL signal, t_ModuleErrorSignalHandlerType notifier )
399 {
400  switch( signal)
401  {
402  case WM_ERROR:
403  return signal_error.connect( notifier );
404  default:
405  std::ostringstream s;
406  s << "Could not subscribe to unknown signal.";
407  throw WSignalSubscriptionFailed( s.str() );
408  break;
409  }
410 }
411 
412 const t_GenericSignalHandlerType WModule::getSignalHandler( MODULE_CONNECTOR_SIGNAL signal )
413 {
414  switch( signal )
415  {
416  case CONNECTION_ESTABLISHED:
417  return boost::bind( &WModule::notifyConnectionEstablished, this, _1, _2 );
418  case CONNECTION_CLOSED:
419  return boost::bind( &WModule::notifyConnectionClosed, this, _1, _2 );
420  case DATA_CHANGED:
421  return boost::bind( &WModule::notifyDataChange, this, _1, _2 );
422  default:
423  std::ostringstream s;
424  s << "Could not subscribe to unknown signal. You need to implement this signal type explicitly in your module.";
425  throw WSignalUnknown( s.str() );
426  break;
427  }
428 }
429 
431 {
432  return m_initialized;
433 }
434 
436 {
437  return m_isAssociated;
438 }
439 
441 {
442  return m_isUsable;
443  //return isInitialized() && isAssociated();
444 }
445 
447 {
448  return m_isReady;
449 }
450 
452 {
453  return m_isReadyOrCrashed;
454 }
455 
457 {
458  return m_isRunning;
459 }
460 
461 void WModule::notifyConnectionEstablished( boost::shared_ptr< WModuleConnector > /*here*/,
462  boost::shared_ptr< WModuleConnector > /*there*/ )
463 {
464  // By default this callback does nothing. Overwrite it in your module.
465 }
466 
467 void WModule::notifyConnectionClosed( boost::shared_ptr< WModuleConnector > /*here*/,
468  boost::shared_ptr< WModuleConnector > /*there*/ )
469 {
470  // By default this callback does nothing. Overwrite it in your module.
471 }
472 
473 void WModule::notifyDataChange( boost::shared_ptr< WModuleConnector > /*input*/,
474  boost::shared_ptr< WModuleConnector > /*output*/ )
475 {
476  // By default this callback does nothing. Overwrite it in your module.
477 }
478 
479 boost::shared_ptr< WProperties > WModule::getProperties() const
480 {
481  return m_properties;
482 }
483 
484 boost::shared_ptr< WProperties > WModule::getInformationProperties() const
485 {
486  return m_infoProperties;
487 }
488 
489 boost::shared_ptr< WProgressCombiner > WModule::getRootProgressCombiner()
490 {
491  return m_progress;
492 }
493 
494 const char** WModule::getXPMIcon() const
495 {
496  // return empty 1x1 icon by default.
497  static const char * o_xpm[] =
498  {
499  "1 1 1 1",
500  " c None",
501  " "
502  };
503  return o_xpm;
504 }
505 
507 {
508  m_isReady( true );
509  m_readyProgress->finish();
510  signal_ready( shared_from_this() );
511 }
512 
514 {
515  // simply iterate all requirements and return the first found that is not fulfilled
516  for( Requirements::const_iterator i = m_requirements.begin(); i != m_requirements.end(); ++i )
517  {
518  if( !( *i )->isComplied() )
519  {
520  return *i;
521  }
522  }
523 
524  return NULL;
525 }
526 
528 {
529  WLogger::getLogger()->addLogMessage( "Starting module main method.", "Module (" + getName() + ")", LL_INFO );
530 
531  // check requirements
532  const WRequirement* failedReq = checkRequirements();
533  if( failedReq )
534  {
535  throw WModuleRequirementNotMet( failedReq );
536  }
537 
538  // call main thread function
539  m_isRunning( true );
540  moduleMain();
541 
542  // NOTE: if there is any exception in the module thread, WThreadedRunner calls onThreadException for us. We can then disconnect the
543  // module and call our own error notification mechanism.
544 
545  // remove all pending connections. This is important as connections that still exists after module deletion can cause segfaults when they get
546  // disconnected in the connector destructor.
547  disconnect();
548  m_isRunning( false );
549 }
550 
552 {
553  // use our own error callback which includes the exact module pointer which caused the problem
554  signal_error( shared_from_this(), e );
555 
556  // ensure the module is properly disconnected
557  disconnect();
558 
559  // module is not running anymore.
560  m_isRunning( false );
561 
562  // let WThreadedRunner do the remaining tasks.
563  handleDeadlyException( e, "Module (" + getName() +")" );
564 }
565 
567 {
568  return wlog::info( getName() );
569 }
570 
572 {
573  return wlog::error( getName() );
574 }
575 
577 {
578  return wlog::debug( getName() );
579 }
580 
582 {
583  return wlog::warn( getName() );
584 }
585 
586 void WModule::setLocalPath( boost::filesystem::path path )
587 {
588  m_localPath = path;
589 }
590 
591 boost::filesystem::path WModule::getLocalPath() const
592 {
593  return m_localPath;
594 }
595 
596 void WModule::setLibPath( boost::filesystem::path path )
597 {
598  m_libPath = path;
599 }
600 
601 boost::filesystem::path WModule::getLibPath() const
602 {
603  return m_libPath;
604 }
605 
606 void WModule::setPackageName( std::string name )
607 {
608  m_packageName = name;
609 }
610 
611 std::string WModule::getPackageName() const
612 {
613  return m_packageName;
614 }
615 
617 {
618  return !deprecated().empty();
619 }
620 
622 {
623  return deprecated();
624 }
625 
627 {
628  if( m_restoreMode )
629  {
630  // this returns if the flag was set in the past since it uses a OneShot Condition.
632  // after load has finished, the module is not in restore mode anymore
633  m_restoreMode = false;
634  }
635 }
636 
638 {
639  return m_restoreMode;
640 }
641 
642 void WModule::setRestoreNeeded( bool restore )
643 {
644  m_restoreMode = restore;
645 }
646 
648 {
649  m_isLoadFinished.set( true );
650 }
651 
652 const std::string& WModule::getUUID() const
653 {
654  return m_uuid;
655 }
656 
657 void WModule::setUUID( std::string uuid )
658 {
659  m_uuid = uuid;
660 
661  // create one if none was specified
662  if( m_uuid.empty() )
663  {
664  // simple random uuid generator
665  boost::uuids::random_generator gen;
666  m_uuid = boost::uuids::to_string( gen() );
667  }
668 }
669 
671 {
672  return WModuleFactory::findByUUID( uuid );
673 }
674